文章來源: IMweb前端社區 黃qiong(imweb.io)
IMweb團隊正在招聘啦,簡歷發至jayccchen@tencent.com前端
Redux 實際上是用來幫咱們管理狀態的一個框架,它暴露給咱們四個接口,分別是:react
createStoreweb
combineReducersredux
bindActionCreators性能優化
applyMiddlewareapp
compose框架
源碼系列裏會分別對這五個接口進行解析。less
Redux 的源碼解析系列開篇以前,先來了解一下它的實現思想。函數
假設一種場景下,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 } }
解決問題:
既能夠解決組件共享問題,同時不會有數據能夠被任意修改的問題。
如今咱們有了狀態,又有了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的大體思想。
參考文檔: