国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

React學(xué)習(xí)之深入Redux應(yīng)用框架

張漢慶 / 1150人閱讀

摘要:作為大型應(yīng)用狀態(tài)管理最常用的工具。它是一個(gè)應(yīng)用數(shù)據(jù)流框架,與框架類(lèi)似。這是觸發(fā)變化的惟一途徑。在這個(gè)函數(shù)內(nèi)部,被調(diào)用,其作用是監(jiān)測(cè)是的。否則的話(huà),認(rèn)為只是一個(gè)普通的,將通過(guò)也就是進(jìn)一步分發(fā)。到此源碼的主要部分學(xué)習(xí)結(jié)束。

Redux作為大型React應(yīng)用狀態(tài)管理最常用的工具。它是一個(gè)應(yīng)用數(shù)據(jù)流框架,與Flux框架類(lèi)似。它是零依賴(lài)的,可以配合其他框架或者類(lèi)庫(kù)一起使用。雖然在平時(shí)的工作中很多次的用到了它,但是一直沒(méi)有對(duì)其原理進(jìn)行研究。最近看了一下源碼,下面是我自己的一些簡(jiǎn)單認(rèn)識(shí)。

createStore

結(jié)合使用場(chǎng)景我們首先來(lái)看一下createStore方法。

// 這是我們平常使用時(shí)創(chuàng)建store
const store = createStore(reducers, state, enhance);

以下源碼為去除異常校驗(yàn)后的源碼,

export default function createStore(reducer, preloadedState, enhancer) {

// 如果有傳入合法的enhance,則通過(guò)enhancer再調(diào)用一次createStore
 if (typeof enhancer !== "undefined") {
   if (typeof enhancer !== "function") {
     throw new Error("Expected the enhancer to be a function.")
   }
   return enhancer(createStore)(reducer, preloadedState) // 這里涉及到中間件,后面介紹applyMiddleware時(shí)在具體介紹
 }

 let currentReducer = reducer     //把 reducer 賦值給 currentReducer
 let currentState = preloadedState   //把 preloadedState 賦值給 currentState
 let currentListeners = []     //初始化監(jiān)聽(tīng)函數(shù)列表
 let nextListeners = currentListeners   //監(jiān)聽(tīng)列表的一個(gè)引用
 let isDispatching = false  //是否正在dispatch

 function ensureCanMutateNextListeners() {}

 function getState() {}

 function subscribe(listener) {}
 
 function dispatch(action) {}

 function replaceReducer(nextReducer) {}
 
// 在 creatorStore 內(nèi)部沒(méi)有看到此方法的調(diào)用,就不講了
 function observable() {}
 //初始化 store 里的 state tree
 dispatch({ type: ActionTypes.INIT })

 return {
   dispatch,
   subscribe,
   getState,
   replaceReducer,
   [$$observable]: observable
 }
}

我們可以看到creatorStore方法除了返回我們常用的方法外,還做了一次初始化過(guò)程dispatch({ type: ActionTypes.INIT });那么dispatch干了什么事情呢?

/**
  * dispath action。這是觸發(fā) state 變化的惟一途徑。
  * @param {Object} 一個(gè)普通(plain)的對(duì)象,對(duì)象當(dāng)中必須有 type 屬性
  * @returns {Object} 返回 dispatch 的 action
  */
 function dispatch(action) {
 // 判斷 dispahch 正在運(yùn)行,Reducer在處理的時(shí)候又要執(zhí)行 dispatch
   if (isDispatching) {
     throw new Error("Reducers may not dispatch actions.")
   }

   try {
     //標(biāo)記 dispatch 正在運(yùn)行
     isDispatching = true
     //執(zhí)行當(dāng)前 Reducer 函數(shù)返回新的 state
     currentState = currentReducer(currentState, action)
   } finally {
     isDispatching = false
   }
   const listeners = (currentListeners = nextListeners)
   //遍歷所有的監(jiān)聽(tīng)函數(shù)
   for (let i = 0; i < listeners.length; i++) {
     const listener = listeners[i]
     listener() // 執(zhí)行每一個(gè)監(jiān)聽(tīng)函數(shù)
   }
   return action
 }

這里dispatch主要做了二件事情

通過(guò)reducer更新state

執(zhí)行所有的監(jiān)聽(tīng)函數(shù),通知狀態(tài)的變更

那么reducer是怎么改變state的呢?這就涉及到下面的combineReducers了。

combineReducers

回到上面創(chuàng)建store的參數(shù)reducers,

// 兩個(gè)reducer
const todos = (state = INIT.todos, action) => {
  // ....
};
const filterStatus = (state = INIT.filterStatus, action) => {
  // ...
};

const reducers = combineReducers({
  todos,
  filterStatus
});
// 這是我們平常使用時(shí)創(chuàng)建store
const store = createStore(reducers, state, enhance);

下面我們來(lái)看combineReducers做了什么

   // 第一次篩選,參數(shù)reducers為Object
  // 篩選掉reducers中不是function的鍵值對(duì)
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    if (typeof reducers[key] === "function") {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)
  
  // 二次篩選,判斷reducer中傳入的值是否合法(!== undefined)
  // 獲取篩選完之后的所有key
  let shapeAssertionError
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  return function combination(state = {}, action) {
    let hasChanged = false
    const nextState = {}
    // 遍歷所有的key和reducer,分別將reducer對(duì)應(yīng)的key所代表的state,代入到reducer中進(jìn)行函數(shù)調(diào)用
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      // 這里就是reducer function的名稱(chēng)和要和state同名的原因,傳說(shuō)中的黑魔法
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === "undefined") {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      // 將reducer返回的值填入nextState
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 發(fā)生改變了返回新的nextState,否則返回原先的state
    return hasChanged ? nextState : state
  }
}

這里 reducer(previousStateForKey, action)執(zhí)行的就是我們上面定義的todos和filterStatus方法。通過(guò)這二個(gè)reducer改變state值。

以前我一直很奇怪我們的reducer里的state是怎么做到取當(dāng)前reducer對(duì)應(yīng)的數(shù)據(jù)。看到const previousStateForKey = state[key]這里我就明白了。

這里還有一個(gè)疑問(wèn)點(diǎn)就是combineReducers的嵌套,最開(kāi)始也我不明白,看了源碼才知道combineReducers()=> combination(state = {}, action),這里combineReducers返回的combination也是接受(state = {}, action)也就是一個(gè)reducer所以可以正常嵌套。

看到這初始化流程已經(jīng)走完了。這個(gè)過(guò)程我們認(rèn)識(shí)了dispatch和combineReducers;接下來(lái)我們來(lái)看一下我們自己要怎么更新數(shù)據(jù)。

用戶(hù)更新數(shù)據(jù)時(shí),是通過(guò)createStore后暴露出來(lái)的dispatch方法來(lái)觸發(fā)的。dispatch 方法,是 store 對(duì)象提供的更改 currentState 這個(gè)閉包變量的唯一建議途徑(注意這里是唯一建議途徑,不是唯一途徑,因?yàn)橥ㄟ^(guò)getState獲取到的是state的引用,所以是可以直接修改的。但是這樣就不能更新視圖了)。
正常情況下我們只需要像下面這樣

//action creator
var addTodo = function(text){
    return {
        type: "add_todo",
        text: text
    };
};
function TodoReducer(state = [], action){
    switch (action.type) {
        case "add_todo":
            return state.concat(action.text);
        default:
            return state;
    }
};

// 通過(guò) store.dispatch(action) 來(lái)達(dá)到修改 state 的目的
// 注意: 在redux里,唯一能夠修改state的方法,就是通過(guò) store.dispatch(action)
store.dispatch({type: "add_todo", text: "讀書(shū)"});// 或者下面這樣
// store.dispatch(addTodo("讀書(shū)"));

也就是說(shuō)dispatch接受一個(gè)包含type的對(duì)象。框架為我們提供了一個(gè)創(chuàng)建Action的方法bindActionCreators。

bindActionCreators

下面來(lái)看下源碼

// 核心代碼,并通過(guò)apply將this綁定起來(lái)
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
// 如果actionCreators是一個(gè)函數(shù),則說(shuō)明只有一個(gè)actionCreator,就直接調(diào)用bindActionCreator
  if (typeof actionCreators === "function") {
    return bindActionCreator(actionCreators, dispatch)
  }
  // 遍歷對(duì)象,然后對(duì)每個(gè)遍歷項(xiàng)的 actionCreator 生成函數(shù),將函數(shù)按照原來(lái)的 key 值放到一個(gè)對(duì)象中,最后返回這個(gè)對(duì)象
  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === "function") {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

bindActionCreators的作用就是使用dispatch把a(bǔ)ction creator包裹起來(lái),這樣我們就可以直接調(diào)用他們了。這個(gè)在平常開(kāi)發(fā)中不常用。

applyMiddleware

最后我們回頭來(lái)看一下之前調(diào)到的中間件,

import thunkMiddleware from "redux-thunk";
// 兩個(gè)reducer
const todos = (state = INIT.todos, action) => {
  // ....
};
const filterStatus = (state = INIT.filterStatus, action) => {
  // ...
};

const reducers = combineReducers({
  todos,
  filterStatus
});
// 這是我們平常使用時(shí)創(chuàng)建store
const store = createStore(reducers, state, applyMiddleware(thunkMiddleware));

為了下文好理解這個(gè)放一下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();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

可以看出thunk返回了一個(gè)接受({ dispatch, getState })為參數(shù)的函數(shù)
下面我們來(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)
    }
    // 每個(gè) middleware 都以 middlewareAPI 作為參數(shù)進(jìn)行注入,返回一個(gè)新的鏈。此時(shí)的返回值相當(dāng)于調(diào)用 thunkMiddleware 返回的函數(shù): (next) => (action) => {} ,接收一個(gè)next作為其參數(shù)
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 并將鏈代入進(jìn) compose 組成一個(gè)函數(shù)的調(diào)用鏈
    // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函數(shù)對(duì)象。
    // 在目前只有 thunkMiddleware 作為 middlewares 參數(shù)的情況下,將返回 (next) => (action) => {}
    // 之后以 store.dispatch 作為參數(shù)進(jìn)行注入注意這里這里的store.dispatch是沒(méi)有被修改的dispatch他被傳給了next;
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

// 定義一個(gè)代碼組合的方法
// 傳入一些function作為參數(shù),返回其鏈?zhǔn)秸{(diào)用的形態(tài)。例如,
// compose(f, g, h) 最終返回 (...args) => f(g(h(...args)))
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  } else {
    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
  }
}

也就是一個(gè)三級(jí)柯里化的函數(shù),我們從頭來(lái)分析一下這個(gè)過(guò)程

// createStore.js
if (typeof enhancer !== "undefined") {
  if (typeof enhancer !== "function") {
    throw new Error("Expected the enhancer to be a function.")
  }
  return enhancer(createStore)(reducer, preloadedState)
}

也就是說(shuō),會(huì)變成這樣

applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)

applyMiddleware(thunkMiddleware)

applyMiddleware接收thunkMiddleware作為參數(shù),返回形如(createStore) => (...args) => {}的函數(shù)。

applyMiddleware(thunkMiddleware)(createStore)

以 createStore 作為參數(shù),調(diào)用上一步返回的函數(shù)(...args) => {}

applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)

以(reducer, preloadedState)為參數(shù)進(jìn)行調(diào)用。 在這個(gè)函數(shù)內(nèi)部,thunkMiddleware被調(diào)用,其作用是監(jiān)測(cè)type是function的action。
因此,如果dispatch的action返回的是一個(gè)function,則證明是中間件,則將(dispatch, getState)作為參數(shù)代入其中,進(jìn)行action 內(nèi)部下一步的操作。否則的話(huà),認(rèn)為只是一個(gè)普通的action,將通過(guò)next(也就是dispatch)進(jìn)一步分發(fā)。

也就是說(shuō),applyMiddleware(thunkMiddleware)作為enhance,最終起了這樣的作用:

對(duì)dispatch調(diào)用的action進(jìn)行檢查,如果action在第一次調(diào)用之后返回的是function,則將(dispatch, getState)作為參數(shù)注入到action返回的方法中,否則就正常對(duì)action進(jìn)行分發(fā),這樣一來(lái)我們的中間件就完成了。

因此,當(dāng)action內(nèi)部需要獲取state,或者需要進(jìn)行異步操作,在操作完成之后進(jìn)行事件調(diào)用分發(fā)的話(huà),我們就可以讓action 返回一個(gè)以(dispatch, getState)為參數(shù)的function而不是通常的Object,enhance就會(huì)對(duì)其進(jìn)行檢測(cè)以便正確的處理。

到此redux源碼的主要部分學(xué)習(xí)結(jié)束。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/105568.html

相關(guān)文章

  • React學(xué)習(xí)之Redux高階運(yùn)用

    摘要:增強(qiáng)除了解決復(fù)用問(wèn)題,高階的另一個(gè)重要作用就是對(duì)原始的進(jìn)行增強(qiáng)。就是典型的利用高階來(lái)增強(qiáng)的例子,它主要作用是使任意變成可以執(zhí)行撤銷(xiāo)和重做的全新。 在Redux架構(gòu)中,reducer是一個(gè)純函數(shù),它的職責(zé)是根據(jù)previousState和action計(jì)算出新的state。在復(fù)雜應(yīng)用中,Redux提供的combineReducers讓我們可以把頂層的reducer拆分成多個(gè)小的reduce...

    supernavy 評(píng)論0 收藏0
  • GraphQL學(xué)習(xí)之實(shí)踐篇

    摘要:前言?xún)善恼聦W(xué)完了基礎(chǔ)篇原理篇,接下去便是實(shí)踐的過(guò)程,這個(gè)實(shí)踐我們使用了如下技術(shù)棧去實(shí)現(xiàn)一套任務(wù)管理系統(tǒng),源碼就不公開(kāi)了等穩(wěn)定后再發(fā)布。后續(xù)我所在的公司網(wǎng)關(guān)團(tuán)隊(duì)會(huì)持續(xù)實(shí)踐,爭(zhēng)取貢獻(xiàn)出更多的解決方案。前言 兩篇文章學(xué)完了GraphQL(基礎(chǔ)篇, 原理篇),接下去便是實(shí)踐的過(guò)程,這個(gè)實(shí)踐我們使用了如下技術(shù)棧去實(shí)現(xiàn)一套任務(wù)管理系統(tǒng),源碼就不公開(kāi)了, 等穩(wěn)定后再發(fā)布。效果如下: showImg(ht...

    Drinkey 評(píng)論0 收藏0
  • 【搶先領(lǐng)】《React 學(xué)習(xí)之道》我們翻譯了一本最簡(jiǎn)單,且最實(shí)用的 React 實(shí)戰(zhàn)教程……

    摘要:學(xué)習(xí)之道簡(jiǎn)體中文版通往實(shí)戰(zhàn)大師之旅掌握最簡(jiǎn)單,且最實(shí)用的教程。前言學(xué)習(xí)之道這本書(shū)使用路線(xiàn)圖中的精華部分用于傳授,并將其融入一個(gè)獨(dú)具吸引力的真實(shí)世界的具體代碼實(shí)現(xiàn)。完美展現(xiàn)了的優(yōu)雅。膜拜的學(xué)習(xí)之道是必讀的一本書(shū)。 《React 學(xué)習(xí)之道》The Road to learn React (簡(jiǎn)體中文版) 通往 React 實(shí)戰(zhàn)大師之旅:掌握 React 最簡(jiǎn)單,且最實(shí)用的教程。 showIm...

    oneasp 評(píng)論0 收藏0
  • 前端學(xué)習(xí)之JS框架的使用

    摘要:目前,有三個(gè)明確的框架可供選擇。和在眾多開(kāi)源框架中贏得了開(kāi)發(fā)人員和公司的信任。雖然這三個(gè)框架有許多共同之處,但它們的受歡迎程度因行業(yè)而異。使用,這有助于在編碼時(shí)發(fā)現(xiàn)并糾正常見(jiàn)錯(cuò)誤。 人們首先注意到的是你的應(yīng)用程序的視覺(jué)吸引力。大多數(shù)用戶(hù)傾向于將界面設(shè)計(jì)與公司的信譽(yù)和專(zhuān)業(yè)能力聯(lián)系起來(lái)。這就是為什么選擇正確的前端技術(shù)對(duì)你的業(yè)務(wù)...

    不知名網(wǎng)友 評(píng)論0 收藏0
  • 專(zhuān)治前端焦慮的學(xué)習(xí)方案

    摘要:不過(guò)今天我希望能夠更進(jìn)一步,不僅僅再抱怨現(xiàn)狀,而是從我個(gè)人的角度來(lái)給出一個(gè)逐步深入學(xué)習(xí)生態(tài)圈的方案。最后,我還是想提到下對(duì)于的好的學(xué)習(xí)方法就是回顧參照各種各樣的代碼庫(kù),學(xué)習(xí)人家的用法與實(shí)踐。 本文翻譯自A-Study-Plan-To-Cure-JavaScript-Fatigue。筆者看到里面的幾張配圖著實(shí)漂亮,順手翻譯了一波。本文從屬于筆者的Web Frontend Introduc...

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

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

0條評(píng)論

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