18.狀態(tài)管理基礎(chǔ)理論及redux自己實現(xiàn)

傳統(tǒng)MVC框架的缺陷

什么是MVC?

Request->Controller->Model
···························->View
MAC的全名是Model View Controller,是模型(Model)、視圖(View)、控制器(controller)的縮寫,是一種軟件設(shè)計典范。
VView視圖是指用戶看到并與之交互界面。
MModel模型是管理數(shù)據(jù),很多業(yè)務(wù)邏輯都在模型中完成。在MVC中三個部件中,模型擁有最多的處理人物。
CController控制器是指控制器接收用戶的輸入并調(diào)用模型和視圖去完成用戶的需求,控制器本身不輸出任何東西和做任何處理。他只是接受請求并決定調(diào)用哪個模型構(gòu)件去處理請求,然后在確定用哪個視圖來顯示返回的數(shù)據(jù)。

MVC只是看起來很美

MVC框架的數(shù)據(jù)流很立項,請求先到Controller,由Controller調(diào)用Model中的數(shù)據(jù)交給View進(jìn)行渲染,但是在實際的項目中,又是允許ModelView直接通信的。

Flux

在2013年,Facebookreact兩項的同事退出了Flux架構(gòu)思想,React的初衷實際上是用來代替Jquery的,Flux實際上就可以用來代替Backbone.js、Ember.js等一系列MVC架構(gòu)和前端JS框架。

其實在Fluxreact里的應(yīng)用就類似于vue中的vuex的作用,但是在vue中,vue是完整的MVVM框架,而vuex只是一個全局的插件。

react只是一個MVC中的V(視圖層),只管頁面的渲染,一旦有數(shù)據(jù)管理的時候,react本身的能力就不足以支撐復(fù)雜組件結(jié)構(gòu)的項目,在傳統(tǒng)的MVC中,就需要用到ModelController。Facebook對于當(dāng)時市面上的MVC框架并不滿意,于是就有了Flux,但Flux并不是一個MVC框架。

Action->Dispatcher->Store->View->Action->Dispatcher...

Flux
  • View:視圖層
  • ActionCreator(動作創(chuàng)造者):視圖層發(fā)出的消息(例如:onClick)
  • Dispatcher(派發(fā)器):用來接收Actions、執(zhí)行回調(diào)函數(shù)
  • Store(數(shù)據(jù)層):用來存放應(yīng)用的狀態(tài),一旦發(fā)生變動,就提醒Views要更新頁面

Flux的流程

  1. 組件獲取到store中保存的數(shù)據(jù)掛載在自己的狀態(tài)上
  2. 用戶產(chǎn)生了操作,調(diào)用actions的方法
  3. actions接收到了用戶的操作,進(jìn)行一系列的邏輯代碼、異步操作
  4. 然后actions回創(chuàng)建出對應(yīng)的action,action帶有標(biāo)識性的屬性
  5. actions調(diào)用dispatcher的dispatch方法將action傳遞給dispatcher
  6. dispatcher接收到action并根據(jù)標(biāo)識信息判斷之后,調(diào)用store的更改數(shù)據(jù)的方法
  7. store的方法被調(diào)用后,更改狀態(tài),并觸發(fā)自己的某一件事
  8. store更改狀態(tài)后事件被觸發(fā),該事件的處理程序會通知view去獲取最新的內(nèi)容

Redux

React只是DOM的一個抽象層,并不是Web應(yīng)用的完整解決方案。有兩個反面它沒有涉及到:

  • 代碼結(jié)構(gòu)
  • 組件之間的通信

2013年Facebook提出了Flux架構(gòu)的思想,引發(fā)了很多的實現(xiàn)。2015年,Redux出現(xiàn),將Flux于函數(shù)式編程結(jié)合起來,很短時間內(nèi)就成為了最熱門的前端架構(gòu)。
如果你不知道是否需要Redux,那就是不需要它。
還有遇到React實在解決不了的問題,你才需要Redux
簡單來說,如果你的UI層非常簡單,沒有很多互動,Redux就是不必要的,用了反而增加復(fù)雜性。

  • 用戶的使用方式非常簡單
  • 用戶之間沒有協(xié)作
  • 不需要與服務(wù)器大量交互,也沒有使用WebSocket
  • 視圖層(view)只從單一來源獲取數(shù)據(jù)
需要使用Redux的項目
  • 用戶的使用方式復(fù)雜
  • 不同身份的用戶有不同的使用方式(例如:普通用戶和管理員)
  • 多個用戶之間可以協(xié)作
  • 與服務(wù)器大量交互,或者使用了WebSoket
  • view要從多個來源獲取數(shù)據(jù)
Redux的設(shè)計思想
  1. web應(yīng)用是一個狀態(tài)機,視圖與狀態(tài)是一一對應(yīng)。
  2. 所有的狀態(tài),保存在一個對象里(唯一數(shù)據(jù)源)

注意:flux、redux都不是必須和react搭配使用的,因為flux和redux是完整的架構(gòu),在學(xué)react的時候,只是將react的組件作為redux中的視圖層去使用了

Redux的使用的三大原則
  • Single Source of Truth (唯一數(shù)據(jù)源)
  • State is read-only (狀態(tài)是只讀的)
  • Changes are made with pure function (數(shù)據(jù)的改變必須通過純函數(shù)完成)

自己實現(xiàn)一個Redux

基礎(chǔ)代碼

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>redux原理</title>
</head>
<body>
<div>
    <button onclick="dispatch({type:'JIAN',n:2})">-</button>
    <span id="countDisplay">10</span>
    <button onclick="dispatch({type:'JIA',n:3})">+</button>
</div>
<script>
    const countDisplay = document.querySelector("#countDisplay");
    //保存數(shù)值
    const countState = {
        count: 5,
    };

    const renderCount = (state) => {
        countDisplay.innerHTML = state.count;
    };

    renderCount(countState);

    // const jian=()=>{
    //     countState.count=countState.count-1;
    //     renderCount(countState)
    // }

    /**
     * 中控 來調(diào)用修改state和渲染頁面
     * @param action 參數(shù)
     * @param action.type 類型
     * @param action.n 跳階
     */
    const dispatch = (action) => {
        changeState(action);
        renderCount(countState)
    };
    const changeState=(action)=>{
        switch (action.type) {
            case "JIAN":
                countState.count-=action.n;
                break;
            case "JIA":
                countState.count+=action.n;
                break;
            default:
                break;
        }
    };
</script>
</body>
</html>

以上這種方式countState變量是可以進(jìn)行改變的,但是我們想要的效果是countState中的所有的值不允許進(jìn)行改變,卻又想讓頁面上渲染的數(shù)值是正確的,即如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>redux原理</title>
</head>
<body>
<div>
    <button onclick="store.dispatch({type:'JIAN',n:2})">-</button>
    <span id="countDisplay">10</span>
    <button onclick="store.dispatch({type:'JIA',n:3})">+</button>
</div>
<script>
    const countDisplay = document.querySelector("#countDisplay");
    //保存數(shù)值
    const countState = {
        count: 5,
    };
    //修改state的值
    const changeState = (state, action) => {
        if(!state){
            return countState;
        }
        switch (action.type) {
            case "JIAN":
                return {
                    ...state, //解構(gòu)出state中所有的屬性 并做返回  在此 return 后 changeState就為新的state就不再指向countState了
                    count: state.count - action.n
                };
            case "JIA":
                return {
                    ...state,
                    count: state.count + action.n
                };
            default:
                return state;
        }
    };

    /**
     * 創(chuàng)建一個私有管理域
     */
    const createStore = () => {
        let state=null;
        //返回全局的countState
        const getState = () => state;
        let listens = [];//保存事件
        //
        const dispatch = (action) => {
            state = changeState(state, action);
            listens.forEach(item => item());//執(zhí)行監(jiān)聽中的所有函數(shù)
        };

        const setListens = (listen) => {
            if (Array.isArray(listen)) {
                listen.forEach(item => {
                    listens.push(item)
                })
            } else {
                listens.push(listen);
            }
        };
        dispatch({});//默認(rèn)執(zhí)行一次空對象
        return {
            getState,
            dispatch,
            setListens
        }
    };

    const store = createStore(changeState());

    const renderCount = () => {
        countDisplay.innerHTML = store.getState().count;
    };
    renderCount();
    //首次加載的時候把renderCount監(jiān)聽存放到每次數(shù)值改變事件中  (觀察者模式)
    store.setListens(renderCount)
</script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容