從零開始擼一個(gè)Redux

Redux

Redux 屬于視圖狀態(tài)管理工具,它解決了傳統(tǒng) MVVM 架構(gòu)的 VM 層狀態(tài)管理責(zé)任過重的問題,他使用訂閱發(fā)布的模式,將 VM 層的狀態(tài)集中式的管理,這樣做的好處有

  • 視圖通訊變得簡單,多個(gè)視圖可以輕松共用狀態(tài)
  • 代碼分割更加簡便

它有以下特點(diǎn)

  • 不支持異步,派發(fā) action 的過程中,沒有實(shí)現(xiàn)thunk接口,不能實(shí)現(xiàn)阻塞
  • 支持對(duì) state 的 cbs(控制反轉(zhuǎn)) 風(fēng)格處理,reducer 為函數(shù)

開始擼一個(gè) redux

我們建立一個(gè)redux文件夾,然后在該目錄創(chuàng)建一個(gè)index.js

import createStore from "./core/createStore";
export default createStore;

接下來我們來實(shí)現(xiàn)createStore方法,在該目錄下建立core目錄,并在該目錄下建立createStore.js文件,創(chuàng)建createStore方法

createStore,創(chuàng)建一個(gè) store,一個(gè) redux 架構(gòu)應(yīng)該只有一個(gè) store,該 store 用于維持 state,派發(fā) action,注冊(cè)監(jiān)聽器,

redux 使用的是函數(shù)式編程,所以下面會(huì)用有一個(gè)閉包函數(shù),執(zhí)行后返回 redux 提供的 API

export default function createStore(reducer, preLoadState, enhancer) {
  if (enhancer) {
    // 中間件處理
    return enhancer(createStore)(reducer, preLoadState);
  }

  function getState() {} // 獲取state

  function subscribe() {}

  function dispatch() {}

  function replaceReducer() {}
  // 告訴控制臺(tái),初始化store 順便觸發(fā)reducer的default分支,拿到initialState
  dispatch({ type: "REDUX_INIT" });
  return {
    getState,
    subscribe,
    dispatch,
    replaceReducer,
  };
}

A. getState (獲取狀態(tài))

getState 用于獲取 store 的 state,接下來我們來實(shí)現(xiàn) getState 方法

export default function createStore(reducer, preLoadState, enhancer) {
  let currentState = preLoadState;
  function getState() {
    if (isDispatching) {
      throw new Error("你不能獲取state,因?yàn)楝F(xiàn)在正在派發(fā)action");
    }
    return currentState;
  }
  // ...
}

B. dispatch (調(diào)度)

dispatch 方法用于派發(fā)一個(gè) action,action 由typepayload組成

export default function createStore(reducer, preLoadState, enhancer) {
  let isDispatching = false;
  let currentReducer = reducer;
  let currentListeners = [];
  let nextListeners = currentListeners;

  function dispatch(action) {
    try {
      isDispatching = true;
      // 處理傳入的action
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }
    currentListeners = nextListeners;
    const listeners = currentListeners;
    // 遍歷訂閱者,通知action已經(jīng)派發(fā)完成
    listeners.forEach((lisntener) => listener());
    return action;
  }
  // ...
}

C. subscribe (訂閱)

該方法用于提供給組件訂閱 store 的變化,當(dāng) store 發(fā)生變化時(shí),既可在訂閱方法中更新視圖

export default function createStore(reducer, preLoadState, enhancer) {
  let isSubscribed = false;
  let nextListeners = [];
  function subscribe(listener) {
    nextListeners.push(listener);
    return function unsubscribe() {
      if (!isSubscribed) {
        return;
      }
      if (isDispatching) {
        throw new Error("不能在派發(fā)action的時(shí)候,取消store的訂閱");
      }
      isSubscribed = true;
      // 可以直接通過indexOf進(jìn)行函數(shù)的查找
      const index = nextListeners.indexOf(listener);
      // 刪除緩存中的訂閱函數(shù)
      nextListeners.splice(index, 1);
    };
  }
  // ...
}

D. replaceReducer (替換處理者)

用于替換 store 的 reducer 對(duì)象,這個(gè)方法沒什么特別的

export default function createStore(reducer, preLoadState, enhancer) {
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer;
    // 告訴控制臺(tái),替換了reducer
    dispatch({ type: "REDUX_REPLACE" });
  }
  // ...
}

結(jié)束

這樣,我們就簡單的擼完一個(gè) redux 了,我們以 React 為例子,來看看如何使用它

// reducer
const reducer = (state = {}, action) => {
  switch (action.tpye) {
    case "TMD": {
      // 對(duì)state進(jìn)行處理
      state.name = "干啥啥不行";
      return state;
    }
    default: {
      return state;
    }
  }
};

const store = createStore(reducer);

import React from "react";

class App extends React.Component {
  constructor() {
    super();
    this.state = store.getState();
  }

  componentDidMount() {
    this.unscribe = store.subscribe(() => {
      this.setState(store.getState());
    });
  }

  render() {
    const styleObj = {
      background: "#66ccff",
      width: "200px",
      color: "#fff",
      textAlign: "center",
      cursor: "pointer",
    };
    return (
      <ul>
        以下是model的數(shù)據(jù) : <br />
        <div style={styleObj} onClick={this.changeStore.bind(this)}>
          點(diǎn)擊這里改變store數(shù)據(jù)
        </div>
        {JSON.stringify(this.state.model)}
      </ul>
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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