状态管理器 redux

简单修改state:
let state = { count: 1 } 我们来使用下状态 console.log(state.count); 我们来修改下状态 state.count = 2; console.log(state.count); 实现了状态(计数)的修改和使用了。
上面的有一个很明显的问题:修改 count 之后,使用 count 的地方不能收到通知。
我们可以使用发布-订阅模式来解决这个问题。 /*------count 的发布订阅者实践------*/
第一个版本:
let state = { count: 1 }; let listeners = []; /*订阅*/ function subscribe(listener) { listeners.push(listener); } function changeCount(count) { state.count = count; // 当count改变的时,就要去通知所有的订阅者 listeners.forEach(listener => { listener && listener(); }); }

/*来订阅一下,当 count 改变的时候,我要实时输出新的值*/
subscribe(() => {
  console.log(state.count);
});

/*我们来修改下 state,当然我们不能直接去改 state 了,我们要通过 changeCount 来修改*/
changeCount(2);

  第二个版本:

现在有两个新的问题摆在我们面前

    • 这个状态管理器只能管理 count,不通用

    • 公共的代码要封装起来
      我们尝试来解决这个问题,把公共的代码封装起来

    • // 第二个版本redux
      // createStore,提供了 changeState,getState,subscribe 三个能力。
      const createStore = (initState) => {
          let state = initState;
          let listeners = [];
      
          // 订阅
          function subscribe (listener) {
              listeners.push(listener);
          }
      
          // 改变state
          function changeState(newState) {
              state = newState;
              // 改变state,通知所有的订阅者
              listeners.forEach(listener => {
                  listener && listener();
              })
          }
      
          function getState() { return state;}
      
          return {
              subscribe, 
              changeState,
              getState
          }
      }
      // 状态管理器管理多个状态
      let initState = {
          counter: {
              count: 0
          },
          info: {
              name: '',
              desc: ''
          }
      };
      
      let store = createStore(initState);
      // console.log(store);
      store.subscribe(() => {
          let state = store.getState();
          console.log('state.info:', state.info);
          // console.log(`${state.info.name}:${state.info.desc}`);
      });
      store.subscribe(() => {
          let state = store.getState();
          console.log(`${state.counter.count}`);
      });
      
      
      let newState = Object.assign({}, store.getState(), {info: {name: '面试', desc: '前端面试'}})
      store.changeState({
          ...newState
      })
      
      store.changeState({
          ...store.getState,
          counter: {
              count: 1
          }
      })

      store.changeState({
        count: store.getState().count - 1
      });
      /*我想随便改*/
      store.changeState({
        count: 'abc'
      });

      我们需要约束,不允许计划外的 count 修改,我们只允许 count 自增和自减两种改变方式!

      那我们分两步来解决这个问题

        • 制定一个 state 修改计划,告诉 store,我的修改计划是什么。

        • 修改 store.changeState 方法,告诉它修改 state 的时候,按照我们的计划修改。
          我们来设置一个 plan 函数,接收现在的 state,和一个 action,返回经过改变后的新的 state。

        • /**
           * 
           * 需要约束,不允许计划外的 count 修改,我们只允许 count 自增和自减两种改变方式!
           * 那我们分两步来解决这个问题
           * 制定一个 state 修改计划,告诉 store,我的修改计划是什么。
           * 修改 store.changeState 方法,告诉它修改 state 的时候,按照我们的计划修改。
           * 我们来设置一个 plan 函数,接收现在的 state,和一个 action,返回经过改变后的新的 state。
           */
          // 第三个版本的redux
          // *注意:action = {type:'',other:''}, action 必须有一个 type 属性*/
          function plan(state, action) {
              switch (action.type) {
                  case 'INC':
                      return {
                          ...state,
                          count: state.count + 1
                      }
                      break;
                  case 'DEC': 
                      return {
                          count: state.count - 1
                      }
                  default:
                      return state;
                      break;
              }
          }
          
          // 我们把这个计划告诉 store,store.changeState 以后改变 state 要按照我的计划来改。
          const createStore = (plan, initState) => {
              let state = initState;
              let listeners = [];
          
              function subscribe(listener) {
                  listeners.push(listener);
              }
              function changeState(action) {
                  // 根据我的计划修改state
                  state = plan(state, action);
                  listeners.forEach(listener => {
                      listener && listener()
                  });
              }
          
              function getState() {return state;}
              return {
                  subscribe,
                  changeState,
                  getState
              }
          }
          
          // 我们来尝试使用下新的 createStore 来实现自增和自减
          let initState = {
              count: 0
          }
          let store = createStore(plan, initState);
          store.subscribe(() => {
              let state = store.getState();
              console.log('state.count', state.count);
          });
          store.changeState({
              type: 'INC'
          });
          store.changeState({
              type: 'DEC'
          });
          
          // 不能随便改,计划修改无效
          store.changeState({
              count: 'aaa'
          })
          
          // 我们商量一下吧?我们给 plan 和 changeState 改下名字好不好?
          plan 改成 reducer,changeState 改成 dispatch! // 不管你同不同意, 我都要换,因为新名字比较厉害(其实因为 redux 是这么叫的)

           

          多文件协作
          reducer 的拆分和合并
          这一小节我们来处理下 reducer 的问题。啥问题?
          
          我们知道 reducer 是一个计划函数,接收老的 state,按计划返回新的 state。那我们项目中,有大量的 state,每个 state 都需要计划函数,如果全部写在一起会是啥样子呢?
          
          所有的计划写在一个 reducer 函数里面,会导致 reducer 函数及其庞大复杂。按经验来说,我们肯定会按组件维度来拆分出很多个 reducer 函数,然后通过一个函数来把他们合并起来。
          
          我们来管理两个 state,一个 counter,一个 info。
          
          let state = {
            counter: {
              count: 0
            },
            info: {
              name: '前端九部',
              description: '我们都是前端爱好者!'
            }
          }
          他们各自的 reducer
          
          /*counterReducer, 一个子reducer*/
          /*注意:counterReducer 接收的 state 是 state.counter*/
          function counterReducer(state, action) {
            switch (action.type) {
              case 'INCREMENT':
                return {
                  count: state.count + 1
                }
              case 'DECREMENT':
                return {
                  ...state,
                  count: state.count - 1
                }
              default:
                return state;
            }
          }
          /*InfoReducer,一个子reducer*/
          /*注意:InfoReducer 接收的 state 是 state.info*/
          function InfoReducer(state, action) {
            switch (action.type) {
              case 'SET_NAME':
                return {
                  ...state,
                  name: action.name
                }
              case 'SET_DESCRIPTION':
                return {
                  ...state,
                  description: action.description
                }
              default:
                return state;
            }
          }
          那我们用 combineReducers 函数来把多个 reducer 函数合并成一个 reducer 函数。大概这样用
          
          const reducer = combineReducers({
              counter: counterReducer,
              info: InfoReducer
          });
          我们尝试实现下 combineReducers 函数
          
          function combineReducers(reducers) {
          
            /* reducerKeys = ['counter', 'info']*/
            const reducerKeys = Object.keys(reducers)
          
            /*返回合并后的新的reducer函数*/
            return function combination(state = {}, action) {
              /*生成的新的state*/
              const nextState = {}
          
              /*遍历执行所有的reducers,整合成为一个新的state*/
              for (let i = 0; i < reducerKeys.length; i++) {
                const key = reducerKeys[i]
                const reducer = reducers[key]
                /*之前的 key 的 state*/
                const previousStateForKey = state[key]
                /*执行 分 reducer,获得新的state*/
                const nextStateForKey = reducer(previousStateForKey, action)
          
                nextState[key] = nextStateForKey
              }
              return nextState;
            }
          }
          我们来尝试下 combineReducers 的威力吧
          
          const reducer = combineReducers({
            counter: counterReducer,
            info: InfoReducer
          });
          
          let initState = {
            counter: {
              count: 0
            },
            info: {
              name: '前端九部',
              description: '我们都是前端爱好者!'
            }
          }
          
          let store = createStore(reducer, initState);
          
          store.subscribe(() => {
            let state = store.getState();
            console.log(state.counter.count, state.info.name, state.info.description);
          });
          /*自增*/
          store.dispatch({
            type: 'INCREMENT'
          });
          
          /*修改 name*/
          store.dispatch({
            type: 'SET_NAME',
            name: '前端九部2号'
          });
          本小节完整源码见 demo-3
          
          state 的拆分和合并
          上一小节,我们把 reducer 按组件维度拆分了,通过 combineReducers 合并了起来。但是还有个问题, state 我们还是写在一起的,这样会造成 state 树很庞大,不直观,很难维护。我们需要拆分,一个 state,一个 reducer 写一块。
          
          这一小节比较简单,我就不卖关子了,用法大概是这样(注意注释)
          
          /* counter 自己的 state 和 reducer 写在一起*/
          let initState = {
            count: 0
          }
          function counterReducer(state, action) {
            /*注意:如果 state 没有初始值,那就给他初始值!!*/  
            if (!state) {
                state = initState;
            }
            switch (action.type) {
              case 'INCREMENT':
                return {
                  count: state.count + 1
                }
              default:    
                return state;
            }
          }
          我们修改下 createStore 函数,增加一行 dispatch({ type: Symbol() })
          
          const createStore = function (reducer, initState) {
            let state = initState;
            let listeners = [];
          
            function subscribe(listener) {
              listeners.push(listener);
            }
          
            function dispatch(action) {
              state = reducer(state, action);
              for (let i = 0; i < listeners.length; i++) {
                const listener = listeners[i];
                listener();
              }
            }
          
            function getState() {
              return state;
            }
            /* 注意!!!只修改了这里,用一个不匹配任何计划的 type,来获取初始值 */
            dispatch({ type: Symbol() })
          
            return {
              subscribe,
              dispatch,
              getState
            }
          }
          我们思考下这行可以带来什么效果?
          
          createStore 的时候,用一个不匹配任何 type 的 action,来触发 state = reducer(state, action)
          
          因为 action.type 不匹配,每个子 reducer 都会进到 default 项,返回自己初始化的 state,这样就获得了初始化的 state 树了。
          你可以试试
          
          /*这里没有传 initState 哦 */
          const store = createStore(reducer);
          /*这里看看初始化的 state 是什么*/
          console.dir(store.getState());
          本小节完整源码见 demo-4
          
          到这里为止,我们已经实现了一个七七八八的 redux 啦!
          
          中间件 middleware
          中间件 middleware 是 redux 中最难理解的地方。但是我挑战一下用最通俗的语言来讲明白它。如果你看完这一小节,还没明白中间件是什么,不知道如何写一个中间件,那就是我的锅了!
          
          中间件是对 dispatch 的扩展,或者说重写,增强 dispatch 的功能!
          
          记录日志
          我现在有一个需求,在每次修改 state 的时候,记录下来 修改前的 state ,为什么修改了,以及修改后的 state。我们可以通过重写 store.dispatch 来实现,直接看代码
          
          const store = createStore(reducer);
          const next = store.dispatch;
          
          /*重写了store.dispatch*/
          store.dispatch = (action) => {
            console.log('this state', store.getState());
            console.log('action', action);
            next(action);
            console.log('next state', store.getState());
          }
          我们来使用下
          
          store.dispatch({
            type: 'INCREMENT'
          });
          日志输出为
          
          this state { counter: { count: 0 } }
          action { type: 'INCREMENT' }
          1
          next state { counter: { count: 1 } }
          现在我们已经实现了一个完美的记录 state 修改日志的功能!
          
          记录异常
          我又有一个需求,需要记录每次数据出错的原因,我们扩展下 dispatch
          
          const store = createStore(reducer);
          const next = store.dispatch;
          
          store.dispatch = (action) => {
            try {
              next(action);
            } catch (err) {
              console.error('错误报告: ', err)
            }
          }
          这样每次 dispatch 出异常的时候,我们都会记录下来。
          
          多中间件的合作
          我现在既需要记录日志,又需要记录异常,怎么办?当然很简单了,两个函数合起来呗!
          
          store.dispatch = (action) => {
            try {
              console.log('this state', store.getState());
              console.log('action', action);
              next(action);
              console.log('next state', store.getState());
            } catch (err) {
              console.error('错误报告: ', err)
            }
          }
          如果又来一个需求怎么办?接着改 dispatch 函数?那再来10个需求呢?到时候 dispatch 函数肯定庞大混乱到无法维护了!这个方式不可取呀!
          
          我们需要考虑如何实现扩展性很强的多中间件合作模式。
          
          1、我们把 loggerMiddleware 提取出来
          
          const store = createStore(reducer);
          const next = store.dispatch;
          
          const loggerMiddleware = (action) => {
            console.log('this state', store.getState());
            console.log('action', action);
            next(action);
            console.log('next state', store.getState());
          }
          
          store.dispatch = (action) => {
            try {
              loggerMiddleware(action);
            } catch (err) {
              console.error('错误报告: ', err)
            }
          }
          2、我们把 exceptionMiddleware 提取出来
          
          const exceptionMiddleware = (action) => {
            try {
              /*next(action)*/
              loggerMiddleware(action);
            } catch (err) {
              console.error('错误报告: ', err)
            } 
          }
          store.dispatch = exceptionMiddleware;
          3、现在的代码有一个很严重的问题,就是 exceptionMiddleware 里面写死了 loggerMiddleware,我们需要让 next(action)变成动态的,随便哪个中间件都可以
          
          const exceptionMiddleware = (next) => (action) => {
            try {
              /*loggerMiddleware(action);*/
              next(action);
            } catch (err) {
              console.error('错误报告: ', err)
            } 
          }
          /*loggerMiddleware 变成参数传进去*/
          store.dispatch = exceptionMiddleware(loggerMiddleware);
          4、同样的道理,loggerMiddleware 里面的 next 现在恒等于 store.dispatch,导致 loggerMiddleware 里面无法扩展别的中间件了!我们也把 next 写成动态的
          
          const loggerMiddleware = (next) => (action) => {
            console.log('this state', store.getState());
            console.log('action', action);
            next(action);
            console.log('next state', store.getState());
          }
          到这里为止,我们已经探索出了一个扩展性很高的中间件合作模式!
          
          const store = createStore(reducer);
          const next = store.dispatch;
          
          const loggerMiddleware = (next) => (action) => {
            console.log('this state', store.getState());
            console.log('action', action);
            next(action);
            console.log('next state', store.getState());
          }
          
          const exceptionMiddleware = (next) => (action) => {
            try {
              next(action);
            } catch (err) {
              console.error('错误报告: ', err)
            }
          }
          
          store.dispatch = exceptionMiddleware(loggerMiddleware(next));
          这时候我们开开心心的新建了一个 loggerMiddleware.js,一个exceptionMiddleware.js文件,想把两个中间件独立到单独的文件中去。会碰到什么问题吗?
          
          loggerMiddleware 中包含了外部变量 store,导致我们无法把中间件独立出去。那我们把 store 也作为一个参数传进去好了~
          
          const store = createStore(reducer);
          const next  = store.dispatch;
          
          const loggerMiddleware = (store) => (next) => (action) => {
            console.log('this state', store.getState());
            console.log('action', action);
            next(action);
            console.log('next state', store.getState());
          }
          
          const exceptionMiddleware = (store) => (next) => (action) => {
            try {
              next(action);
            } catch (err) {
              console.error('错误报告: ', err)
            }
          }
          
          const logger = loggerMiddleware(store);
          const exception = exceptionMiddleware(store);
          store.dispatch = exception(logger(next));
          到这里为止,我们真正的实现了两个可以独立的中间件啦!
          
          现在我有一个需求,在打印日志之前输出当前的时间戳。用中间件来实现!
          
          const timeMiddleware = (store) => (next) => (action) => {
            console.log('time', new Date().getTime());
            next(action);
          }
          ...
          const time = timeMiddleware(store);
          store.dispatch = exception(time(logger(next)));
          本小节完整源码见 demo-6
          
          中间件使用方式优化
          上一节我们已经完全实现了正确的中间件!但是中间件的使用方式不是很友好
          
          import loggerMiddleware from './middlewares/loggerMiddleware';
          import exceptionMiddleware from './middlewares/exceptionMiddleware';
          import timeMiddleware from './middlewares/timeMiddleware';
          
          ...
          
          const store = createStore(reducer);
          const next = store.dispatch;
          
          const logger = loggerMiddleware(store);
          const exception = exceptionMiddleware(store);
          const time = timeMiddleware(store);
          store.dispatch = exception(time(logger(next)));
          其实我们只需要知道三个中间件,剩下的细节都可以封装起来!我们通过扩展 createStore 来实现!
          
          先来看看期望的用法
          
          /*接收旧的 createStore,返回新的 createStore*/
          const newCreateStore = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware)(createStore);
          
          /*返回了一个 dispatch 被重写过的 store*/
          const store = newCreateStore(reducer);
          实现 applyMiddleware
          
          const applyMiddleware = function (...middlewares) {
            /*返回一个重写createStore的方法*/
            return function rewriteCreateStoreFunc(oldCreateStore) {
               /*返回重写后新的 createStore*/
              return function newCreateStore(reducer, initState) {
                /*1. 生成store*/
                const store = oldCreateStore(reducer, initState);
                /*给每个 middleware 传下store,相当于 const logger = loggerMiddleware(store);*/
                /* const chain = [exception, time, logger]*/
                const chain = middlewares.map(middleware => middleware(store));
                let dispatch = store.dispatch;
                /* 实现 exception(time((logger(dispatch))))*/
                chain.reverse().map(middleware => {
                  dispatch = middleware(dispatch);
                });
          
                /*2. 重写 dispatch*/
                store.dispatch = dispatch;
                return store;
              }
            }
          }
          让用户体验美好
          现在还有个小问题,我们有两种 createStore 了
          
          /*没有中间件的 createStore*/
          import { createStore } from './redux';
          const store = createStore(reducer, initState);
          
          /*有中间件的 createStore*/
          const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware);
          const newCreateStore = rewriteCreateStoreFunc(createStore);
          const store = newCreateStore(reducer, initState);
          为了让用户用起来统一一些,我们可以很简单的使他们的使用方式一致,我们修改下 createStore 方法
          
          const createStore = (reducer, initState, rewriteCreateStoreFunc) => {
              /*如果有 rewriteCreateStoreFunc,那就采用新的 createStore */
              if(rewriteCreateStoreFunc){
                 const newCreateStore =  rewriteCreateStoreFunc(createStore);
                 return newCreateStore(reducer, initState);
              }
              /*否则按照正常的流程走*/
              ...
          }
          最终的用法
          
          const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware);
          
          const store = createStore(reducer, initState, rewriteCreateStoreFunc);
          本小节完整源码见 demo-7
          
          完整的 redux
          退订
          不能退订的订阅都是耍流浪!我们修改下 store.subscribe 方法,增加退订功能
          
            function subscribe(listener) {
              listeners.push(listener);
              return function unsubscribe() {
                const index = listeners.indexOf(listener)
                listeners.splice(index, 1)
              }
            }
          使用
          const unsubscribe = store.subscribe(() => {
            let state = store.getState();
            console.log(state.counter.count);
          });
          
          /*退订*/
          unsubscribe();
          中间件拿到的store
          现在的中间件拿到了完整的 store,他甚至可以修改我们的 subscribe 方法,按照最小开放策略,我们只用把 getState 给中间件就可以了!因为我们只允许你用 getState 方法!
          
          修改下 applyMiddleware 中给中间件传的 store
          
          /*const chain = middlewares.map(middleware => middleware(store));*/
          const simpleStore = { getState: store.getState };
          const chain = middlewares.map(middleware => middleware(simpleStore));
          compose
          我们的 applyMiddleware 中,把 [A, B, C] 转换成 A(B(C(next))),是这样实现的
          
          const chain = [A, B, C];
          let dispatch = store.dispatch;
          chain.reverse().map(middleware => {
             dispatch = middleware(dispatch);
          });
          redux 提供了一个 compose 方式,可以帮我们做这个事情
          
          const chain = [A, B, C];
          dispatch = compose(...chain)(store.dispatch)
          看下他是如何实现的
          
          export default function compose(...funcs) {
            if (funcs.length === 1) {
              return funcs[0]
            }
            return funcs.reduce((a, b) => (...args) => a(b(...args)))
          }
          当然 compose 函数对于新人来说可能比较难理解,你只需要他是做什么的就行啦!
          
          省略initState
          有时候我们创建 store 的时候不传 initState,我们怎么用?
          
          const store = createStore(reducer, {}, rewriteCreateStoreFunc);
          redux 允许我们这样写
          
          const store = createStore(reducer, rewriteCreateStoreFunc);
          我们仅需要改下 createStore 函数,如果第二个参数是一个object,我们认为他是 initState,如果是 function,我们就认为他是 rewriteCreateStoreFunc。
          
          function craeteStore(reducer, initState, rewriteCreateStoreFunc){
              if (typeof initState === 'function'){
              rewriteCreateStoreFunc = initState;
              initState = undefined;
            }
            ...
          }
          2 行代码的 replaceReducer
          reducer 拆分后,和组件是一一对应的。我们就希望在做按需加载的时候,reducer也可以跟着组件在必要的时候再加载,然后用新的 reducer 替换老的 reducer。
          
          const createStore = function (reducer, initState) {
            ...
            function replaceReducer(nextReducer) {
              reducer = nextReducer
              /*刷新一遍 state 的值,新来的 reducer 把自己的默认状态放到 state 树上去*/
              dispatch({ type: Symbol() })
            }
            ...
            return {
              ...
              replaceReducer
            }
          }
          我们来尝试使用下
          
          const reducer = combineReducers({
            counter: counterReducer
          });
          const store = createStore(reducer);
          
          /*生成新的reducer*/
          const nextReducer = combineReducers({
            counter: counterReducer,
            info: infoReducer
          });
          /*replaceReducer*/
          store.replaceReducer(nextReducer);
          replaceReducer 示例源码见 demo-5
          
          bindActionCreators
          bindActionCreators 我们很少很少用到,一般只有在 react-redux 的 connect 实现中用到。
          
          他是做什么的?他通过闭包,把 dispatch 和 actionCreator 隐藏起来,让其他地方感知不到 redux 的存在。
          
          我们通过普通的方式来 隐藏 dispatch 和 actionCreator 试试,注意最后两行代码
          
          const reducer = combineReducers({
            counter: counterReducer,
            info: infoReducer
          });
          const store = createStore(reducer);
          
          /*返回 action 的函数就叫 actionCreator*/
          function increment() {
            return {
              type: 'INCREMENT'
            }
          }
          
          function setName(name) {
            return {
              type: 'SET_NAME',
              name: name
            }
          }
          
          const actions = {
            increment: function () {
              return store.dispatch(increment.apply(this, arguments))
            },
            setName: function () {
              return store.dispatch(setName.apply(this, arguments))
            }
          }
          /*注意:我们可以把 actions 传到任何地方去*/
          /*其他地方在实现自增的时候,根本不知道 dispatch,actionCreator等细节*/
          actions.increment(); /*自增*/
          actions.setName('九部威武'); /*修改 info.name*/
          我眼睛一看,这个 actions 生成的时候,好多公共代码,提取一下
          
          const actions = bindActionCreators({ increment, setName }, store.dispatch);
          来看一下 bindActionCreators 的源码,超级简单(就是生成了刚才的 actions)
          
          /*核心的代码在这里,通过闭包隐藏了 actionCreator 和 dispatch*/
          function bindActionCreator(actionCreator, dispatch) {
            return function () {
              return dispatch(actionCreator.apply(this, arguments))
            }
          }
          
          /* actionCreators 必须是 function 或者 object */
          export default function bindActionCreators(actionCreators, dispatch) {
            if (typeof actionCreators === 'function') {
              return bindActionCreator(actionCreators, dispatch)
            }
          
            if (typeof actionCreators !== 'object' || actionCreators === null) {
              throw new Error()
            }
          
            const keys = Object.keys(actionCreators)
            const boundActionCreators = {}
            for (let i = 0; i < keys.length; i++) {
              const key = keys[i]
              const actionCreator = actionCreators[key]
              if (typeof actionCreator === 'function') {
                boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
              }
            }
            return boundActionCreators
          }
          bindActionCreators 示例源码见 demo-8
          
          大功告成
          完整的示例源码见 demo-9,你可以和 redux 源码做一下对比,你会发现,我们已经实现了 redux 所有的功能了。
          
          当然,为了保证代码的理解性,我们少了一些参数验证。比如 createStore(reducer)的参数 reducer 必须是 function 等等。
          
          最佳实践
          纯函数
          什么是纯函数?
          
          纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
          
          通俗来讲,就两个要素
          
          相同的输入,一定会得到相同的输出
          
          不会有 “触发事件”,更改输入参数,依赖外部参数,打印 log 等等副作用
          
          /*不是纯函数,因为同样的输入,输出结果不一致*/
          function a( count ){
             return count + Math.random();
          }
          
          /*不是纯函数,因为外部的 arr 被修改了*/
          function b( arr ){
              return arr.push(1);
          }
          let arr = [1, 2, 3];
          b(arr);
          console.log(arr); //[1, 2, 3, 1]
          
          /*不是纯函数,以为依赖了外部的 x*/
          let x = 1;
          function c( count ){
              return count + x;
          }
          我们的 reducer 计划函数,就必须是一个纯函数!
          
          只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
          
          总结
          到了最后,我想把 redux 中关键的名词列出来,你每个都知道是干啥的吗?
          
          createStore
          
          创建 store 对象,包含 getState, dispatch, subscribe, replaceReducer
          
          reducer
          
          reducer 是一个计划函数,接收旧的 state 和 action,生成新的 state
          
          action
          
          action 是一个对象,必须包含 type 字段
          
          dispatch
          
          dispatch( action ) 触发 action,生成新的 state
          
          subscribe
          
          实现订阅功能,每次触发 dispatch 的时候,会执行订阅函数
          
          combineReducers
          
          多 reducer 合并成一个 reducer
          
          replaceReducer
          
          替换 reducer 函数
          
          middleware
          
          扩展 dispatch 函数!

           

           

           

           

          来自:
          https://mp.weixin.qq.com/s/zWKZPLpo_7kcNKzZeqH2kg
posted @ 2019-09-25 11:22  单先生  阅读(346)  评论(1编辑  收藏  举报