美文网首页
redux直通自行车

redux直通自行车

作者: 流年_338f | 来源:发表于2018-05-22 20:33 被阅读0次

    前言

    redux是个状态管理工具,提供简化的api做到行为的可预测性,由于react在组件之间通信繁琐,状态管理需要状态提升造成代码冗长等局限,redux常与react结合使用,但是redux作为独立的框架,其实是可以和其他的库配合使用的,为了不被react混淆了redux的使用方法,我们这里结合jq来理解redux的使用

    redux基本概念

    redux有三个基本原则

    • 单一数据源:一个应用state只存在唯一一个对象树当中(state状态与试图层级对应形成树装结构,所以称为对象树),由唯一的store统一管理
    • state是只读的:redux规定不能直接修改state,修改state需要触发action,action* 是一个包含type字段的普通函数,用来描述发生的事情
    • 使用纯函数修改state:通过action描述怎样修改state,需要手动创建函数reducer(一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数)
    const state ={
        visibilityFilter: 'SHOW_ALL',
        todos: [
          {
            text: 'Consider using Redux',
            completed: true,
          },
          {
            text: 'Keep all state in a single tree',
            completed: false
          }
        ]
    }
    

    我们可以根据之前定义的action更新state,创建redux

    const state ={
        visibilityFilter: 'SHOW_ALL',
        todos: [
          {
            text: 'Consider using Redux',
            completed: true,
          },
          {
            text: 'Keep all state in a single tree',
            completed: false
          }
        ]
    }
    function reducer(state=state,action){
       switch(action.type){
          case 'ADD_TODO':
            return Object.assign({},state,{
                todos:[
                    ...state.todos,
                    {text:action.text,completed:false}
                ]
            });
            case 'SET_VISIBILITY':
            return Object.assign({},state,{
               visibilityFilter:action.filter 
            }); 
          default:
            return state  
       }
    }
    

    可以看到上面的两个更新是可以独立拆分出来的,分成两个小的reducer,最后合成出一个完整的state

    function reducer(state=state,action){
       return {
           todos:todos(state.todos,action),
           visibilityFilter:visibilityFilter(state.visibilityFilter,action)
       }
    }
    function todos(state=[],action) {
       switch(action.type){
           case 'ADD_TODO':
             return [
                ...state,
                {
                    text:action.text,
                    completed:false
                }
             ]
            default:
              return state 
       }
    }
    function visibilityFilter(state='SHOW_ALL',action){
        switch(action.type){
            case 'SET_VISIBILITY_FILTER':
              return action.filter
            default:
              return state  
        }
    }
    

    redux本身提供了一个方法combineReducers(reducers),辅助我们根据给出的key值让拆分出来的函数管理对应的state,返回一个完整的reducer,合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。

     const reducer = combineReducers({
      todos,
      visibilityFilter
    })
    

    下面是combineReducers部分源码

    return function combination(state = {}, action) {
        let hasChanged = false
        const nextState = {}
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]
          const reducer = finalReducers[key]
          const previousStateForKey = state[key]
          const nextStateForKey = reducer(previousStateForKey, action)
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        return hasChanged ? nextState : state
      }
    

    通过上面的源码可以看到,在调用combineReducers方法后返回一个函数combination,这个combination就作为接下来要说的createStore的第一个必填参数传入,生成store

    createStore

    cosnt store = createStore(reducer);
    

    createStore接收三个参数,reducers、initState、enhancer(中间件)
    createStore最重要的是在内部构造了四个方法,分别是:
    dispatch: 接收action,更新状态数。
    getState: 得到当前最新state树。
    replaceReducer: 替换reducers,初始化state树。
    subscribe: 用于观察者模式,设置监听,并返回退出监听方法,在调用dispatch时,运行所有被监听对象。

    createStore接受三个参数,第一参数reducers是必填,第二个参数initState用来初始化state,第三个参数用来合并中间件,这里我们分析一下store的四个方法和中间件,initSate初始化状态和服务端渲染没有做过多研究,之后补充。

    createStore创建一个单一的store,用来管理唯一state,state是一个对象,store管理这个对象,通过store.getState返回最新的state。

    function getState() {
        return currentState;
    }
    

    redux规定不能直接修改应用的状态 state,想要修改state只能通过dispatch方法,dispatch接受一个叫action的对象,里面必须包含一个 type 字段来声明你到底想干什么。redux相当于生成了一个共享,共享的状态如果可以被任意修改的话,那么程序的行为将非常不可预料,所以我们提高了修改数据的门槛:你必须通过 dispatch 执行某些允许的修改操作,而且必须大张旗鼓的在 action 里面声明。

    /**
    *通过部分源码可以看到dispatch接受action,然后调用reducers函数放回新的state更新当前的state
    *至于怎么写这个reducers有很多方式,不做本文重点,不做介绍,可以看官方文档,至于用哪种方式,怎么用就看个人喜好,用与不用它就在那里不悲不喜
    */
    const dispatch = (action)=>{
        currentState = reducers(currentState,action);
    }
    

    现在可以获取到state并修改它,那我们想每次更新状态的时候做一些统一的操作,比如打印出state看到变化,我们需要调用store.subscribe

    nextListeners.push(listener);
    
    return function unsubscribe() {
        var index = nextListeners.indexOf(listener);
        nextListeners.splice(index, 1);
    };
    

    通过部分源码我们可以看到,subscribe其实就是接收一个函数,然后把它存在一个数组,每次dispatch调用时遍历一遍这个数组

    //dispatch部分源码
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]();
    }
    

    还有一个replaceReducer方法,他接受一个新的reducers替换旧的reducers,然后初始化一个新的state

    function replaceReducer(nextReducer) {
        if (typeof nextReducer !== 'function') {
          throw new Error('Expected the nextReducer to be a function.')
        }
    
        currentReducer = nextReducer         // 就是这么耿直简单粗暴!
        dispatch({ type: ActionTypes.INIT }) // 触发生成新的 state 树
      }
    

    dispatch与middleware

    store里的dispatch函数只能接收一个action然后传给reducer更新state然后重新渲染,想要扩展一些操作就需要中间件来加强dispatch的功能,例如向服务器请求后进行后再进行更新state的操作,可自己定义一个中间件来进行想要的强化,输出不够补输出,防御不够补护甲。

    applyMiddleware(...middleware)

    applyMiddleware方法接收多个中间件方法,将多个中间方法合成一个新的dispatch,然后执行新的dispatch时根据传入顺序逐个执行

    var store = createStore(reducer, preloadedState, enhancer)
          
          var dispatch = store.dispatch // 指向原 dispatch
          var chain = [] // 存储中间件的数组
     
          var middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action)
          }
     
          chain = middlewares.map(middleware => middleware(middlewareAPI))  //生成一个中间的数组
      
          dispatch = compose(...chain)(store.dispatch)
    
          return {
            ...store, // store 的 API 中保留 getState / subsribe / replaceReducer
            dispatch  // 新 dispatch 覆盖原 dispatch,往后调用 dispatch 就会触发 chain 内的中间件链式串联执行
          }
    

    在上面代码中最主要的就是compose函数,它将个所有的中间件合并然后生成新的增强后dispatch,就像dispatch加了一层又一层的buff,为什么是说它是一层一层的是因为compose将所有的中间件组合生成新的dispatch就是洋葱一样将dispatch一层层包裹传递

    function compose(...funcs) {
      return arg => funcs.reduceRight((composed, f) => f(composed), arg);
    }
    

    我们调用compose函数将chain里的函数从右向左依次执行,传入的arg就是store.dispatch,假设我们添加了三个中间件最后生成的dispatch

    dispatch = f1(f2(f3(store.dispatch))))
    

    就像洋葱一样将dispatch一层层包裹,首先执行f3然后将f3中间件的返回值是一个函数作为参数传给传给f2,f2执行后又包裹了一层f2的返回值,然后传给f1,最后f1将自己的返回值与之前两个中间件的返回值组一起生成新的dispatch函数

    我们用一个简单的logger中间件理解一下这个过程

    const logger = (store)=>(next)=>(action)=>{
        console.log(store.getState());
        next(action);
        console.log(store.getState());
    }
    

    之前在applyMiddleware有这样一行代码

    chain = middlewares.map(middleware => middleware(middlewareAPI)) 
    

    它生出一个中间件的数组集合的同事其实也执行了每个中间件,所以中间函数到下一层返回了一个函数

    (next)=>(action)=>{
        console.log(store.getState());
        next(action);
        console.log(store.getState());
    }
    

    而到了compose合并的时候返回的的新函数就是

    (action)=>{
        console.log(store.getState());
        next(action);
        console.log(store.getState());
    }
    

    理解这个过程很重要,这里执行dispatch没有直接调用store里的dispatch而是传进去,是因为如果是多个中间件调用的话,这里的next可能是上一层的(action)=>{}函数不是store.dispatch
    我们在加一个logNext的函数将next打印出来看一下效果

    const logNext = (store)=>(next)=>(action)=>{
        console.log(next.toString());
        next(action);
    }
    

    我们可以看到此时的next

    function (n){console.log(e.getState()),t(n),console.log(e.getState())}
    

    它的运行顺序

                 --------------------------------------
                |            middleware1              |
                |    ----------------------------     |
                |    |       middleware2         |    |
                |    |    -------------------    |    |
                |    |    |  middleware3    |    |    |
                |    |    |                 |    |    |
              next next next  ———————————   |    |    |
    dispatch  —————————————> |  reducer |   |    |    |
    nextState <————————————— |          |   |    |    |
                |    |    |  ———————————    |    |    |
                |    |    |                 |    |    |
                |    |    -------------------    |    |
                |    ----------------------------     |
                --------------------------------------
    

    在生成新的dispatch是层层包裹。层层进入,层层冒出,就像是剥洋葱

    总结

    本文介绍了redux的一些机制,它可以帮助我们管理状态,并且通过准守它的规定,可以让状态的修改可预料,并且实现了自动渲染,redux是优秀的函数式编程应用,体现了函数式编程的灵活性,可以根据自己的需求自定义扩展,来管理使用。

    相关文章

      网友评论

          本文标题:redux直通自行车

          本文链接:https://www.haomeiwen.com/subject/mkkjlftx.html