昨天看了一天 Redux, 把头绪理一理, 顺便写个入门教程. 按个人理解写, 若写不好, 请勿发动攻击.
Redux 其实就是一个订阅者模式, 用来管理一个 state(js 对象, 保存数据状态, 类似 json), 按规定, 在外面使用的时候, state 只读不写, 要写就在 reducer 函数里写.
先来一个最最简单的例子, 以便于解说
javascript 代码
- import {createStore} from "redux";
- function reducer(state={num:666},action){
- return state;
- }
- var store=createStore(reducer);
- console.log(store.getState());
reducer 函数有两个参数, 一个是上面提到的 state(数据), 一个是 action(动作, 下面再说这个), 函数里现在什么都不做, 直接 return 数据, 结果为 {num:666}
store 对象有几个方法:
- store.getState(); // 获取 state
- store.subscribe(func); // 订阅
- store.unbscribe(func); // 取消订阅
- store.dispatch(action); // 发布
- store.replaceReducer(); // 替换 reducer(这个我没用过)
以上应该是 redux 之 store 的底基了.
接下来, 扩展为完整流程
javascript 代码
- import {createStore} from "redux";
- function reducer(state={num:666},action){
- var result=Object.assign({},state); // 由于 redux 规定, 不能在原数据上操作 (所谓的什么纯数据), 所以创建一个新数据. 同 es6 result={...state};
- switch(action.type)
- {
- case "increment":
- result.num++; // 给 num+1;
- break;
- case "decrement":
- result.num--; //num-1
- break;
- }
- return result;
- }
- var store=createStore(reducer);
- store.subscribe(function(){
- console.log(store.getState());
- });
- //console.log(store.getState()); // 结果为 {num:666}
- var action={type:null}; // 必须要有 type 属性
- store.dispatch(action); // 打印出默认值 {num:666}
- action={type:"increment"};
- store.dispatch(action); // 打印出 num+1, 结果为 {num:667}
- store.dispatch(action); // 再打印 num+1, 结果为 {num:668}
- action={type:"decrement"};
- store.dispatch(action); // 打印出 num-1, 结果为 {num:667}
这代码中, 我觉得最不好的地方是 subscribe 的实现, 有两缺点:
一, func 参数无 state 的传值 (因为订阅一般都是为了数据, 所以数据得给出);
二, func 中 this 指向为 undefined(不给 state, 那给个 this, 我自行取, 总可以吧?);
如果能实现其中之一, 那应该会好的多.
redux 源码中的 subscribe 实现 (部分代码)
javascript 代码
- try {
- isDispatching = true
- currentState = currentReducer(currentState, action)
- } finally {
- isDispatching = false
- }
- const listeners = currentListeners = nextListeners
- for (let i = 0; i < listeners.length; i++) {
- const listener = listeners[i]
- listener()
- }
个人觉得改为这样效果更佳:
javascript 代码
- try {
- isDispatching = true
- currentState = currentReducer(currentState, action)
- } finally {
- isDispatching = false
- }
- const listeners = currentListeners = nextListeners
- for (let i = 0; i < listeners.length; i++) {
- const listener = listeners[i]
- listener(currentState) //<= 改动处
- }
然后上面例子中的 subscribe 就可以改为 (少了一个 store 闭包, 当然, 实现中不应该改人家源码, 只能在这 YY 一下就行了
):
javascript 代码
- store.subscribe(function(state){
- console.log(state);
- });
到这, 基本流程就走通了.
进一步扩展, 传数据:
javascript 代码
- import {createStore} from "redux";
- function reducer(state={num:666},action){
- var result=Object.assign({},state);
- switch(action.type)
- {
- case "increment":
- result.num+=action.data; // 加上传进来的值
- break;
- case "decrement":
- result.num-=action.data; // 减去传进来的值
- break;
- }
- return result;
- }
- var store=createStore(reducer);
- store.subscribe(function(){
- console.log(store.getState());
- });
- //console.log(store.getState()); // 结果为 {num:666}
- var action;
- action={type:"increment",data:4};
- store.dispatch(action); // 结果为 {num:670}
- store.dispatch(action); // 结果为 {num:674}
- action={type:"decrement",data:2};
- store.dispatch(action); // 结果为 {num:672}
原理到这就结束了.
但, 模块开发, 一般都分为多个 actions(一个模块一个), 多个 reducer, 下面就处理这一部分 (为了方便看, 都写在一个文件中, 实际中 import 进来就可以了).
javascript 代码
- // 导入 combineReducers 方法
- import {createStore,combineReducers} from "redux";
- /* reducer1 */
- function reducer(state={num:666},action){ // 多个 reducer 要合并时, state 必须有默认值, 如不设, 会抛出错误
- var result=Object.assign({},state);
- switch(action.type)
- {
- case "increment":
- result.num+=action.data;
- break;
- case "decrement":
- result.num-=action.data;
- break;
- }
- return result;
- }
- /* reducer2 */
- function my_like(state=[],action){ //reducer 的函数名可以随意取
- switch(action.type){
- case "add":
- // 因为 state 为 Array, 生成新 Array 对象有好几种方法, 在此选用 concat
- return state.concat(action.like);
- default:
- return state.slice();
- }
- }
- /* 合并 reducer */
- var reducers=combineReducers({
- reducer,
- my_like
- });
- var store=createStore(reducers); // 参数改为 reducers, 会生成一个 state 对象 {"reducer":{"num":666},"my_like":[]}
- store.subscribe(function(){
- console.log(JSON.stringify(store.getState()));
- });
- var action;
- action={type:"increment",data:4};
- store.dispatch(action); // 结果为 {"reducer":{"num":670},"my_like":[]}
- store.dispatch(action); // 结果为 {"reducer":{"num":674},"my_like":[]}
- action={type:"decrement",data:2};
- store.dispatch(action); // 结果为 {"reducer":{"num":672},"my_like":[]}
- action={type:"add",like:"javascript"};
- store.dispatch(action); // 结果为 {"reducer":{"num":672},"my_like":["javascript"]}
- action={type:"add",like:"html5"};
- store.dispatch(action); // 结果为 {"reducer":{"num":672},"my_like":["javascript","html5"]}
多模块的合并原理已经写完, 一般模块文件分为 actionTypes,actions,reducer, 划分并不难. 就不多说了.
over
来源: http://www.qdfuns.com/article/11445/b7deae04975b53ba69ef9357fa2be186.html