Redux 莞式教程 之 简明篇
Redux 简明教程
§ 为什么要用 Redux
抛开需求讲实用性都是耍流氓,因此下面由我扮演您那可亲可爱的产品经理 ⊙ 需求 1:在控制台上记录用户的每个动作不知道您是否有后端的开发经验,后端一般会有记录访问日志的中间件 var loggerMiddleware = function(req,res,next) { console.log('[Logger]',req.method,req.originalUrl) next() } ... app.use(loggerMiddleware) 每次访问的时候,都会在控制台中留下类似下面的日志便于追踪调试: [Logger] GET / [Logger] POST /login [Logger] GET /user?uid=10086 ... 如果我们把场景转移到前端,请问该如何实现用户的动作跟踪记录? /** jQuery **/ $('#loginBtn').on('click',function(e) { console.log('[Logger] 用户登录') ... }) $('#logoutBtn').on('click',function() { console.log('[Logger] 用户退出登录') ... }) /** MVC / MVVM 框架(这里以纯 Vue 举例) **/ methods: { handleLogin () { console.log('[Logger] 用户登录') ... },handleLogout () { console.log('[Logger] 用户退出登录') ... } } 上述 jQuery 与 MV* 的写法并没有本质上的区别 ⊙ 需求 2:在上述需求的基础上,记录用户的操作时间
显然地,前端的童鞋又得一个一个去改(当然 编辑器 / IDE 都支持全局替换): /** jQuery **/ $('#loginBtn').on('click',function(e) { console.log('[Logger] 用户登录',new Date()) ... }) $('#logoutBtn').on('click',function() { console.log('[Logger] 用户退出登录',new Date()) ... }) /** MVC / MVVM 框架(这里以 Vue 举例) **/ methods: { handleLogin () { console.log('[Logger] 用户登录',new Date()) ... },handleLogout () { console.log('[Logger] 用户退出登录',new Date()) ... } } 而后端的童鞋只需要稍微修改一下原来的中间件即可: var loggerMiddleware = function(req,new Date(),req.originalUrl) next() } ... app.use(loggerMiddleware) ⊙ 需求 3:正式上线的时候,把控制台中有关 Logger 的输出全部去掉难道您以为有了 UglifyJS,配置一个 而我们后端的童鞋呢?只需要注释掉一行代码即可: ⊙ 需求 4:正式上线后,自动收集 bug,并还原出当时的场景收集用户报错还是比较简单的,利用 但要完全还原出当时的使用场景,几乎是不可能的。因为您不知道这个报错,用户是怎么一步一步操作得来的 相对地,后端的报错的收集、定位以及还原却是相当简单。只要一个 API 有 bug,那无论用什么设备访问,都会得到这个 bug
※ 小结为何前后端对于这类需求的处理竟然大相径庭?后端为何可以如此优雅? 多年来,前端工程师忍辱负重,操着卖白粉的心,赚着买白菜的钱,一直处于程序员鄙视链的底层 使用 Redux,借助 Redux DevTools 可以实现出“华丽如时光旅行一般的调试效果”
§ Store首先要区分
/** 应用初始 state,本代码块记为 code-1 **/ { counter: 0,todos: [] }
二者的关系是: Redux 规定,一个应用只应有一个单一的 var state = store.getState() state.counter = state.counter + 1 // 禁止在业务逻辑中直接修改 state 若要改变
上面提到, import { createStore } from 'redux' ... const store = createStore(reducer,initialState) // store 是靠传入 reducer 生成的哦!
§ Action上面提到, /** 本代码块记为 code-2 **/ { type: 'ADD_TODO',payload: { id: 1,content: '待办事项1',completed: false } } 当然, /** 如下都是合法的,但就是不够规范 **/ { type: 'ADD_TODO',id: 1,completed: false } { type: 'ADD_TODO',abcdefg: { id: 1,completed: false } }
如果需要新增一个代办事项,实际上就是将 /** 本代码块记为 code-3 **/ { counter: 0,todos: [{ id: 1,completed: false }] } 刨根问底, ⊙ Action Creator
顾名思义,Action Creator 是 /** 本代码块记为 code-4 **/ var id = 1 function addTodo(content) { return { type: 'ADD_TODO',payload: { id: id++,content: content,// 待办事项内容 completed: false // 是否完成的标识 } } } 将该函数应用到一个表单(假设 <--! 本代码块记为 code-5 --> <input type="text" id="todoInput" /> <button id="btn">提交</button> <script> $('#btn').on('click',function() { var content = $('#todoInput').val() // 获取输入框的值 var action = addTodo(content) // 执行 Action Creator 获得 action store.dispatch(action) // 改变 state 的不二法门:dispatch 一个 action!!! }) </script> 在输入框中输入 “待办事项2” 后,点击一下提交按钮,我们的 /** 本代码块记为 code-6 **/ { counter: 0,completed: false },{ id: 2,content: '待办事项2',completed: false }] }
刚刚提到过, § Reducer
用户每次
在上面 Action Creator 中提到的 待办事项的 /** 本代码块记为 code-7 **/ var initState = { counter: 0,todos: [] } function reducer(state,action) { // ※ 应用的初始状态是在第一次执行 reducer 时设置的(除非是服务端渲染) ※ if (!state) state = initState switch (action.type) { case 'ADD_TODO': var nextState = _.deepClone(state) // 用到了 lodash 的深克隆 nextState.todos.push(action.payload) return nextState default: // 由于 nextState 会把原 state 整个替换掉 // 若无修改,必须返回原 state(否则就是 undefined) return state } }
§ 总结
⊙ Redux 与传统后端 MVC 的对照
§ 最简单的例子 ( 在线演示 )<!DOCTYPE html> <html> <head> <script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script> </head> <body> <script> /** Action Creators */ function inc() { return { type: 'INCREMENT' }; } function dec() { return { type: 'DECREMENT' }; } function reducer(state,action) { // 首次调用本函数时设置初始 state state = state || { counter: 0 }; switch (action.type) { case 'INCREMENT': return { counter: state.counter + 1 }; case 'DECREMENT': return { counter: state.counter - 1 }; default: return state; // 无论如何都返回一个 state } } var store = Redux.createStore(reducer); console.log( store.getState() ); // { counter: 0 } store.dispatch(inc()); console.log( store.getState() ); // { counter: 1 } store.dispatch(inc()); console.log( store.getState() ); // { counter: 2 } store.dispatch(dec()); console.log( store.getState() ); // { counter: 1 } </script> </body> </html>
§ 下一章:Redux 进阶教程(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |