美文网首页
Redux初识一

Redux初识一

作者: 逗比寨主 | 来源:发表于2019-10-21 16:12 被阅读0次

    写在前面:
    新手学习,不喜轻喷。

    1.个人初解

    redux将整个应用的state存储树级机构的object中,树级结构的顶点为唯一Store,在应用的整个生命周期内,只会存在一个Store。
    store中存放state,要修改store中的state必须通过action来触发,使用reducer里面的函数对state修改,个人觉得有点类似于函数式编程。

    2.数据流向

    2.1.redux是严格的单向数据流

    即:view触发action,reducer接收action修改store中的state,view根据判断,重新渲染。

    redux.png

    2.2.同一个action得到的新的state数据值一定是相等的

    reducer是纯函数,只是用来计算下一个state,函数结果是可以预测的,相同的输入,相同的输出,不应该有副作用。

    3.redux在运行过程中到底做了什么事情

    3.1. redux/src/index.js redux的入口文件

    先看一段redux入口源码

    import createStore from './createStore'
    import combineReducers from './combineReducers'
    import bindActionCreators from './bindActionCreators'
    import applyMiddleware from './applyMiddleware'
    import compose from './compose'
    import warning from './utils/warning'
    import __DO_NOT_USE__ActionTypes from './utils/actionTypes' //redux自带的action类型
    
    /*
     * This is a dummy function to check if the function name has been altered by minification.
     * If the function has been minified and NODE_ENV !== 'production', warn the user.
     * 判断在非生产环境下,redux代码是否被压缩,使用function.name来进行判断,无实际意义和方法体
     */
    function isCrushed() {}
    
    if (
      process.env.NODE_ENV !== 'production' && //当前环境不是生产环境
      typeof isCrushed.name === 'string' && //方法名称是String类型,IE不支持Function.name
      isCrushed.name !== 'isCrushed' //当代码被编译压缩之后,方法名称会改变,该判断标识当前没有被编译和压缩
    ) {
      warning(
        '您当前正在使用node_env==“production”之外的缩小代码。 ' +
        '这意味着您正在运行一个较慢的redux开发版本。' +
        '你可以使用loose envify(https://github.com/zertosh/loose-envify)来浏览 ' +
        '或在webpack中设置生产模式(https://webpack.js.org/concepts/mode/)' +
        '以确保生产版本的代码正确。'
      )
    }
    
    export {
      createStore,
      combineReducers,
      bindActionCreators,
      applyMiddleware,
      compose,
      __DO_NOT_USE__ActionTypes //语义:不要使用ActionTypes
    }
    

    该文件主要导出变量和检查当前环境下是否有压缩代码,redux压缩之后,运行性能会有所降低。

    关于代码压缩简单理解:
    未压缩
    function isCrushed() {}
    if (
    process.env.NODE_ENV !== 'production' &&
    typeof isCrushed.name === 'string' &&
    isCrushed.name !== 'isCrushed'
    )
    压缩之后,方法名称会改变,所以等式成立
    function d(){}"string"==typeof d.name&&"isCrushed"!==d.name

    3.2.redux/src/createStore

    createStore(reducer:any,preloadedState?:any,enhancer?:middleware),最终返回一个\color{red}{state tree}实例。可以进行\color{red}{getState}\color{red}{subscribe}监听和 \color{red}{dispatch}派发。

    看一段代码:

    /**
    *创建保存状态树的redux存储。
    *更改存储区中数据的唯一方法是对其调用“dispatch()”。
    *
    *你的应用中应该只有一个store。指定不同的
    *部分状态树响应操作,可以组合多个还原器
    *通过使用“combinereducers”将其转换为单个reducer函数。
    *
    *@param{function}reducer返回下一个状态树的函数,给定
    *当前状态树和要处理的操作。
    *
    *@param{any}[preloadedstate]初始状态。您可以选择指定它
    *在通用应用程序中对服务器的状态进行优化,或恢复
    *以前序列化的用户会话。
    *如果使用'combinereducers'生成根还原函数,则必须
    *与“combinereducers”键形状相同的对象。
    *
    *@param{function}[enhancer]存储增强器。您可以选择指定它
    *为了增强store的第三方功能,如中间件,
    *时间旅行、坚持等。redux附带的唯一存储增强功能
    *是“applymiddleware()”。
    *
    *@return{store}一个redux存储,用于读取状态、分派操作
    *并订阅更改。
    */
    export default function createStore(reducer, preloadedState, enhancer) {
    if (
        (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
        (typeof enhancer === 'function' && typeof arguments[3] === 'function')
      ) {
        throw new Error(
          'It looks like you are passing several store enhancers to ' +
            'createStore(). This is not supported. Instead, compose them ' +
            'together to a single function.'
        )
      }
      
      //检查增强器
      if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        enhancer = preloadedState
        preloadedState = undefined
      }
      //增强器只能是函数
      if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
          throw new Error('Expected the enhancer to be a function.')
        }
        //返回增强后的store
        return enhancer(createStore)(reducer, preloadedState)
      }
      //reducer也只能是函数
      if (typeof reducer !== 'function') {
        throw new Error('Expected the reducer to be a function.')
      }
    }
    

    enhancer?:middle,增强器,若干个中间件可以通过applymiddleware产生一个增强器,增强器通过Compose函数合并成一个增强器。
    applymiddleware代码如下:

    export default function applyMiddleware(...middlewares) {
        // 接受若干个中间件参数
       // 返回一个enhancer增强器函数,enhancer的参数是一个createStore函数。等待被enhancer(createStore)
      return createStore => (...args) => {
        // 先创建store或者更新Store
        const store = createStore(...args)
        // 如果没有创建完成就被调用,抛出异常
        let dispatch = () => {
          throw new Error(
            'Dispatching while constructing your middleware is not allowed. ' +
              'Other middleware would not be applied to this dispatch.'
          )
        }
        //暂时存储更新前的Store
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        //遍历中间件,将更新前的Store传入,获取更新后的tore数组
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        //组合中间件,将dispatch传入,即每个中间件都有一个增强后的dispatch
        dispatch = compose(...chain)(store.dispatch)
        //返回一个store和dispatch,即:返回实现了中间件的store增强器
        return {
          ...store,
          dispatch
        }
      }
    }
    

    调用逻辑:
    1.通过createStore方法创建出一个store
    2.定一个dispatch,如果在中间件构造过程中调用,抛出错误提示
    3.定义middlewareAPI,有两个方法,一个是getState,另一个是dispatch,将其作为中间件调用的store的桥接
    4.middlewares调用Array.prototype.map进行改造,存放在chain
    5.用compose整合chain数组,并赋值给dispatch
    6.将新的dispatch替换原先的store.dispatch

    compose代码如下:

    export default function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
    
      if (funcs.length === 1) {
        return funcs[0]
      }
    
      return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }
    

    compose接收>0个的增强器,如果没有增强器,就返回一个空函数,如果有一个函数,返回函数本身,如果是多个,才会产生合并的过程即compose的过程,最后一行,通过迭代器生成组合迭代函数。
    看到这里,很懵逼有没有?有没有?


    6af89bc8gw1f8rntaeadij20hs0hsgm7.jpg

    redux-thunk、redux-saga都是redux常用的中间件,一般配合applyMiddleware使用,applyMiddleware的作用就是将这些enhancer格式化成redux想要的enhancer,以redux-thunk举个栗子

    function createThunkMiddleware(extraArgument) {
      return ({ dispatch, getState }) => next => action => {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
    
        return next(action);
      };
    }
    
    const thunk = createThunkMiddleware();
    thunk.withExtraArgument = createThunkMiddleware;
    
    export default thunk;
    

    根据源码来看,我们如果单独使用thunk,应该如下:

    thunk = ({ dispatch, getState })=>{
        return next => action => {
            if (typeof action === 'function') {
                return action(dispatch, getState);
            }
            return next(action);
        };
     } 
    

    applyMiddleware处理之后,应该是china数组如下:

    const newDispatch;
    const middlewareAPI={
      getState:store.getState,
      dispatch: (...args) => newDispatch(...args)
    }
    const { dispatch, getState } = middlewareAPI;
    const  fun1 = (next)=>{
      return action => {
        if (typeof action === 'function') {
            return action(dispatch, getState);
        }
        return next(action);
      }
    }
    const chain = [fun1]
    

    模拟整个过程:

    function makeASandwichWithSecretSauce(forPerson) {
      return function (dispatch) {
        return fetchSecretSauce().then(
          sauce => dispatch(makeASandwich(forPerson, sauce)),
          error => dispatch(apologize('The Sandwich Shop', forPerson, error))
        );
      };
    }
    // store.dispatch就等价于newDispatch
    store.dispatch(makeASandwichWithSecretSauce('Me'))
    
    ====> 转换
    const forPerson = 'Me';
    const action = (dispatch)=>{
        return fetchSecretSauce().then(
          sauce => dispatch(makeASandwich(forPerson, sauce)),
          error => dispatch(apologize('The Sandwich Shop', forPerson, error))
        );
    }
    newDispatch()
    
    ===> typeof action === 'function' 成立时
    
     ((dispatch)=>{
        return fetchSecretSauce().then(
          sauce => dispatch(makeASandwich(forPerson, sauce)),
          error => dispatch(apologize('The Sandwich Shop', forPerson, error))
        );
      })( (...args) => newDispatch(...args), getState)
    
    ====> 计算运行结果
    const forPerson = 'Me';
    const dispatch = (...args) => newDispatch(...args) ;
    fetchSecretSauce().then(
          sauce => dispatch(makeASandwich(forPerson, sauce)),
          error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
    // 其中:
    function fetchSecretSauce() {
      return fetch('https://www.google.com/search?q=secret+sauce');
    }
    function makeASandwich(forPerson, secretSauce) {
      return {
        type: 'MAKE_SANDWICH',
        forPerson,
        secretSauce
      };
    }
    
    function apologize(fromPerson, toPerson, error) {
      return {
        type: 'APOLOGIZE',
        fromPerson,
        toPerson,
        error
      };
    }
    ====> 我们这里只计算Promise.resolve的结果,并且假设fetchSecretSauce返回值为'666',即sauce='666'
    
    const forPerson = 'Me';
    const dispatch = (...args) => newDispatch(...args) ;
    dispatch({
        type: 'MAKE_SANDWICH',
        'Me',
        '666'
    })
    ====> 为了方便对比,我们再次转换一下
    
    const action = {
        type: 'MAKE_SANDWICH',
        'Me',
        '666'
    };
    
    const next = store.dispatch
    
    const newDispatch = action =>{
      if (typeof action === 'function') {
        return action(dispatch, getState);
      }
      return next(action);
    }
    
    newDispatch(action)
    
    ====> 最终结果
    store.dispatch({
        type: 'MAKE_SANDWICH',
        'Me',
        '666'
    });
    

    相关文章

      网友评论

          本文标题:Redux初识一

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