彻底搞懂React-redux

1,864 阅读5分钟

滴水能把石穿透,万事功到自然成—zZ先森

一张图搞懂Redux运行原理

前言

众所周知,不管是react-redux还是vuex,都是参考于redux的公共状态管理机制。那么搞清楚redux的运行原理,也就搞明白了俩大视图框架的公共状态的一个管理模式。接下来,让我们探究一下它到底是如何做到的。

概括Redux的运行原理

看到首图很懵吧?首先,以我的理解,先概括一下上图所表达的Redux运行原理。

  • 创建一个仓库: 创建仓库,用来存储和管控公共状态。

不难理解,管理公共状态,首先得给他找个地方存储,我把它称为仓库。仓库里有俩个地方分别存放了状态修改状态的事件,称为状态池事件池,仓库并提供了操作状态和事件的方法。

  • 通过store.getState()获取仓库中的状态

各个组件通过此方法获取用到的公共状态,作为渲染视图的数据。

  • reducer 充当了仓库的管理角色,每个组件操做状态都得通过reducer。

reducer作为英文单词是减速器、缩减者的意思。在这里的意思是操作状态,管理行为,让组件更加有秩序地、有规则地从仓库里获取状态信息。reducer是一个函数,有俩个参数分别是:【state】【action】,在接收组件dispatch传递进来的行为对象,传递给action,进而做相应的处理。action是一个对象,并保证type属性的存在,用来识别该行为。state携带的就是仓库的初始数据,仓库只不过是给reducer提供一个存储数据的地方。通过对action的操作返回最新修改后的状态信息,同时把仓库中的状态修改了。

  • 通过store.subscribe()往仓库的事件池中订阅方法

各个组件为了后期能获取到最新的状态,提前向组件中订阅修改自己状态的方法,一旦仓库中的状态改变,组件会重新渲染。

store.subscribe()方法执行的返回值是从事件池中移除方法的函数:unsubscribe()

  • 通过store.dispatch()派发一个任务告知reducer修改仓库中的状态。派发的就是一个带有type的对象,通过type来标识修改哪个状态。

最后各个组件都能有效的实时更新,实现了公共状态的管理以及各个组件之间的通信。

手写Redux源码以及优化

  • 提供五个方法
    • dispatch
    • subscribe
    • getState
    • replaceRedecer
    • [?observable]:observable
  • Redux源码优化
    • 用户使用getState,克隆一份新的状态,防止直接修改

    • 对于添加事件池中,做去重处理

    • 防止在reducer中直接修改状态

      克隆一份新的状态,并返回 我们知道确实是通过派发任务通知reducer去修改状态,但是我们在操作状态的时候为了能一次性更新,且在修改的时候不破坏原有的状态,防止造成状态的混乱,有必要克隆一份新的状态。在我们使用reducer的时候,也是新克隆一份。

      • 深克隆(推荐)
      • JSON.parse(JSON.stringify(xxx))
    • 对于状态没有更新,我们不通知事件池中的方法执行,从而提高性能。用深比较。

//优化一 深克隆
export cloneDeep(obj){
    if(obj===null) return null;
    if(typeof obj!=="function") return obj;
    if(obj instanceof RegExp) return new RegExp(obj);
    if(obj instanceof Date) return enw Date(obj);
    let clone  = new obj.constructor;
    Object.keys(obj).forEach(item=>{
        clone[item] = cloneDeep(obj[item])
    })
    return clone
}
export function createStore(reducer){
if(typeof reducer!=="function"){
    throw new TypeError("Expected the reducer to be a function ")
}
   //state存放状态 listener
   存放事件池
   let state,
   isdispatch =false;
       listeners = [];
    //获取状态
    getState(){
        //优化一:把返回的信息进行深克隆,这样读取到的状态信息,不能直接基于对象.xx修改了
        return cloneDeep(state)
    }
    //向事件池中追加方法
    function subscribe(func){
        if(typeof!=="function"){
            throw new Error('Expectted the listener to be a function ')
        }
        //优化二
        if(!listeners.includes(func)){
            listeners = listeners.push(func);
        }
        return function unsubscribe(){
            listeners.filter(item=>{
                item!==function
            })
        }
    }
    
    //派发任务
    function dispatch(action){
        if(func===null||typeof action!=="object"){
            throw new Error(
            'Action must be plain objects.'+'Use custom middleware for async actions'
            )
        }
        if(typeof action === 'undefined'){
            throw new Error(
            'Action may not have an undefined 'type' property'+'have you misspelled a constant?'
            )
        }
        //优化三 防止在reducer中修改状态,不用用户自己手动克隆state,这里可以深克隆一下
        let preState = state;
        state = reducer(state,action);
        
        //优化四 深比较 没有修改则不通知listeners中的方法执行
        if(!compareDeep(preState,state){
            listeners.forEach(item=>{
            item();
        })
        } 
        
    }
    //默认创建store 就执行一次dispatch 目的是给状态赋初始值
    dispatch({
        //不能和后期用户的action.type冲突
        type:Symbol('INIT')
    })
    
    return {
        setState,
        subscribe,
        dispatch
    }
}
//优化四 深比较
export  compareDeep(val1,val2){
    let type1 = typeof val1,
        type2 = typeof val2;
    if(type1===null&& type2===null){
        return true
    }    
    if(type1==="function"&&type2==="function"){
        return val1.toString === val2.toString
    }
    if(type1==="object"&&type2==="object"){
        let ct1 = val1.costructor,
            ct2 = val2.constructor;
        if((ct1===RegExp&&ct1===RegExp)||(ct2===Date&&ct2===Date)){
               return val1.toString() === val2.toString()
        }
        let key1 = Object.keys(val1),
        key2 = Object.keys(val2);
        if(key1.length===key2.length){
            key1.forEach(item=>{
                let value1=val1[item],
                    value2=val2[item];
                let res = compareDeep(value1,value2);
                 if(!res) return res
            })
        }
        return false;
    }
   //基本数据类型 直接基于三个等于号比较即可
   return val1===val2
    
}

React-redux

React-redux是React基于Redux封装的状态管理库,基于Redux,它做了以下三个大事情:

  • 提供Provider祖先组件,把store仓库放置到上下文中供子组件调用
  • 提供connect方法,将状态通过属性的方式挂载到子组件上。把action-creator返回的对象类型转换为dispatch派发格式,并通过属性挂载到子组件上。
  • 返回的是代理组件(也是一个函数)把当前需要代理的组件作为参数放到返回的函数中即可,会帮我们给redux容器中的事件池中添加一个“公共状态改变能够重新渲染的事件方法。

思维导图:

React-redux部分源码

import React from 'redux';
import PropTypes from 'prop-types';
const ThemeContext = React. creatContext();
export class Provider extends React.Component{
    static propTypes = {
        store:PropTypes.object.isRequired
    };
    render(){
        <ThemeContent value={
            {
                store:this.props.store
            }
        }>
        //真实要渲染的组件拿过来渲染
        {this.props.children}
        </ThemeContent>
    }
}

export function connect(mapStateToProps,mapDispatchToProps){
    //初始化参数
    if(typeof mapStateToProps!=="function"){
        mapStateToProps = state=>{
            return{}
        }
    }
    if(typeof mapDispatchToProps!=="function"){
        if(mapDispatchToProps!==null&& typeof mapDispatchToProps==='object'){
            let action = mapDispatchToProps;
            mapDispatchToProps = dispatch=>{
                let obj = {};
                Object.keys(action).forEach(item=>{
                    obj[item] = (...args)=>{
                        dispatch(action[item](...args))
                    }    
                })
                return obj;
            }
        }
        mapDispatchToProps = dispatch=>{
            return{}
        }
    }
    return function connectHOC(component){
        return class proxy texends React.Component{
            static contextType = ThemeContext;
            render(){
                return <component/>;
            }
            queryPros=()=>{
                let { store } = this.conext,
                    state = store.getState(),
                    dispatch = store.dispatch;
                return {
                    ...mapStateToProps(state);
                    ...mapDispatchToProps(dispatch)
                }
            }
            componentDidMount(){
                this.context.store.subscribe(()=>{
                  this.forceUpdate();  
                })
            }
        } 
    }
}

结合使用中间件

总结

  • 掌握Redux原理
  • 掌握React-redux运行原理
  • 配合使用中间件