1. 程式人生 > >redux的實現過程

redux的實現過程

訂閱者 con ase listener pda 維護 dsta 能夠 第一步

Redux實現過程

不同組件需要依賴同一個數據的時候,就需要狀態提升至這些組件的根組件。
redux是狀態統一管理工具,需要使用它的原因是: 組件之間通信統一管理,方便代碼維護。

React中有一個特性context,只要某個組件使用context存儲了數據,那麽這個組件的所有子組件都可以訪問該context內容,並且還可以修改它。就像是這個組件的全局變量,它的所有子組件都可以訪問這個全局變量。
如下圖,假設要更改主題顏色,那麽在Index根組件的context中存儲當前主題色,那麽它的子組件header,footer,title,menu都可以訪問到,並且可以修改。
技術分享圖片

Redux的統一數據管理,與React的關聯之處利用的就是context特性
但是context有一個缺點就是,所有的子組件都可以修改共享的內容,每個組件都能夠改 context 裏面的內容會導致程序的運行不可預料。所以React團隊的做法就是,提高修改的門檻,修改數據時統一調用dispatch函數,並且需要傳入修改類型,如果dispatch中不存在該類型,則不允許修改。
接下來簡單推理一下redux的實現過程(假設有組件Index,Header,Footer)。
1、第一步是需要有一個存儲數據的對象,定義為appState

    var appState = {
        themeColor:‘red‘,
        themeBackground:‘black‘
    };
    //還需要一個修改數據的函數
    function dispatch(action){
        switch(action.type){
            case ‘UPDATE_THEME_COLOR‘:
                appState.themeColor = action.color;break;
            case ‘UPDATE_THEME_BACKGROUND‘
: appState.themeBackground = action.color;break; default break; } } //修改完數據,就需要重新渲染了,定義渲染函數 function renderApp(state){ renderHeader(state); renderFooter(state); } function renderHeader(state){ header = document.getElementById
(‘header‘); header.style.color = state.color; header.style.backgroundColor = state.themeBackground; } function renderFooter(state){ title = document.getElementById(‘footer‘); footer.style.color = state.color; footer.style.backgroundColor = state.themeBackground; } dispatch({type:‘UPDATE_THEME_COLOR‘,color:‘green‘})//修改主題色 renderApp(appState)//執行重新渲染

2、封裝state和dispatch,命名為store,方便復用,同時添加監聽,當數據變化時,通知訂閱者重新渲染

    function createStore(state,stateChanger){
        const getState = () => state;//獲取數據
        const listeners = [];
        const subscribe = (listener) => listeners.push(listener);
        const dispatch = (action) => {
            stateChanger(state, action);
            listeners.forEach((listener)=>listener());
        };
        return {getState,dispatch,subscribe};
    }
    //實例化一個store。為了統一命名,dispatch改為stateChanger,appState改名為state
    const store = createStore(state,stateChanger);
    renderApp(store.getState());//首次渲染
    store.subscribe(renderApp(store.getState()));//添加訂閱者,監聽到變化就重新渲染數據
    store.dispatch({type:‘UPDATE_THEME_COLOR‘,color:‘green‘});//修改主題

3、第二步有一個很大的缺陷就是,每次修改數據就重新全部渲染一遍,對性能影響很大。
優化點是: 判斷數據是否有變化,如果沒變化就不需要重新渲染,另外stateChanger與state合為一體。

    //此處用到es6的淺復制,例如
    let a = { name:‘HAPPY‘,attr:{age:23},common:{sex:‘女‘}};
    let b = {...a,attr:{age:24}};//b為{name:‘HAPPY‘,attr:{age:24},common:{sex:‘女‘}} 
    a.name===b.name//true
    a.attr===b.attr//false
    a.common===b.common//true
    //這樣有50%以上的復用率
    function stateChanger(state,action){
        if(!state){
             return {
                 themeColor:‘red‘,
                 themeBackground:‘black‘
             }
        }
        const newState = {...state};
        switch(action.type){
            case ‘UPDATE_THEME_COLOR‘:
                return {
                    ...newState,
                    themeColor: action.color
                };
            case ‘UPDATE_THEME_BACKGROUND‘:
                return {
                    ...newState,
                    themeBackground: action.color
                };
            default 
                return{
                    ...newState
                };  
        }
    }
    //那麽渲染的時候需要知道oldState和newState,這樣才能對比數據的變化
    function renderApp(state,oldState={}){//此處oldState放在後面,並且給默認值,是為了兼容首次渲染,首次渲染olsState是沒有值的,所以給默認值
        if(oldState===state){
            return;
        }
        renderHeader(oldState,state);
        renderFooter(oldState,state);
    }
    //如果renderHeder需要的渲染的數據是state內的子對象,那麽在renderHeader渲染之前,也需要判斷一下數據是否有變化,此處舉的例子state結構簡單,所以不需要判斷。
    
    function createStore(stateChanger){//此處是優化state與stateChanger結合。
        let state = null
        const getState = () => state;//獲取數據
        const listeners = [];
        const subscribe = (listener) => listeners.push(listener);
        const dispatch = (action) => {
            state = stateChanger(state, action);
            listeners.forEach((listener)=>listener());
        };
        return {getState,dispatch,subscribe};
    }
    //最後需要oldState,那麽我們就需要定義oldState
    const store = createStore(stateStranger);
    const oldState = store.getState();
    store.subscribe(()=>{
        const newState = store.getState();
        renderApp(oldState,newState);
        oldState = newState;
    });//添加監聽
    renderApp(store.getState())//首次渲染
    store.dispatch({type:‘UPDATE_THEME_COLOR‘,color:‘green‘});//修改主題

4.此時的stateChanger是一個純函數,就是內部邏輯只與參數有關,並且無副作用(也就是對其他數據沒有任何影響)。它要做的僅僅是 —— 初始化和計算新的 state。(並不會修改state,因為我們每次返回的都是新的對象)
而這個函數就是redux中的reducer,那麽我們給stateChanger改名字為reducer.現在我們的redux就實現完成了。

     function reducer(action){...}
     function createStore(reducer){...}
     //接下來我們就可以定義不同的reducer,生成不同的store了,並且修改監聽,例如
     function themeReducer(action){...}
     const store = createStore(themeReducer);
     // 監聽數據變化重新渲染頁面
     store.subscribe(() => renderApp(store.getState()))
     // 首次渲染頁面
     renderApp(store.getState())
     // 後面可以隨意 dispatch 了,頁面自動更新
     store.dispatch(...)
但是我們怎麽跟react進行連接呢?怎麽把redux用在react中呢?就需要react-redux來連接。下一篇繼續

參考教程:(http://huziketang.mangojuice.top/books/react)

redux的實現過程