zoukankan      html  css  js  c++  java
  • Redux

    在学习Redux之前,不妨先了解下Flux:

    Flux

    • 同MVC一样,是一种架构思想,但更简单清晰
    • 解决软件的结构问题,提供了一套数据流动方案
    • 通过事件和监听实现数据单向流动,中心化控制

    • View: 视图层
    • Action(动作):视图层发出的消息
    • Dispatcher(派发器):用来接收Actions、执行回调函数
    • Store(数据层):用来存放应用的状态,动态更新Views页面

    一个基本的流程可以描述为

    • Your Views "Dispatch" "Actions"(视图触发事件)
    • Your "Store" Responds to Dispatched Actions(store触发回调)
    • Your Store Emits a "Change" Event(store触发change事件)
    • Your View Responds to the "Change" Event(视图接收到事件重新渲染)

    与React关系可以理解为:

    Flux:是一个系统架构,用于推进应用中的数据单向流动
    React:是一个JavaScript库,用于构建“可预期”和“声明式”的可组合式Web用户界面

    问题

    • 代码冗余
    • dispatcher实例手动创建
    • store既保存状态数据,又有处理逻辑:直接处理数据
    • 程序可能会涉及多种数据结构,必然导致有多个store
    • 深层次组件通信问题

    参考

    Flux架构入门教程 - ryfeng

    Flux For Stupid People

    一篇漫画,图解Flux

    Redux

    Redux是基于Flux架构思想的一个库的实现,JavaScript状态容器,提供可预测化的状态管理。

    设计思想

    • Web 应用是一个状态机,视图与状态是一一对应的。 
    • 所有的状态,保存在一个对象里面。
    • 为应用程序提供一个可预测的状态容器

    三大原则

    • 单一数据源
    • State 是只读
    • 使用纯函数来执行修改

    显著特点

    • 可预测性(Reducer 是纯函数)
    • 可扩展性(middleware)

    核心元素

    • store
    • action
    • dispatch
    • reducer
    • subscribe

    Redux中的 reducer 就是一个纯函数,store.dispatch(_action) 会自动触发 reducer 方法,更新state。

    注意,reducer方法不会改变state,而是返回一个全新的state对象。

    关于纯函数:同样的输入,必定得到同样的输出

    • 不得改写参数
    • 不能调用系统 I/O 的API
    • 不能调用Date.now()Math.random()等不纯的方法,因为每次会得到不一样的结果

    适用场景

    多交互、多数据源

    • 用户的使用方式复杂,不同身份的用户有不同的使用方式(比如普通用户和管理员),多个用户之间可以协作
    • 与服务器大量交互,或者使用了WebSocket
    • View要从多个来源获取数据

    关于Redux的生态系统,请参见:http://www.redux.org.cn/docs/introduction/Ecosystem.html

    store

    本质:状态树

    let { subscribe, dispatch, getState } = createStore(reducer);
    

    关于createStore的基本实现,辅助理解

    function createStore(reducer, initialState) {
        //闭包私有变量 
        var currentReducer = reducer;
        var currentState = initialState;
        var listeners = [];
    
        function getState() {
          return currentState;
        }
    
        function subscribe(listener) {
          listeners.push(listener);
    
          return function unsubscribe() {
            var index = listeners.indexOf(listener);
            listeners.splice(index, 1);
          };
        }
    
        function dispatch(action) {
            currentState = currentReducer(currentState, action);
            listeners.slice().forEach(listener => listener());
            return action;
        }
    
        //返回一个包含可访问闭包变量的公有方法
        return {dispatch, subscribe, getState};
    }
    

    store可以看作是对reducer的封装,通过store.dispatch(action)自动触发对reducer的调用,而不是直接调用reducer(currentState, action),在一定程度上可以避免频繁传参,以更好地对store进行统一管理。

    Action & Action Creator & bindActionCreators

    • action:传递操作消息
    • action creator:操作制造机
    • bindActionCreators:对dispatch封装
    var actionCreatorsNew = bindActionCreators(actionCreators, store.dispatch);
    

    借鉴store对reducer的封装,对store.dispatch作封装,自动把actionCreators绑定到dispatch,使actionCreators成为具有操作全局state的函数集合。

    其中,actionCreators表示action集合。触发action,会自动调用dispatch(action),避免直接对dispatch的调用。 

    middleware  

    异步场景:在异步操作结束后自动执行reducer 

    即,如何在操作结束时,自动送出第二个 Action 

    扩展点:dispatch()发出时,reducer()处理前

    对于中间件,举例,日志中间件 redux-logger

    import { applyMiddleware, createStore } from 'redux';
    import createLogger from 'redux-logger';
    
    const logger = createLogger();
    const store = createStore(reducer, initial_state,
      applyMiddleware(logger)
    );
    

    other,请注意中间件的引入次序。

    给出 applymiddleware 的实现,仅供参考

    export default function applyMiddleware(...middlewares) {
      return (createStore) => (reducer, preloadedState, enhancer) => {
        var store = createStore(reducer, preloadedState, 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 用于组合函数、串联链式执行,顺序自右向左,维护扩展方便 

    var greeting = (firstName, lastName) => 'hello, ' + firstName + ' ' + lastName
    var toUpper = str => str.toUpperCase()
    var fn = compose(toUpper, greeting)
    console.log(fn('jack', 'smith'))  // ‘HELLO,JACK SMITH’
    

    异步情况,涉及发出三种不同种类的 Action  

    • 操作发起时的 Action
    • 操作成功时的 Action
    • 操作失败时的 Action

    同时,state需要维护、以反映不同的操作状态 

    let state = {
      // ... 
      isFetching: true,  // 表示是否在抓取数据
      didInvalidate: true,  // 表示数据是否过时
      lastUpdated: 'xxxxxxx'  // 表示上一次更新时间
    };
    

    整个异步操作流程应该是这样的  

    • 操作开始时,送出一个 Action,触发 State 更新为"正在操作"状态,View 重新渲染
    • 操作结束后,再送出一个 Action,触发 State 更新为"操作结束"状态,View 再一次重新渲染

    在Redux中,中间件是纯粹的函数,有明确的使用方法并且严格的遵循以下格式:

    var anyMiddleware = function ({ dispatch, getState }) {
       return function(next) {
           return function (action) {
              // 你的中间件业务相关代码
           }
       }
    }
    

    所有的这些,applyMiddleware会全部替我们封装实现。 

    异步解决方案可以引入中间件 redux-thunk 或 redux-promise  

    • redux-thunk:解决stroe.dispatch()的参数只能为action对象、不能是函数的问题
      • 返回函数的 Action Creator(返回的function在合适的时机dispatch action)
    • redux-promise:允许 Promise 对象作为stroe.dispatch()的参数,以类似同步的方式来组织代码
      • 返回 Promise 对象的 Action Creator 
      • 或者,Action 对象的 payload 属性为 Promise 对象

    但是,两者均是相对原始的解决方案,在action需要组合、取消时操作不易处理。最佳实践dva推荐 redux-saga,可测试、可mock、声明式的指令,管理actions,处理异步逻辑,管理所有的业务逻辑。

    React-Redux

    组件分类

    • UI组件:presentational component,无状态的纯组件,只负责UI视觉
    • 容器组件:container component,有状态,负责管理数据和逻辑

    Redux的重要思想就是:容器组件和展示组件的分离

    业务逻辑:针对UI组件

    • 输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
    • 输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去

    在网上看到一个非常不错的关系图,分享给大家

    connect

    将 UI 组件生成容器组件:

    const HocView = connect(mapStateToProps, mapDispatchToProps)(myComp)
    

    其中,mapStateToProps负责输入逻辑、将状态数据state映射到 UI 组件的参数props,mapDispatchToProps负责输出逻辑、将用户对 UI 组件的操作映射成 Action。

    mapStateToProps会订阅Store,每当state更新,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

    <Provider>

    用来实现对store的全局访问,使容器组件获取到state:

    render(
      <Provider store={store}>
        <Root />
      </Provider>,
      document.getElementById('root')
    )
    

    原理是利用React的 context 属性:  

    class Provider extends Component {
      getChildContext() {
        return {
          store: this.props.store
        };
      }
      render() {
        return this.props.children;
      }
    }
    
    Provider.childContextTypes = {
      store: React.PropTypes.object
    }  

    子组件通过 this.context.store 获取。一个简单的计数器例子,供参见。

    Redux扩展: redux-devtools-extension

    • 时间旅行式调试工具,time-travelling tool
    • 热重加载,hot reloading
    • 基于数据不可变性(immutable)

    使用步骤

    • 在Chrome中安装Redux Devtools扩展
    • npm安装 redux-devtools-extension
    • import引入

    参考

    Redux 中文文档redux-tutorial

    Redux教程(1-4) - 阮一峰Redux视频前30集后30集; 

    redux在react中的应用(基础篇)redux入门教程

    看漫画,学Reduxredux 三重境 - 对 redux 最佳实践的思考和总结

    React 数据流管理架构之 Redux 介绍

    redux-saga

    前面提到 redux-saga 可以作为 Redux 的异步解决方案,简单学习之,为后面学习 dva 作个铺垫。

    A Redux middleware for handling side effects (异步任务).

    Redux中间件,基于ES6的Generator功能,用于管理应用程序Side Effect的库,副作用例如

    • 异步获取数据
    • 访问浏览器缓存

    建议先了解下 Generator语法。通过redux-saga中间件将 Saga 与 Redux Store 建立连接:

    import { createStore, applyMiddleware } from 'redux'
    import createSagaMiddleware from 'redux-saga'
    import reducer from './reducers'
    import mySaga from './sagas'
    
    const sagaMiddleware = createSagaMiddleware();
    const store = createStore(reducer, applyMiddleware(sagaMiddleware));
    sagaMiddleware.run(mySaga); /// then run the saga
    

    其中,'./sagas' 用于处理所有异步操作逻辑,'./reducers' 用于处理action对stage更新。

    参考

    redux-saga | 中文教程redux-saga-beginner-tutorial

    dva

    由支付宝前端团队开发,相关历史可参见:支付宝前端应用架构的发展和选择: 从 roof 到 redux 再到 dva

    • 基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装
    • React + Redux 最佳实践,简化使用 redux 和 redux-saga 时繁杂的操作
    • 组件耦合度低
    • 结合了 react 和 vue 两者的优点,但格式固定、降低了灵活度
    • 除 react 和 react-dom 外,封装了所有其他依赖

    相关简介参见:dva - what&why -简介

    注意,dva 是 framework,而 redux 是 library。

    最核心功能是提供 app.model 方法,用于把 reducer, initialState, action, saga 封装到一起

    每个路由对应一个model,这个model掌管该路由的所有状态(action、state、reducer、sagas),组件想改变状态只要dispatch type即可。 

    参考

    React + Redux 最佳实践 - dva 基于此封装

  • 相关阅读:
    【目录】循序渐进学.Net Core Web Api开发系列
    UML各种图总结-精华
    UML各种线的含义
    最新10大Python面试常问的问题,60%的人都不会
    Java—线程池ThreadPoolExecutor案例详解,高薪必备
    HashMap 的 7 种遍历方式与性能分析,高薪必学
    DelayQueue核心源码解析,Java面试必学
    Spring源码-循环依赖,Java架构师必学
    JAVA编程思想,去掉别扭的if,自注册策略模式优雅满足开闭原则,80%的人都不知道
    用Python实现植物大战僵尸游戏,很酷
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/9285816.html
Copyright © 2011-2022 走看看