解读redux工作原理

前端之家收集整理的这篇文章主要介绍了解读redux工作原理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

欢迎访问个人博客的其他文章

1. 前言

随着WEB应用变得越来越复杂,再加上node前后端分离越来越流行,那么对数据流动的控制就显得越发重要。redux是在flux的基础上产生的,基本思想是保证数据的单向流动,同时便于控制、使用、测试。

redux不依赖于任意框架(库),只要subscribe相应框架(库)的内部方法,就可以使用该应用框架保证数据流动的一致性。

那么如何使用redux呢?下面一步步进行解析,并带有源码说明,不仅做到知其然,还要做到知其所以然

2. 主干逻辑介绍(createStore)

2.1 简单demo入门

先来一个直观的认识:

  1. // 首先定义一个改变数据的plain函数,成为reducer
  2. function count (state,action) {
  3. var defaultState = {
  4. year: 2015,};
  5. state = state || defaultState;
  6. switch (action.type) {
  7. case 'add':
  8. return {
  9. year: state.year + 1
  10. };
  11. case 'sub':
  12. return {
  13. year: state.year - 1
  14. }
  15. default :
  16. return state;
  17. }
  18. }
  19.  
  20. // store的创建
  21. var createStore = require('redux').createStore;
  22. var store = createStore(count);
  23.  
  24. // store里面的数据发生改变时,触发的回调函数
  25. store.subscribe(function () {
  26. console.log('the year is: ',store.getState().year);
  27. });
  28.  
  29. // action: 触发state改变的唯一方法(按照redux的设计思路)
  30. var action1 = { type: 'add' };
  31. var action2 = { type: 'add' };
  32. var action3 = { type: 'sub' };
  33.  
  34. // 改变store里面的方法
  35. store.dispatch(action1); // 'the year is: 2016
  36. store.dispatch(action2); // 'the year is: 2017
  37. store.dispatch(action3); // 'the year is: 2016

2.2 挖掘createStore实现

为了说明主要问题,仅列出其中的关键代码,全部代码,可以点击这里阅读。

a 首先看createStore到底都返回的内容:

  1. export default function createStore(reducer,initialState) {
  2. ...
  3. return {
  4. dispatch,subscribe,getState,replaceReducer
  5. }
  6. }

每个属性的含义是:

  • dispatch: 用于action的分发,改变store里面的state

  • subscribe: 注册listener,store里面state发生改变后,执行该listener

  • getState: 读取store里面的state

  • replaceReducer: 替换reducer,改变state修改的逻辑

b 关键代码解析

  1. export default function createStore(reducer,initialState) {
  2. // 这些都是闭包变量
  3. var currentReducer = reducer
  4. var currentState = initialState
  5. var listeners = []
  6. var isDispatching = false;
  7.  
  8. // 返回当前的state
  9. function getState() {
  10. return currentState
  11. }
  12.  
  13. // 注册listener,同时返回一个取消事件注册方法
  14. function subscribe(listener) {
  15. listeners.push(listener)
  16. var isSubscribed = true
  17.  
  18. return function unsubscribe() {
  19. if (!isSubscribed) {
  20. return
  21. }
  22.  
  23. isSubscribed = false
  24. var index = listeners.indexOf(listener)
  25. listeners.splice(index,1)
  26. }
  27. }
  28.  
  29. // 通过action该改变state,然后执行subscribe注册方法
  30. function dispatch(action) {
  31. try {
  32. isDispatching = true
  33. currentState = currentReducer(currentState,action)
  34. } finally {
  35. isDispatching = false
  36. }
  37. listeners.slice().forEach(listener => listener())
  38. return action
  39. }
  40.  
  41. // 替换reducer,修改state变化的逻辑
  42. function replaceReducer(nextReducer) {
  43. currentReducer = nextReducer
  44. dispatch({ type: ActionTypes.INIT })
  45. }
  46.  
  47. // 初始化时,执行内部一个dispatch,得到初始state
  48. dispatch({ type: ActionTypes.INIT })
  49. }

如果还按照2.1的方式进行开发,那跟flux没有什么大的区别,需要手动解决很多问题,那redux如何将整个流程模板化(Boilerplate)呢?

3. 保证store的唯一性

随着应用越来越大,一方面,不能把所有的数据都放到一个reducer里面,另一方面,为每个reducer创建一个store,后续store的维护就显得比较麻烦。如何将二者统一起来呢?

3.1 demo入手

通过combineReducers将多个reducer合并成一个rootReducer:

  1. // 创建两个reducer: count year
  2. function count (state,action) {
  3. state = state || {count: 1}
  4. switch (action.type) {
  5. default:
  6. return state;
  7. }
  8. }
  9. function year (state,action) {
  10. state = state || {year: 2015}
  11. switch (action.type) {
  12. default:
  13. return state;
  14. }
  15. }
  16.  
  17. // 将多个reducer合并成一个
  18. var combineReducers = require('./').combineReducers;
  19. var rootReducer = combineReducers({
  20. count: count,year: year,});
  21.  
  22. // 创建store,跟2.1没有任何区别
  23. var createStore = require('./').createStore;
  24. var store = createStore(rootReducer);
  25.  
  26. var util = require('util');
  27. console.log(util.inspect(store));
  28. //输出的结果,跟2.1的store在结构上不存在区别
  29. // { dispatch: [Function: dispatch],// subscribe: [Function: subscribe],// getState: [Function: getState],// replaceReducer: [Function: replaceReducer]
  30. // }

3.2 源码解析combineReducers

  1. // 高阶函数,最后返回一个reducer
  2. export default function combineReducers(reducers) {
  3. // 提出不合法的reducers,finalReducers就是一个闭包变量
  4. var finalReducers = pick(reducers,(val) => typeof val === 'function')
  5. // 将各个reducer的初始state均设置为undefined
  6. var defaultState = mapValues(finalReducers,() => undefined)
  7.  
  8. // 一个总reducer,内部包含子reducer
  9. return function combination(state = defaultState,action) {
  10. var finalState = mapValues(finalReducers,(reducer,key) => {
  11. var prevIoUsStateForKey = state[key]
  12. var nextStateForKey = reducer(prevIoUsStateForKey,action)
  13. hasChanged = hasChanged || nextStateForKey !== prevIoUsStateForKey
  14. return nextStateForKey
  15. }
  16. }
  17.  
  18. return hasChanged ? finalState : state
  19.  
  20. }

4. 自动实现dispatch

4.1 demo介绍

在2.1中,要执行state的改变,需要手动dispatch:

  1. var action = { type: '***',payload: '***'};
  2. dispatch(action);

手动dispatch就显得啰嗦了,那么如何自动完成呢?

  1. var bindActionCreators = require('redux').bindActionCreators;
  2. // 可以在具体的应用框架隐式进行该过程(例如react-redux的connect组件中)
  3. bindActionCreators(action)

4.2 源码解析

  1. // 隐式实现dispatch
  2. function bindActionCreator(actionCreator,dispatch) {
  3. return (...args) => dispatch(actionCreator(...args))
  4. }
  5.  
  6. export default function bindActionCreators(actionCreators,dispatch) {
  7. if (typeof actionCreators === 'function') {
  8. return bindActionCreator(actionCreators,dispatch)
  9. }
  10. return mapValues(actionCreators,actionCreator =>
  11. bindAQctionCreator(actionCreator,dispatch)
  12. )
  13. }

5. 支持插件 - 对dispatch的改造

5.1 插件使用demo

一个action可以是同步的,也可能是异步的,这是两种不同的情况, dispatch执行的时机是不一样的:

  1. // 同步的action creator,store可以默认实现dispatch
  2. function add() {
  3. return { tyle: 'add' }
  4. }
  5. dispatch(add());
  6.  
  7. // 异步的action creator,因为异步完成的时间不确定,只能手工dispatch
  8. function fetchDataAsync() {
  9. return function (dispatch) {
  10. requst(url).end(function (err,res) {
  11. if (err) return dispatch({ type: 'SET_ERR',payload: err});
  12. if (res.status === 'success') {
  13. dispatch({ type: 'FETCH_SUCCESS',payload: res.data });
  14. }
  15. })
  16. }
  17. }

下面的问题就变成了,如何根据实际情况实现不同的dispatch方法,也即是根据需要实现不同的moddleware:

  1. // 普通的dispatch创建方法
  2. var store = createStore(reducer,initialState);
  3. console.log(store.dispatch);
  4.  
  5. // 定制化的dispatch
  6. var applyMiddleware = require('redux').applyMiddleware;
  7. // 实现action异步的middleware
  8. var thunk = requre('redux-thunk');
  9. var store = applyMiddleware([thunk])(createStore);
  10. // 经过处理的dispatch方法
  11. console.log(store.dispatch);

5.2 源码解析

  1. // next: 其实就是createStore
  2. export default function applyMiddleware(...middlewares) {
  3. return (next) => (reducer,initialState) => {
  4. var store = next(reducer,initialState)
  5. var dispatch = store.dispatch
  6. var chain = []
  7.  
  8. var middlewareAPI = {
  9. getState: store.getState,dispatch: (action) => dispatch(action)
  10. }
  11. chain = middlewares.map(middleware => middleware(middlewareAPI))
  12. dispatch = compose(...chain)(store.dispatch)
  13.  
  14. return {
  15. ...store,dispatch // 实现新的dispatch方法
  16. }
  17. }
  18. }
  19. // 再看看redux-thunk的实现,next就是store里面的上一个dispatch
  20. function thunkMiddleware({ dispatch,getState }) {
  21. return function(next) {
  22. return function(action) {
  23. typeof action === 'function' ?
  24. action(dispatch,getState) :
  25. next(action);
  26. }
  27. }
  28. return next => action =>
  29. typeof action === 'function' ?
  30. action(dispatch,getState) :
  31. next(action);
  32. }

6. 与react框架的结合

6.1 基本使用

目前已经有现成的工具react-redux来实现二者的结合:

  1. var rootReducers = combineReducers(reducers);
  2. var store = createStore(rootReducers);
  3. var Provider = require('react-redux').Provider;
  4. // App 为上层的Component
  5. class App extend React.Component{
  6. render() {
  7. return (
  8. <Provier store={store}>
  9. <Container />
  10. </Provider>
  11. );
  12. }
  13. }
  14.  
  15. // Container作用: 1. 获取store中的数据; 2.将dispatch与actionCreator结合起来
  16. var connect = require('react-redux').connect;
  17. var actionCreators = require('...');
  18. // MyComponent是与redux无关的组件
  19. var MyComponent = require('...');
  20.  
  21. function select(state) {
  22. return {
  23. count: state.count
  24. }
  25. }
  26. export default connect(select,actionCreators)(MyComponent)

6.2 Provider -- 提供store

React通过Context属性,可以将属性(props)直接给子孙component,无须通过props层层传递,Provider仅仅起到获得store,然后将其传递给子孙元素而已:

  1. export default class Provider extends Component {
  2. getChildContext() { // getChildContext: 将store传递给子孙component
  3. return { store: this.store }
  4. }
  5.  
  6. constructor(props,context) {
  7. super(props,context)
  8. this.store = props.store
  9. }
  10.  
  11. componentWillReceiveProps(nextProps) {
  12. const { store } = this
  13. const { store: nextStore } = nextProps
  14.  
  15. if (store !== nextStore) {
  16. warnAboutReceivingStore()
  17. }
  18. }
  19.  
  20. render() {
  21. let { children } = this.props
  22. return Children.only(children)
  23. }
  24. }

6.3 connect -- 获得store及dispatch(actionCreator)

connect是一个高阶函数,首先传入mapStateToProps、mapDispatchToProps,然后返回一个生产Component函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect(MyComponent),这样就生产出一个经过包裹的Connect组件,该组件具有如下特点:

  • 通过this.context获取祖先Component的store

  • props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component

  • componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互

  • shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState

  • componentWillUnmount时移除注册的事件this.handleChange

  • 在非生产环境下,带有热重载功能

    // 主要的代码逻辑
    export default function connect(mapStateToProps,mapDispatchToProps,mergeProps,options = {}) {

    1. return function wrapWithConnect(WrappedComponent) {
    2. class Connect extends Component {
    3. constructor(props,context) {
    4. // 从祖先Component处获得store
    5. this.store = props.store || context.store
    6. this.stateProps = computeStateProps(this.store,props)
    7. this.dispatchProps = computeDispatchProps(this.store,props)
    8. this.state = { storeState: null }
    9. // 对stateProps、dispatchProps、parentProps进行合并
    10. this.updateState()
    11. }
    12. shouldComponentUpdate(nextProps,nextState) {
    13. // 进行判断,当数据发生改变时,Component重新渲染
    14. if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
    15. this.updateState(nextProps)
    16. return true
    17. }
    18. }
    19. componentDidMount() {
    20. // 改变Component的state
    21. this.store.subscribe(() = {
    22. this.setState({
    23. storeState: this.store.getState()
    24. })
    25. })
    26. }
    27. render() {
    28. // 生成包裹组件Connect
    29. return (
    30. <WrappedComponent {...this.nextState} />
    31. )
    32. }
    33. }
    34. Connect.contextTypes = {
    35. store: storeShape
    36. }
    37. return Connect;
    38. }

    }

7. redux与react-redux关系图

猜你在找的React相关文章