对于 redux 的一些理解-1.基础
最近从 https://github.com/frontend9 上看了下博主对 redux 学习过程的一个分享 , 讲解的非常仔细易懂 , 感兴趣的建议从博主的 https://github.com/brickspert/blog/issues/22 案例中去逐个学习理解(动手敲) , 相信一定能够对 redux的使用有个彻底的理解;
我们从中文文档中可以看到这样的话:
'随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等'
Redux 试图让 state 的变化变得可预测。这些限制条件反映在了 redux 的三大原则中
1:单一数据源
2:state 是只读的
3:使用纯函数来执行修改
假设你从来没有用过 redux, 抛弃这些名词, 回到原始的开发模式。使用 ajax 从服务器请求数据, 根据数据生成 html片段, 插入到指定的 dom节点。逻辑很简单根本就不需要使用框架, 就可以获得很好的开发和浏览体验。
当单页面模式推崇之后 , 所有的操作都在一个 html文件中去处理, 情况变得复杂起来了。在一个页面中你需要处理各个模块的逻辑 , 你要不断去处理数据的更新, 添加, 删除; 这时候如果不对数据修改加以约束(随处可见的修改, 跨页面的修改等), 会难以追踪到你的数据走向。
尤其是对于引用类型数据, 对某一处不经意的修改, 简直会让人崩溃(你不知道到底在哪里作了修改, 可能就发生在某一个函数的调用中, 比如对形参 param.a = 1 这种操作)。
这时候也有人推出了深复制的解决办法, 每次都是操作一个全新的对象, 对源数据不会造成任何影响, 思路当然没错. 可这距离我们期望的结果: 对于数据的每一次修改都是可监测可控制的。还是有些差距。
Redux 的推出可谓是一种很好的解决方案。正如 redux文档自述中所言: Redux 是 JavaScript 状态容器,提供可预测化的状态管理
正因为它坚持的三大原则
1. 单一数据源; 避免了我们在各个模块中去重复复制, 重复定义, 这对于整个应用的数据更新都会带来麻烦; 要么只在模块内部去定义, 要么定义在全局(单一数据源上), 不推荐在内部定义一个, 在全局又定义一个同一用途的数据;
2. state 是只读的, redux提供了唯一改变 state 的方法就是触发 action; 它们只能表达想要修改的意图,
3. 使用纯函数来执行修改;
我们来看一看执行修改的逻辑就明白了:
function changeCount(state={}, action){ switch(action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 } default: return state } }
我们对原状态 state 没有做任何修改, 只是返回了一个新的 state; 借由此, 在处理逻辑时, 我们可以把应用上一次的 state, 和更新后的 state 都传递出去; 甚至我们可以改写里面的逻辑, 记录下前几次状态值的修改;
始终记住 redux提供可预测化的状态管理, 这就是它所做的工作。
/*********************** *************************************/
在以前自己去记录应用状态的做法可能是这样的;
function createStore (initData) { var APP_DATA = initData || {};
return { get: function (key) { return key ? APP_DATA[key] : APP_DATA; }, set: function (key, data) { APP_DATA[key] = data; } } } var Store = createStore(); function getUserInfo () { setTimeout(function () { var userInfo = { name: '张三', age: '23' }; Store.set('userInfo', userInfo); }, 100) } getUserInfo(); function validate () { return !!Store.get('userInfo'); } function submit () { var userInfo; if (validate()) { userInfo = Store.get('userInfo'); console.log(userInfo); } } setTimeout(submit, 300);
换作用 redux 可能得这样
function createStore (reducer, initState) { var listeners = []; var store = initState; function getState () { return store; } function subscribe (listen) { listeners.push(listen) } function dispatch (action) { store = reducer(store, action); for(var i = 0; i < listeners.length; i++) { listeners[i]() } } return { getState: getState, subscribe: subscribe, dispatch: dispatch } } function reducer (state, action) { switch (action.type) { case 'SET_USERINFO': return { ...state, userInfo: action.data } default: return state; } } var Store = createStore(reducer, {}); function getUserInfo () { setTimeout(function () { Store.dispatch({ type: 'SET_USERINFO', data: { name: '张三', age: '23' } }); }, 100) } getUserInfo(); function validate () { return !!Store.getState().userInfo; } function submit () { var userInfo; if (validate()) { userInfo = Store.getState().userInfo; console.log(userInfo); } } setTimeout(submit, 300);
应该用什么样的方式去管理应用状态 , 这个问题或许太宽泛无法回答 , 我只能说踩过的一些坑 , 也是对之前的"错误做法"一些总结:
1. 赋值操作时要格外小心 , 尤其是引用;
2. 避免兀余的数据 , 这时我们常常会定义些 "全局变量" , 来保存多个位置的相同信息。尽量去约束修改的行为
3. 保护好你的应用数据 。不得已 可以通过重置的手段还原到初始状态。
/*********************** 补充 *************************************/
对于 redux 自己也存在些疑惑的地方 , 在下面暂且把它视作 redux 的缺陷吧
1. redux 的 action 分发机制: 在源码中 action 被自顶往下层层传递 , 也就意味所有的下级状态都会接受到消息 , 那也就可能存在 有多个下级状态 同时做出响应 ---- 只要 action.type 不是唯一, 那么就必然会出现这种情况
这一点可以被利用来做一些特定场景 , 但糟糕的设想是: 一个应用中有 未知数相同的 action.type, 在某一处的修改触发了所有状态的修改 ;
理想状况下 , 我们应该只修改当前状态的值 , 而不应扩宽它的影响范围 , 因此称它为缺陷。 我们在开发中应该尽量对 action.type 命名唯一。