• Redux学习


    最近在这段时间了解了下redux,下面简单记录下所得。

    redux总结

    首先,还是用几句话简单概括下redux吧,欢迎拍砖。

    • redux采用的函数式编程方式,基于单项数据流模式管理应用中的状态。
    • 一个复杂应用对应一个状态树,应用模块之前共享的状态都交由该状态树负责维护。
    • 只能通过触发action对象来更新状态树。
    • redux核心功能只提供简单的同步方式管理应用状态,即更新、获取状态。其他额外功能通过Middleware方式提供。

    下面,先通过经典应用todos简单介绍下redux的一些基础概念

    state

    state树中存放一个应用程序内所有共享的数据,一个应用程序有且只有一个state。
    todos的state结构为

    {
        todos: [], // 完整的任务列表
        filter: 'SHOW_ALL' // 当前选中的任务过滤条件
    }
    

    action

    action是唯一种改变state的方式,用来通知reducers修改state。
    action是JavaScript plain object,描述“发生了什么”。
    默认action对象内部必须有type字段(字符串)来表示将要执行的动作。

    const ADD_TODO = 'ADD_TODO'
    {
        type: ADD_TODO,
        title: '你个标题党'
    }
    

    actionCreator就是生成action对象的函数,非常纯的纯函数。

    function addTodo(title) {
        return {
            type: ADD_TODO,
            title
        }
    }
    

    reducer

    action对象负责描述”发生了什么“,那reducer就是执行者,按照action对象的描述,
    严格更新state。
    reducer也是一种纯函数,接受action对象和state树作为参数,生成新的state。

    const reducer = (prevState, action) => nextState
    

    这里reducer采用纯函数的意义是保证应用状态的可预测性,只要传入参数可知,那结果就可知。
    同时nextState并不是直接修改prevState所得,而是在prevState基础上返回的一个新数组,通过
    Object.assign或者immutable.js生成。这样的目的是方便跟踪全局应用状态和回退/撤销操作,
    而且可以在React组件内直接通过shouldComponentUpdate(nextProps, nextState)进行对比。
    所以,千万不要“玷污”reducer函数:

    • 修改传入参数
    • 执行有副作用的函数,如API请求和路由跳转
    • 返回非纯函数,如Date.now()或Math.random()

    最后,随着应用的复杂,state树也会更加庞大,reducer内部的处理逻辑也会更加复杂。很难想象一堆代
    码根据action.type的可能值进行判断处理。
    我们可以通过分解、细化reducers来拆分数据处理逻辑,最后通过redux提供的combineReducers()
    API来生成唯一的rootReducer。

    const todoApp = combineReducers({
      visibilityFilter,
      todos
    })
    

    store

    store就是将action和reducer联系在一起的对象。

    const = createStore(reducer, initState)
    

    store对象有三种方法,通过这三种方法来维持应用的state。

    • 提供getState()方法获取应用的state
    • 提供dispatch(action)方法通知reducer根据action对象更新state
    • 提供subscribe(listener)注册监听器,监听state的更新,然后调用listener函数
    • 通过调用subscribe(listener)返回的函数,注销监听器

    至此,redux的核心方面已经说完。你可能发现这个redux似乎和观察者模式差不多呢。其实,它就是一个观察者模式。
    我们可以通过babel-node执行我们的todos查看redux应用的调用过程。

    ps: balel-node是node中安装babel插件转换ES6代码,此时node v6.3.1还不支持export & import用法
    上面redux调用流程图:

    redux只是一种应用状态管理器,我们需要通过react-redux结合React一起使用才能开发出完整的应用。

    react-redux

    react-redux是redux官方提供的一种绑定react的实现方案。主要提供了两个api:

    • <Provider store>包裹React顶层组件,并且为子组件传递Redux store props
    • connect([mapStateToProps], [mapDispatchToProps])

    connect方法主要有可选参数,具体可参考官网API文档,不过平常主要使用这两个

    • mapStateToProps(state) 每次state树更新时,都会被connect调用,返回需要传递给子组件的state对象,并被组合到react组件的props中。
    • mapDispatchToProps(dispatch): mapDispatchToProps负责返回一个 dispatchProps。dispatchProps 是actionCreator的key和dispatch(action)的组合。
    {
        addTodo,
       removeTodo,
    }
    

    react组件内部通过this.props.addTodo('hello')调用。

    Middleware(中间件)

    在介绍接下来的内容之前,插个已经被问烂的问题:什么是js闭包和函数柯里化。此处只是简单的举个例子说明一下

    let test = (a, b) => a + b
    let curryTest = a => b => a + b
    

    好,接下来继续扯我们的,什么是Middleware
    此处,Redux通过Middleware实现对store.dispatch的封装,扩展
    该函数原有的功能,典型的是实现state日志记录的功能。

    // 手动记录logger功能代码
    console.log('dispatching', action)
    store.dispatch(action)
    console.log('next state', store.getState())
    

    而redux通过Middleware建立一个store.dispatch的链条,每层middleware都会接受前一个middleware返回
    的next(最初是store.dispatch), 然后在进行封装,返回给后一个middleware调用的next,直到最后一个middleware返回
    原始的store.dispatch处理action对象。
    例如官网的logger middleware代码:

    const logger = store => next => action => {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
    

    applyMiddleware(...middlewares)源码:

    export default function applyMiddleware(...middlewares) {
      return (createStore) => (reducer, initialState, enhancer) => {
        var store = createStore(reducer, initialState, enhancer)
        var dispatch = store.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,
          dispatch
        }
      }
    }
    

    compose(..funcs)源码

    export default function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      } else {
        const last = funcs[funcs.length - 1]
        const rest = funcs.slice(0, -1)
        return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
      }
    }
    

    通过源码我们能发现,applyMiddleware接受的Middlewares数组除了最后一个middleware接受原始的store.dispatch(Array.prototype.reduceRight),其余middleware都会接受前一个middleware封装后的next,所以此时的redux流程图就是下面这样:

    介绍完redux的middleware,那redux的异步流程模式也就出来了。官网是通过redux-thunk Middleware实现的,我们看下thunkMiddleware的源码:

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

    这样store.dispatch就可以接受函数,也就可以将其和网络请求状态动态绑定在一起了。
    具体源码可参考官网async示例源码。

    本文参考链接:

    1. http://redux.js.org/index.html
    2. http://cn.redux.js.org/
    3. http://www.jianshu.com/p/3334467e4b32
  • 相关阅读:
    CKEditor4x word导入不保存格式的解决方案
    为了希望正式开始开发
    HTTP权威指南-URL与资源
    HTTP权威指南-HTTP概述
    同源策略和跨域访问
    普通Html文件图片上传(Storing Image To DB)
    PostgreSQL时间戳提取的特殊需求
    (转)百度前端规范、腾讯前端规范
    整理一下嵌入式WEB开发中的各种屏蔽(转)
    Excel表格指定列数据转换成文本
  • 原文地址:https://www.cnblogs.com/qingguo/p/5742700.html
Copyright © 2020-2023  润新知