完全搞懂React-redux

滴水能把石穿透,萬事功到天然成—zZ先森vue

一張圖搞懂Redux運行原理

前言

衆所周知,無論是react-redux仍是vuex,都是參考於redux的公共狀態管理機制。那麼搞清楚redux的運行原理,也就搞明白了倆大視圖框架的公共狀態的一個管理模式。接下來,讓咱們探究一下它究竟是如何作到的。react

歸納Redux的運行原理

看到首圖很懵吧?首先,以個人理解,先歸納一下上圖所表達的Redux運行原理。vuex

  • 建立一個倉庫: 建立倉庫,用來存儲和管控公共狀態。

不難理解,管理公共狀態,首先得給他找個地方存儲,我把它稱爲倉庫。倉庫裏有倆個地方分別存放了狀態修改狀態的事件,稱爲狀態池事件池,倉庫並提供了操做狀態和事件的方法。redux

  • 經過store.getState()獲取倉庫中的狀態

各個組件經過此方法獲取用到的公共狀態,做爲渲染視圖的數據。bash

  • reducer 充當了倉庫的管理角色,每一個組件操作狀態都得經過reducer。

reducer做爲英文單詞是減速器、縮減者的意思。在這裏的意思是操做狀態,管理行爲,讓組件更加有秩序地、有規則地從倉庫裏獲取狀態信息。reducer是一個函數,有倆個參數分別是:【state】【action】,在接收組件dispatch傳遞進來的行爲對象,傳遞給action,進而作相應的處理。action是一個對象,並保證type屬性的存在,用來識別該行爲。state攜帶的就是倉庫的初始數據,倉庫只不過是給reducer提供一個存儲數據的地方。經過對action的操做返回最新修改後的狀態信息,同時把倉庫中的狀態修改了。框架

  • 經過store.subscribe()往倉庫的事件池中訂閱方法

各個組件爲了後期能獲取到最新的狀態,提早向組件中訂閱修改本身狀態的方法,一旦倉庫中的狀態改變,組件會從新渲染。async

store.subscribe()方法執行的返回值是從事件池中移除方法的函數:unsubscribe()ide

  • 經過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運行原理
  • 配合使用中間件
相關文章
相關標籤/搜索