成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

簡(jiǎn)單梳理Redux的源碼與運(yùn)行機(jī)制

劉東 / 2573人閱讀

摘要:然后循環(huán)調(diào)用中的更新函數(shù),更新函數(shù)一般是我們的渲染函數(shù),函數(shù)內(nèi)部會(huì)調(diào)用來(lái)獲取數(shù)據(jù),所以頁(yè)面會(huì)更新。

前言

前幾天寫了一篇react另一個(gè)狀態(tài)管理工具Unstated的源碼解析。 開(kāi)啟了我的看源碼之路。想一想用了好長(zhǎng)時(shí)間的redux,但從沒(méi)有深究過(guò)原理,遇到報(bào)錯(cuò)更是懵逼,所以就啃了一遍它的源碼,寫了這篇文章, 分享我對(duì)于它的理解。

API概覽

看一下redux源碼的index.js,看到了我們最常用的幾個(gè)API:

createStore

combineReducers

bindActionCreators

applyMiddleware

compose

不著急分析,我們先看一下Redux的基本用法:

import React from "react" import ReactDOM from "react-dom" import { createStore } from "redux" const root = document.getElementById("root") // reducer 純函數(shù) const reducer = (state = 0, action) => { switch (action.type) { case "INCREMENT": return state + 1 case "DECREMENT": return state - 1 default: return state } } // 創(chuàng)建一個(gè)store const store = createStore(reducer) const render = () => ReactDOM.render(

{store.getState()}
, root ) render() // store訂閱一個(gè)更新函數(shù),待dispatch之后,執(zhí)行這個(gè)更新函數(shù),獲取新的值 store.subscribe(render)

這里實(shí)現(xiàn)的是一個(gè)點(diǎn)擊按鈕加減數(shù)字的效果,點(diǎn)擊觸發(fā)的行為,與展示在頁(yè)面上的數(shù)字變化,都是通過(guò)redux進(jìn)行的。我們通過(guò)這個(gè)例子來(lái)分析一下redux是怎么工作的:

使用reducer創(chuàng)建一個(gè)store,便于我們通過(guò)store來(lái)與redux溝通

頁(yè)面上通過(guò)store.getState()拿到了當(dāng)前的數(shù)字,初始值為0(在reducer中)

store.subscribe(render),訂閱更新頁(yè)面的函數(shù),在reducer返回新的值時(shí),調(diào)用。(實(shí)際subscribe會(huì)把函數(shù)推入listeners數(shù)組,在之后循環(huán)調(diào)用)

點(diǎn)擊按鈕,告訴redux,我是要增加還是減少(調(diào)用dispatch,傳入action)

調(diào)用dispatch之后,dispatch函數(shù)內(nèi)部會(huì)調(diào)用我們定義的reducer,結(jié)合當(dāng)前的state,和action,返回新的state

返回新的state之后,調(diào)用subscribe訂閱的更新函數(shù),更新頁(yè)面 目前為止,我們所有的操作都是通過(guò)store進(jìn)行的,而store是通過(guò)createStore創(chuàng)建的,那么我們來(lái)看一下它內(nèi)部的邏輯

createStore

createStore總共接收三個(gè)參數(shù):reducer, preloadedState, enhancer,

reducer:一個(gè)純函數(shù),接收上一個(gè)(或初始的)state,和action,根據(jù)action 的type返回新的state

preloadedState:一個(gè)初始化的state,可以設(shè)置store中的默認(rèn)值,

enhancer:增強(qiáng)器,用來(lái)擴(kuò)展store的功能

暴露給我們幾個(gè)常用的API:

dispatch:接收一個(gè)action, 是一個(gè)object{type:"a_action_type"}作為參數(shù),之后其內(nèi)部會(huì)調(diào)用reducer,根據(jù)這個(gè)action,和當(dāng)前state,返回新的state。

subscribe:訂閱一個(gè)更新頁(yè)面的函數(shù),放進(jìn)linsteners數(shù)組,用于在reducer返回新的狀態(tài)的時(shí)候被調(diào)用,更新頁(yè)面。

getState:獲取store中的狀態(tài)

我們先通過(guò)接收的參數(shù)和暴露出來(lái)的api梳理一下它的機(jī)制:

首先是接收上面提到的三個(gè)參數(shù)創(chuàng)建一個(gè)store,store是存儲(chǔ)應(yīng)用所有狀態(tài)的地方。同時(shí)暴露出三個(gè)方法,UI可以通過(guò)store.getState()獲取到store中的數(shù)據(jù), store.subscribe(),作用是讓store訂閱一個(gè)更新UI的函數(shù),將這個(gè)函數(shù)push到listeners數(shù)組中,等待執(zhí)行。 store.dispatch()是更新store中數(shù)據(jù)的唯一方法,dispatch被調(diào)用后,首先會(huì)調(diào)用reducer,根據(jù)當(dāng)前的state和action返回新的狀態(tài)。然后循環(huán)調(diào)用listeners中的更新函數(shù), 更新函數(shù)一般是我們UI的渲染函數(shù),函數(shù)內(nèi)部會(huì)調(diào)用store.getState()來(lái)獲取數(shù)據(jù),所以頁(yè)面會(huì)更新。

看一下createStore函數(shù)的結(jié)構(gòu)

createStore(reducer, preloadedState, enhancer) { // 轉(zhuǎn)換參數(shù) if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState preloadedState = undefined } function getState() { // 返回當(dāng)前的state, 可以調(diào)用store.getState()獲取到store中的數(shù)據(jù), ... } function subscribe(listener) { // 訂閱一個(gè)更新函數(shù)(listener),實(shí)際上的訂閱操作就是把listener放入一個(gè)listeners數(shù)組 // 然后再取消訂閱,將更新函數(shù)從listeners數(shù)組內(nèi)刪除 // 但是注意,這兩個(gè)操作都是在dispatch不執(zhí)行時(shí)候進(jìn)行的。因?yàn)閐ispatch執(zhí)行時(shí)候會(huì)循環(huán)執(zhí)行更新函數(shù),要保證listeners數(shù)組在這時(shí)候不能被改變 ... } function dispatch(action) { // 接收action,調(diào)用reducer根據(jù)action和當(dāng)前的state,返回一個(gè)新的state // 循環(huán)調(diào)用listeners數(shù)組,執(zhí)行更新函數(shù),函數(shù)內(nèi)部會(huì)通過(guò)store.getState()獲取state,此時(shí)的state為最新的state,完成頁(yè)面的更新 ... } return { dispatch, subscribe, getState, } }

結(jié)構(gòu)就是這樣,但是是如何串聯(lián)起來(lái)的呢?下面來(lái)看一下完整的代碼(刪除了一些)

createStore(reducer, preloadedState, enhancer) { if (typeof preloadedState === "function" && typeof enhancer === "undefined") { // 有了這一層判斷,我們就可以這樣傳:createStore(reducer, initialState, enhancer) // 或者這樣: createStore(reducer, enhancer),其中enhancer還會(huì)是enhancer。 enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } // enhancer的作用是擴(kuò)展store,所以傳入createStore來(lái)改造, // 再傳入reducer, preloadedState生成改造后的store,這一有一點(diǎn)遞歸調(diào)用的意思 return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== "function") { throw new Error("Expected the reducer to be a function.") } let currentReducer = reducer // 當(dāng)前的reducer,還會(huì)有新的reducer let currentState = preloadedState // 當(dāng)前的state let currentListeners = [] // 存儲(chǔ)更新函數(shù)的數(shù)組 let nextListeners = currentListeners // 下次dispatch將會(huì)觸發(fā)的更新函數(shù)數(shù)組 let isDispatching = false //類似一把鎖,如果正在dispatch action,那么就做一些限制 // 這個(gè)函數(shù)的作用是判斷nextListeners 和 currentListeners是否是同一個(gè)引用,是的話就拷貝一份,避免修改各自相互影響 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } function getState() { // 正在執(zhí)行reducer的時(shí)候,是不能獲取state的,要等到reducer執(zhí)行完,返回新的state才可以獲取 if (isDispatching) { throw new Error( "You may not call store.getState() while the reducer is executing. " + "The reducer has already received the state as an argument. " + "Pass it down from the top reducer instead of reading it from the store." ) } return currentState } function subscribe(listener) { if (typeof listener !== "function") { throw new Error("Expected the listener to be a function.") } // 由于dispatch函數(shù)會(huì)在reducer執(zhí)行完畢后循環(huán)執(zhí)行l(wèi)isteners數(shù)組內(nèi)訂閱的更新函數(shù),所以要保證這個(gè)時(shí)候的listeners數(shù)組 // 不變,既不能添加(subscribe)更新函數(shù)也不能刪除(unsubscribe)更新函數(shù) if (isDispatching) { throw new Error( "You may not call store.subscribe() while the reducer is executing. " + "If you would like to be notified after the store has been updated, subscribe from a " + "component and invoke store.getState() in the callback to access the latest state. " + "See https://redux.js.org/api-reference/store#subscribe(listener) for more details." ) } let isSubscribed = true ensureCanMutateNextListeners() // 將更新函數(shù)推入到listeners數(shù)組,實(shí)現(xiàn)訂閱 nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } if (isDispatching) { throw new Error( "You may not unsubscribe from a store listener while the reducer is executing. " + "See https://redux.js.org/api-reference/store#subscribe(listener) for more details." ) } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) // 取消訂閱 nextListeners.splice(index, 1) } } function dispatch(action) { if (!isPlainObject(action)) { throw new Error( "Actions must be plain objects. " + "Use custom middleware for async actions." ) } if (typeof action.type === "undefined") { throw new Error( "Actions may not have an undefined "type" property. " + "Have you misspelled a constant?" ) } // 正在dispatch的話不能再次dispatch,也就是說(shuō)不可以同時(shí)dispatch兩個(gè)action if (isDispatching) { throw new Error("Reducers may not dispatch actions.") } try { isDispatching = true // 獲取到當(dāng)前的state currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) // 循環(huán)執(zhí)行當(dāng)前的linstener for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } // dispatch一個(gè)初始的action,作用是不命中你reducer中寫的任何關(guān)于action的判斷,直接返回初始的state dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, // observable replaceReducer和$$observable主要面向庫(kù)開(kāi)發(fā)者,這里先不做解析 // replaceReducer, // [$$observable]: } }

combineReducers

combineReducers用于將多個(gè)reducer合并為一個(gè)總的reducer,所以可以猜出來(lái), 它最終返回的一定是一個(gè)函數(shù),并且形式就是一般的reducer的形式,接收state和action, 返回狀態(tài):

function combine(state, action) { ...... return state }

來(lái)看一下核心代碼:

export default function combineReducers(reducers) { // 獲取到所有reducer的名字,組成數(shù)組 const reducerKeys = Object.keys(reducers) // 這個(gè)finalReducers 是最終的有效的reducers const finalReducers = {} // 以reducer名為key,reducer處理函數(shù)為key,生成finalReducers對(duì)象,形式如下 /* { * reducerName1: f, * reducerName2: f * } */ for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== "production") { unexpectedKeyCache = {} } let shapeAssertionError // assertReducerShape用來(lái)檢查這每個(gè)reducer有沒(méi)有默認(rèn)返回的state, // 我們?cè)趯憆educer時(shí)候,都是要在switch中加一個(gè)default的,來(lái)默認(rèn)返回初始狀態(tài) try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } // 這個(gè)函數(shù),就是上邊說(shuō)的返回的最后的那個(gè)終極reducer,傳入createStore, // 然后在dispatch中調(diào)用,也就是currentReducer // 這個(gè)函數(shù)的核心是根據(jù)finalReducer中存儲(chǔ)的所有reducer信息,循環(huán),獲取到每個(gè)reducer對(duì)應(yīng)的state, // 并依據(jù)當(dāng)前dispatch的action,一起傳入當(dāng)前循環(huán)到的reducer,生成新的state,最終,將所有新生成的 // state作為值,各自的reducerName為鍵,生成最終的state,就是我們?cè)趓eduxDevTool中看到的state樹(shù),形式如下: /* { * reducerName1: { * key: "value" * }, * reducerName2: { * key: "value" * }, * } */ return function combination(state = {}, action) { if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== "production") { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false // 存放最終的所有的state const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { // 獲取每個(gè)reducer的名字 const key = finalReducerKeys[i] // 獲取每個(gè)reducer const reducer = finalReducers[key] // 獲取每個(gè)reducer的舊狀態(tài) const previousStateForKey = state[key] // 調(diào)用該reducer,根據(jù)這個(gè)reducer的舊狀態(tài),和當(dāng)前action來(lái)生成新的state const nextStateForKey = reducer(previousStateForKey, action) // 以各自的reducerName為鍵,新生成的state作為值,生成最終的state object, nextState[key] = nextStateForKey // 判斷所有的state變化沒(méi)變化 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 變化了,返回新的state,否則,返回舊的state return hasChanged ? nextState : state } }

applyMiddleware

redux原本的dispatch方法只能接受一個(gè)對(duì)象作為action

用戶操作 -> dispatch(action) -> reducer(prevState, action) -> 新的state -> 界面

這么直接干脆的操作固然好,可以讓每一步的操作可追蹤,方便定位問(wèn)題,但是帶來(lái)一個(gè)壞處,比如,頁(yè)面需要發(fā)請(qǐng)求獲取數(shù)據(jù),并且把數(shù)據(jù)放到action里面, 最終通過(guò)reducer的處理,放到store中。這時(shí),如何做呢?

用戶操作 -> dispatch(action) -> middleware(action) -> 真正的action -> reducer(prevState, action) -> 新的state -> 界面

重點(diǎn)在于dispatch(action) -> middleware(action) 這個(gè)操作,這里的action可以是一個(gè)函數(shù),在函數(shù)內(nèi)我們就可以進(jìn)行很多操作,包括調(diào)用API, 然后在調(diào)用API成功后,再dispatch真正的action。想要這么做,那就是需要擴(kuò)展redux(改造dispatch方法),也就是使用增強(qiáng)器:enhancer:

const store = createStore(rootReducer, applyMiddleware(thunk), )

applyMiddleware(thunk)就相當(dāng)于一個(gè)enhancer,它負(fù)責(zé)擴(kuò)展redux,說(shuō)白了就是擴(kuò)展store的dispatch方法。

既然要改造store,那么就得把store作為參數(shù)傳遞進(jìn)這個(gè)enhancer中,再吐出一個(gè)改造好的store。吐出來(lái)的這個(gè)store的dispatch方法,是enhancer改造store的最終實(shí)現(xiàn)目標(biāo)。

回顧一下createStore中的這部分:

if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } // 把createStore傳遞進(jìn)enhancer return enhancer(createStore)(reducer, preloadedState) }

看下上邊的代碼,首先判斷enhancer,也就是createStore的第三個(gè)參數(shù)不為undefined且為函數(shù)的時(shí)候,那么去執(zhí)行這個(gè)enhancer。

我們看到enhancer(createStore),是把createStore傳入,進(jìn)行改造,先不管這個(gè)函數(shù)返回啥,我們先看它執(zhí)行完之后還需要的參數(shù) (reducer, preloadedState), 是不是有點(diǎn)眼熟呢?回想一下createStore的調(diào)用方法,createStore(reducer, state)。

由此可知enhancer(createStore)返回的是一個(gè)新的createStore,而這個(gè)createStore是被改造過(guò)后的,它內(nèi)部的dispatch方法已經(jīng)不是原來(lái)的了。至此,達(dá)到了改造store的效果。

那到底是如何改造的呢? 先不著急,我們不妨先看一個(gè)現(xiàn)成的中間件redux-thunk。要了解redux中間件的機(jī)制,必須要理解中間件是怎么運(yùn)行的。

我們先來(lái)看用不用它有什么區(qū)別:

一般情況下,dispatch的action是一個(gè)純對(duì)象

store.dispatch({ type:"EXPMALE_TYPE", payload: { name:"123", } })

使用了thunk之后,action可以是函數(shù)的形式

function loadData() { return (dispatch, getState) => { // 函數(shù)之內(nèi)會(huì)真正dispatch action callApi("/url").then(res => { dispatch({ type:"LOAD_SUCCESS", data: res.data }) }) } } store.dispatch(loadData()) //派發(fā)一個(gè)函數(shù)

一般情況下,dispatch一個(gè)函數(shù)會(huì)直接報(bào)錯(cuò)的,因?yàn)閏reateStore中的dispatch方法內(nèi)部判斷了action的類型。redux-thunk幫我們做的事就是改造dispatch,讓它可以dispatch一個(gè)函數(shù)。 看一下redux-thunk的核心代碼:

function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware();

這里的三個(gè)箭頭函數(shù)是函數(shù)的柯里化。

真正調(diào)用的時(shí)候,理論上是這樣thunk({ dispatch, getState })(next)(action)。

其中,thunk({ dispatch, getState})(next)這部分,看它執(zhí)行時(shí)接收的參數(shù)是一個(gè)action,那么它必然是一個(gè)dispatch方法,在此處相當(dāng)于改造過(guò)后的dispatch,而這部分會(huì)在applyMiddleware中去調(diào)用,(下邊會(huì)講到)

然后從左往右看,{ dispatch, getState }是當(dāng)前store的dispatch和getState方法,是最原始的,便于在經(jīng)過(guò)中間件處理之后,可以拿到最原始的dispatch去派發(fā)真正的action。

next則是被當(dāng)前中間件改造之前的dispatch。注意這個(gè)next,他與前邊的dispatch并不一樣,next是被thunk改造之前的dispatch,也就是說(shuō)有可能是最原始的dispatch,也有可能是被其他中間件改造過(guò)的dispatch。

為了更好理解,還是翻譯成普通函數(shù)嵌套加注釋吧

function createThunkMiddleware(extraArgument) { return function({ dispatch, getState }) { //真正的中間件函數(shù),內(nèi)部的改造dispatch的函數(shù)是精髓 return function(next) { //改造dispatch的函數(shù),這里的next是外部傳進(jìn)來(lái)的dispatch,可能是被其他中間件處理過(guò)的,也可能是最原本的 return function(action) { //這個(gè)函數(shù)就是改造過(guò)后的dispatch函數(shù) if (typeof action === "function") { // 如果action是函數(shù),那么執(zhí)行它,并且將store的dispatch和getState傳入,便于我們dispatch的函數(shù)內(nèi)部邏輯執(zhí)行完之后dispatch真正的action, // 如上邊示例的請(qǐng)求成功后,dispatch的部分 return action(dispatch, getState, extraArgument); } // 否則說(shuō)明是個(gè)普通的action,直接dispatch return next(action); } } } } const thunk = createThunkMiddleware();

總結(jié)一下:說(shuō)白了,redux-thunk的作用就是判斷action是不是一個(gè)函數(shù),是就去執(zhí)行它,不是就用那個(gè)可能被別的中間件改造過(guò)的,也可能是最原始的dispatch(next)去派發(fā)這個(gè)action。

那么接下來(lái)看一下applyMiddleware的源碼:

export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( "Dispatching while constructing your middleware is not allowed. " + "Other middleware would not be applied to this dispatch." ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => { // 假設(shè)我們只是用了redux-thunk,那么此時(shí)的middleware就相當(dāng)于thunk,可以往上看一下thunk返回的函數(shù), // 就是這個(gè): function({ dispatch, getState }),就會(huì)明白了 return middleware(middlewareAPI) }) // 這里的compose函數(shù)的作用就是,將所有的中間件函數(shù)串聯(lián)起來(lái),中間件1結(jié)束,作為參數(shù)傳入中間件2,被它處理, // 以此類推最終返回的是被所有中間件處理完的函數(shù),最開(kāi)始接收store.dispatch為參數(shù),層層改造后被賦值到新的dispatch變量中 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }

先看最簡(jiǎn)單的情況:假設(shè)我們只使用了一個(gè)middleware(redux-thunk),就可以暫時(shí)拋開(kāi)compose,那么這里的邏輯就相當(dāng)于 dispatch = thunk(middlewareAPI)(store.dispatch) 是不是有點(diǎn)熟悉? 在redux-thunk源碼中我們分析過(guò):

真正調(diào)用thunk的時(shí)候,thunk({ dispatch, getState })(next)(action) 其中,thunk({ dispatch, getState })(next)這部分,相當(dāng)于改造過(guò)后的dispatch,而這部分會(huì)在applyMiddleware中去調(diào)用

所以,這里就將store的dispatch方法改造完成了,最后用改造好的dispatch覆蓋原來(lái)store中的dispatch。

來(lái)總結(jié)一下,

中間件和redux的applyMiddleware的關(guān)系。中間件(middleware)會(huì)幫我們改造原來(lái)store的dispatch方法

而applyMiddleware會(huì)將改造好的dispatch方法應(yīng)用到store上(相當(dāng)于將原來(lái)的dispatch替換為改造好的dispatch) 理解中間件的原理是理解applyMiddleware機(jī)制的前提

另外說(shuō)一下,關(guān)于redux-thunk的一個(gè)參數(shù):extraArgument這個(gè)參數(shù)不是特別重要的,一般是傳入一個(gè)實(shí)例,然后在我們需要在真正dispatch的時(shí)候需要這個(gè)參數(shù)的時(shí)候可以獲取到,比如傳入一個(gè)axios 的Instance,那么在請(qǐng)求時(shí)候就可以直接用這個(gè)instance去請(qǐng)求了

import axiosInstance from "../request" const store = createStore(rootReducer, applyMiddleware(thunk.withExtraArgument(axiosInstance))) function loadData() { return (dispatch, getState, instance) => { instance.get("/url").then(res => { dispatch({ type:"LOAD_SUCCESS", data: res.data }) }) } } store.dispatch(loadData())

總結(jié)

到這里,redux幾個(gè)比較核心的概念就講解完了,不得不說(shuō)寫的真簡(jiǎn)潔,函數(shù)之間的依賴關(guān)系讓我一度十分懵逼,要理解它還是要用源碼來(lái)跑一遍例子, 一遍一遍地看。

總結(jié)一下redux就是創(chuàng)建一個(gè)store來(lái)管理所有狀態(tài),觸發(fā)action來(lái)改變store。關(guān)于redux的使用場(chǎng)景是非常靈活的,可以結(jié)合各種庫(kù)去用,我用慣了react,用的時(shí)候還要配合react-redux。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/6653.html

相關(guān)文章

  • 簡(jiǎn)單梳理Redux源碼運(yùn)行機(jī)制

    摘要:然后循環(huán)調(diào)用中的更新函數(shù),更新函數(shù)一般是我們的渲染函數(shù),函數(shù)內(nèi)部會(huì)調(diào)用來(lái)獲取數(shù)據(jù),所以頁(yè)面會(huì)更新。 歡迎訪問(wèn)個(gè)人網(wǎng)站:https://www.neroht.com/ 前言 前幾天寫了一篇react另一個(gè)狀態(tài)管理工具Unstated的源碼解析。開(kāi)啟了我的看源碼之路。想一想用了好長(zhǎng)時(shí)間的redux,但從沒(méi)有深究過(guò)原理,遇到報(bào)錯(cuò)更是懵逼,所以就啃了一遍它的源碼,寫了這篇文章,分享我對(duì)于它的理...

    betacat 評(píng)論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來(lái)我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開(kāi)啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開(kāi)緩存網(wǎng)頁(yè)性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來(lái)我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開(kāi)啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫法...

    dailybird 評(píng)論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來(lái)我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開(kāi)啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開(kāi)緩存網(wǎng)頁(yè)性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來(lái)我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開(kāi)啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫法...

    hellowoody 評(píng)論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來(lái)我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開(kāi)啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開(kāi)緩存網(wǎng)頁(yè)性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來(lái)我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開(kāi)啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫法...

    wwolf 評(píng)論0 收藏0
  • Redux源碼分析

    摘要:在得到新的狀態(tài)后,依次調(diào)用所有的監(jiān)聽(tīng)器,通知狀態(tài)的變更。執(zhí)行完后,獲得數(shù)組,它保存的對(duì)象是第二個(gè)箭頭函數(shù)返回的匿名函數(shù)。部分源碼利用這個(gè)屬性,所有子組件均可以拿到這個(gè)屬性。 Redux使用中的幾個(gè)點(diǎn): Redux三大設(shè)計(jì)原則 Create Store Redux middleware combineReducer Provider與Connect Redux流程梳理 Redux設(shè)計(jì)特...

    renweihub 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<