Redux 源码解析系列(一) -- Redux的实现思想
文章來源: IMweb前端社區(qū) 黃qiong(imweb.io)
IMweb團(tuán)隊(duì)正在招聘啦,簡(jiǎn)歷發(fā)至jayccchen@tencent.com
Redux 其實(shí)是用來幫我們管理狀態(tài)的一個(gè)框架,它暴露給我們四個(gè)接口,分別是:
- createStore
- combineReducers
- bindActionCreators
- applyMiddleware
- compose
源碼系列里會(huì)分別對(duì)這五個(gè)接口進(jìn)行解析。
Redux 的源碼解析系列開篇之前,先來了解一下它的實(shí)現(xiàn)思想。
為什么要有dispatch
假設(shè)一種場(chǎng)景下,app里每個(gè)組件都需要拿到appState的一部分進(jìn)行渲染。
但是這里存在一個(gè)風(fēng)險(xiǎn)就是,誰(shuí)都可以修改appState的值,換句話說,有一天當(dāng)appState變了你都不知道是誰(shuí)改的,所以我們需要有一個(gè)管理員來幫我們管理我們的狀態(tài),這時(shí)候引入了dispatch函數(shù),來專門修改負(fù)責(zé)數(shù)據(jù)的修改。
function dispatch (action) {switch (action.type) {case 'UPDATE_TITLE_TEXT':appState.title.text = action.textbreakcase 'UPDATE_TITLE_COLOR':appState.title.color = action.colorbreakdefault:break} }復(fù)制代碼解決問題:
既可以解決組件共享問題,同時(shí)不會(huì)有數(shù)據(jù)可以被任意修改的問題。
為什么要有createStore
現(xiàn)在我們有了狀態(tài),又有了dispatch,這時(shí)候我們需要一個(gè)高層管理者store,幫我們管理好他們,這樣再用的時(shí)候就可以直接store.getState store.dispatch的方式獲取和更改組件。
所以我們就有了createStore這個(gè)函數(shù)幫我們生成store, 然后將getState 跟 dispatch 方法export出去。
function createStore(state, stateChanger) {const getState = () => state;const dispatch = (action) => stateChanger(state, action)return {getState, dispatch} }復(fù)制代碼createStore 接受兩個(gè)參數(shù),一個(gè)是表示app的 state。另外一個(gè)是 stateChanger,它來描述應(yīng)用程序狀態(tài)會(huì)根據(jù) action 發(fā)生什么變化,其實(shí)就是相當(dāng)于本節(jié)開頭的 dispatch 代碼里面的內(nèi)容,我們后來會(huì)將它命名為reducer。
但是這里還有一個(gè)問題,就是數(shù)據(jù)發(fā)生改變之后,我們都需要手動(dòng)在重新render一次APP,這時(shí)候就需要觀察者模式,訂閱數(shù)據(jù)的改變,然后自動(dòng)調(diào)用renderAPP,所以我們的createStore功能又強(qiáng)大啦~
function createStore(state, reducer) {const getState = () => state;const listeners = [];const subscribe = (listener) => {listeners.push(listener)} const dispatch = (action) => {reducer(state, action);// 數(shù)據(jù)已發(fā)生改變就把所有的listener跑一遍listeners.forEach((listener) => {listener()})}return {getState, dispatch, subscribe} }復(fù)制代碼我們就可以這樣使用
store.subscribe(() => renderApp(store.getState()))復(fù)制代碼由此可以看出,dispatch是一個(gè)重要函數(shù),當(dāng)每一次我們調(diào)用dispatch去改變app的狀態(tài)的時(shí)候,它都會(huì)同時(shí)執(zhí)行所有的訂閱函數(shù)。
到這一步,一個(gè)APP就已經(jīng)可以無壓力的跑起來啦,最后一步,當(dāng)然是關(guān)注性能,我們這個(gè)app 還是有嚴(yán)重性能問題的,因?yàn)槊恳淮蔚膁ispatch 所有的子組件都會(huì)被重新渲染,這當(dāng)然是不必要的。
所以就需要對(duì)reducer產(chǎn)生的前后appState進(jìn)行一個(gè)對(duì)比,這就要求reducer必須是一個(gè)純函數(shù),返回的是一個(gè)新的object,不能直接更改reducer的參數(shù),這樣才能夠?qū)Ρ瓤梢酝ㄟ^對(duì)比前后的state是否相等,來決定是否render
// reducer用來管理狀態(tài)變化 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}}} }復(fù)制代碼function createStore(state, reducer) {let appState = state;const getState = () => appState;const listeners = [];const subscribe = (listener) => {listeners.push(listener)} const dispatch = (action) => {// 覆蓋原先的appStateappState = reducer(state, action);listeners.forEach((listener) => {listener()})}return {getState, dispatch, subscribe} }復(fù)制代碼OK,到這一步,我們的redux就基本完成啦~ 接著改裝下我們的reducer,讓它有一個(gè)初始值,這樣我們的createStore就只需要傳入一個(gè)reducer即可
// reducer用來管理狀態(tài)變化 function reducer (state, action) { //設(shè)置初始值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}}} }復(fù)制代碼function createStore (reducer) {let state = nullconst listeners = []const subscribe = (listener) => listeners.push(listener)const getState = () => stateconst dispatch = (action) => {// 可以看到 由于reducer返回的是一個(gè)新的object,那在外層,我們就可以對(duì)比nextProps跟t his.props 來決定是否渲染state = reducer(state, action)listeners.forEach((listener) => listener())}dispatch({}) // 初始化 statereturn { getState, dispatch, subscribe } }復(fù)制代碼總結(jié)以下:createStore里要做三件事
- getState
- dispatch
- subscribe
- 初始reducer的狀態(tài)
四個(gè)步驟
// 定一個(gè) reducer, 負(fù)責(zé)管理數(shù)據(jù)變化還有初始化appState的數(shù)據(jù) function reducer (state, action) {/* 初始化 state 和 switch case */ }// 生成 store const store = createStore(reducer)// 監(jiān)聽數(shù)據(jù)變化重新渲染頁(yè)面 store.subscribe(() => renderApp(store.getState()))// 首次渲染頁(yè)面 renderApp(store.getState()) // 后面可以隨意 dispatch 了,頁(yè)面自動(dòng)更新 store.dispatch(...)復(fù)制代碼我們整個(gè)過程就是不斷地發(fā)現(xiàn)問題,解決問題
1、共享狀態(tài) -> dispatch
2、store統(tǒng)一管理 dispatch getState
3、性能優(yōu)化 --> reducer是一個(gè)純函數(shù)
4、最終初始化整個(gè)reducer
以上就是redux的大致思想。
參考文檔:
huziketang.com/books/react…
轉(zhuǎn)載于:https://juejin.im/post/5a119426f265da430f31b165
總結(jié)
以上是生活随笔為你收集整理的Redux 源码解析系列(一) -- Redux的实现思想的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python添加pdf水印
- 下一篇: 使用栈结构完毕四则运算