Redux 源碼解析系列(一) -- Redux的實現思想

文章來源: IMweb前端社區 黃qiong(imweb.io)
IMweb團隊正在招聘啦,簡歷發至jayccchen@tencent.com前端

Redux 實際上是用來幫咱們管理狀態的一個框架,它暴露給咱們四個接口,分別是:react

  • createStoreweb

  • combineReducersredux

  • bindActionCreators性能優化

  • applyMiddlewareapp

  • compose框架

源碼系列裏會分別對這五個接口進行解析。less

Redux 的源碼解析系列開篇以前,先來了解一下它的實現思想。函數

爲何要有dispatch

假設一種場景下,app裏每一個組件都須要拿到appState的一部分進行渲染。性能

可是這裏存在一個風險就是,誰均可以修改appState的值,換句話說,有一天當appState變了你都不知道是誰改的,因此咱們須要有一個管理員來幫咱們管理咱們的狀態,這時候引入了dispatch函數,來專門修改負責數據的修改。

function dispatch (action) {
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      appState.title.text = action.text
      break
    case 'UPDATE_TITLE_COLOR':
      appState.title.color = action.color
      break
    default:
      break
  }
}

解決問題:
既能夠解決組件共享問題,同時不會有數據能夠被任意修改的問題。

爲何要有createStore

如今咱們有了狀態,又有了dispatch,這時候咱們須要一個高層管理者store,幫咱們管理好他們,這樣再用的時候就能夠直接store.getState store.dispatch的方式獲取和更改組件。

因此咱們就有了createStore這個函數幫咱們生成store, 而後將getState 跟 dispatch 方法export出去。

function createStore(state, stateChanger) {
  const getState = () => state;
  const dispatch = (action) => stateChanger(state, action)
  return {getState, dispatch}
}

createStore 接受兩個參數,一個是表示app的 state。另一個是 stateChanger,它來描述應用程序狀態會根據 action 發生什麼變化,其實就是至關於本節開頭的 dispatch 代碼裏面的內容,咱們後來會將它命名爲reducer。

可是這裏還有一個問題,就是數據發生改變以後,咱們都須要手動在從新render一次APP,這時候就須要觀察者模式,訂閱數據的改變,而後自動調用renderAPP,因此咱們的createStore功能又強大啦~

function createStore(state, reducer) {
  const getState = () => state;
  const listeners = [];
  const subscribe = (listener) => {
    listeners.push(listener)
  } 
  const dispatch = (action) => {
    reducer(state, action);
    // 數據已發生改變就把全部的listener跑一遍
    listeners.forEach((listener) => {
      listener()
    })
  }

  return {getState, dispatch, subscribe}
}

咱們就能夠這樣使用

store.subscribe(() => renderApp(store.getState()))

由此能夠看出,dispatch是一個重要函數,當每一次咱們調用dispatch去改變app的狀態的時候,它都會同時執行全部的訂閱函數。

到這一步,一個APP就已經能夠無壓力的跑起來啦,最後一步,固然是關注性能,咱們這個app 仍是有嚴重性能問題的,由於每一次的dispatch 全部的子組件都會被從新渲染,這固然是沒必要要的。

因此就須要對reducer產生的先後appState進行一個對比,這就要求reducer必須是一個純函數,返回的是一個新的object,不能直接更改reducer的參數,這樣纔可以對比能夠經過對比先後的state是否相等,來決定是否render

// reducer用來管理狀態變化
function reducer (state, action) {
  if(!state) {
    return appState;
  }
  switch (action.type) {
    case 'CHANGE_TITLE':
      return {
        ...state,
        title: {
          ...state.title,
          text: action.text
        }
      }
    case 'CHANGE_CONTENT':
      return {
        ...state,
        content: {
          ...state.content,
          color: action.color
        }
      }
  }
}
function createStore(state, reducer) {
  let appState = state;
  const getState = () => appState;
  const listeners = [];
  const subscribe = (listener) => {
    listeners.push(listener)
  } 
  const dispatch = (action) => {
    // 覆蓋原先的appState
    appState = reducer(state, action);
    listeners.forEach((listener) => {
      listener()
    })
  }

  return {getState, dispatch, subscribe}
}

OK,到這一步,咱們的redux就基本完成啦~ 接着改裝下咱們的reducer,讓它有一個初始值,這樣咱們的createStore就只須要傳入一個reducer便可

// reducer用來管理狀態變化
function reducer (state, action) {
//設置初始值
  if(!state) {
    return appState;
  }
  switch (action.type) {
    case 'CHANGE_TITLE':
      return {
        ...state,
        title: {
          ...state.title,
          text: action.text
        }
      }
    case 'CHANGE_CONTENT':
      return {
        ...state,
        content: {
          ...state.content,
          color: action.color
        }
      }
  }
}
function createStore (reducer) {
  let state = null
  const listeners = []
  const subscribe = (listener) => listeners.push(listener)
  const getState = () => state
  const dispatch = (action) => {
    // 能夠看到 因爲reducer返回的是一個新的object,那在外層,咱們就能夠對比nextProps跟t his.props 來決定是否渲染
    state = reducer(state, action)
    listeners.forEach((listener) => listener())
  }
  dispatch({}) // 初始化 state
  return { getState, dispatch, subscribe }
}

總結如下:createStore裏要作三件事

  • getState

  • dispatch

  • subscribe

  • 初始reducer的狀態

四個步驟

// 定一個 reducer, 負責管理數據變化還有初始化appState的數據
function reducer (state, action) {
  /* 初始化 state 和 switch case */
}

// 生成 store
const store = createStore(reducer)

// 監聽數據變化從新渲染頁面
store.subscribe(() => renderApp(store.getState()))

// 首次渲染頁面
renderApp(store.getState()) 

// 後面能夠隨意 dispatch 了,頁面自動更新
store.dispatch(...)

咱們整個過程就是不斷地發現問題,解決問題

一、共享狀態 -> dispatch

二、store統一管理 dispatch getState

三、性能優化 --> reducer是一個純函數

四、最終初始化整個reducer

以上就是redux的大體思想。

參考文檔:

http://huziketang.com/books/r...

相關文章
相關標籤/搜索