亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

理解redux

piapia / 1851人閱讀

摘要:假如說是士兵的話那么就是將軍,來管理士兵的狀態(tài)。對于來說,也就是一個最外層的容器,全部作為該容器組件的類似于,這樣一來發(fā)生變化,就會重新渲染整個頁面,從而達到更新的效果。得益于,每次并不用更新整個樹。規(guī)定,對外不暴露任何修改數(shù)據(jù)的接口。

redux 為什么引入redux

react來說,state可以包含內(nèi)部的狀態(tài)以及業(yè)務數(shù)據(jù),對于一個復雜的應用來說,state非常難以管理,一個model的變化可能引起另一個model的變化...依次下去,造成了難以維護的情況,別人很難一下子摸透你的state到底是怎么變得?所以需要引入一個東西來做數(shù)據(jù)管理,使數(shù)據(jù)變化變得清晰,redux做的就是這件事情。假如說react是士兵的話那么redux就是將軍,來管理士兵的狀態(tài)。

Flux與redux Flux

Flux是facebook提出的一種架構思想,與react一樣,強調(diào)的是單向數(shù)據(jù)流,由三大部分組成:

store來保存數(shù)據(jù),一個數(shù)據(jù)對應一個store,每一個store有一個監(jiān)聽器

dispatcher,含有兩個方法:

register,注冊事件

dispatch,分發(fā)一個action使store變化

view,與flux搭配不僅僅是react還可以是vue等,為當前頁面數(shù)據(jù)的一個展現(xiàn),與數(shù)據(jù)保持一致

flux并不是一個mvc框架,flux沒有明確的contoller,但是卻有著一個controller-view,類似于mvvm中的vmview=vm(model))。對于react來說,也就是一個最外層的容器store全部作為該容器組件的state(類似于),這樣一來state發(fā)生變化,就會重新渲染整個頁面,從而達到更新view的效果。得益于virtual dom,每次并不用更新整個dom樹。(每次觸發(fā)重繪會先在內(nèi)存中對新的dom樹和老的dom樹進行diff,把變化放入到patches中,最后統(tǒng)一更改)。

flux規(guī)定storestore對外不暴露任何修改數(shù)據(jù)的接口。

redux

facebook提供的flux包基本只有一個dispatcher的實現(xiàn),意味著我們需要為對每一個store進行一次定義并且創(chuàng)建一個dispatcher實例,需要register一次,dispatch時也需要區(qū)分不同的store(聽著就麻煩)?;?b>flux思想的redux為我們做了簡化。

redux與Flux的區(qū)別

redux提倡的是單一數(shù)據(jù)源,也就是一個應用就有一個store,包含著許多的reducer,reducer根據(jù)傳入的action來決定怎么改變當前狀態(tài)。關于redux,大家可以直接去看文檔,說的很清楚詳細,下面來看一下redux的源碼:

入口

index.jsredux的入口文件,暴露出來了redux所提供的API

export {
  createStore,           // 創(chuàng)建store
  combineReducers,       // 合并reducer
  bindActionCreators,   
  applyMiddleware,
  compose
}

其中還有一個isCrushed函數(shù),其作用就是做環(huán)境的檢測,假如在非生產(chǎn)環(huán)境中使用了壓縮的redux,則提出警告,判斷方式也很簡單:

isCrushed.name !== "isCrushed"  // 壓縮后函數(shù)名字會變短

下面來一個一個的看redux暴露出API的實現(xiàn):

compose

compose源自于函數(shù)式編程,redux將其用于用于store的增強,將傳入的函數(shù)從右到左的執(zhí)行,避免了層層嵌套,舉個例子:

const x = 10,
    add = x => x + 10,
    sub = x => x - 20,
    sup = x => x * 10;
// 原生方式嵌套實現(xiàn)
add(
    sup(
        sub(
            add(x)
        )
    )
);
// 利用compose改進
const fn = compose(add, sup, sub, add);
fn(x);

對比上面代碼,利用compose會使代碼變得清晰,compose就是函數(shù)式編程中的組合:

export default function compose(...funcs) {
    if (funcs.length === 0) {
        return arg => arg;
    }
    if (funcs.length === 1) {
        return funcs[0];
    }        
    return funcs.reduce(
        (a, b) => (
            (...args) => a(b(...args))
        )
    );
};
createStore 什么是store

redux強調(diào)的是單一數(shù)據(jù)源,把所有的state放入到一棵object tree中,這棵樹只能唯一的存在于一個store中,也就是說redux強調(diào)整個應用只有一個store。store可以包含

解析依賴函數(shù)

該模塊依賴了lodashisPlainObject,該函數(shù)用來判斷對象是否繼承了自定義類,實現(xiàn)起來非常簡單:

function isPlainObject(val) {
    // 非對象的情況直接返回false
    if (!isObjectLike(value) || baseGetTag(value) != "[object Object]") {
        return false
      }
      const proto = Object.getPrototypeOf(value)
      // 針對Object.create(null)創(chuàng)建出來的對象
      if (proto === null) {
        return true
      }
      const Ctor = hasOwnProperty.call(proto, "constructor") && proto.constructor
      // prototype.constructor === Object
      return typeof Ctor == "function" && Ctor instanceof Ctor &&
        funcToString.call(Ctor) == objectCtorString
}    
主體函數(shù)
// 內(nèi)部的action,用于reset
export const ActionTypes = {
  INIT: "@@redux/INIT"
};

/*
 * 創(chuàng)建store
 * reducer        reducer函數(shù)
 * preloadedState 初始狀態(tài)
 * enhancer       增強函數(shù),對createStoren能力進行增強,如devtools
 */
export default function createStore(reducer, preloadedState, enhancer) {
    // 參數(shù)修正
    if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
        enhancer = preloadedState;
        preloadedState = undefined;
    }
    if (typeof enhancer !== "undefined") {
        if (typeof enhancer !== "function") {
            throw new Error("Expected the enhancer to be a function.");
        }
        // 返回已經(jīng)增強后的store
        return enhancer(createStore)(reducer, preloadedState);
    }
    if (typeof reducer !== "function") {
        throw new Error("Expected the reducer to be a function.");
    }
    // 記錄當前值
    let currentReducer = reducer;
    let currentState = preloadedState;
    // 監(jiān)聽store變化的監(jiān)聽器(一些回調(diào)函數(shù))
    let currentListeners = [];
    let nextListeners = currentListeners;
    // 是否處于dispatch的過程中
    let isDispatching = false;

    /**** 看下面文字解釋 ****/
    function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
            nextListeners = currentListeners.slice();
        }
    }
    // 給store新增一個監(jiān)聽器
    function subscribe(listener) {
        if (typeof listener !== "function") {
            throw new Error("Expected listener to be a function.");
        }
        let isSubscribed = true;

        ensureCanMutateNextListeners();
        nextListeners.push(listener);
        // 返回一個卸載方法
        return function unsubscribe() {
            if (!isSubscribed) {
                return
            }
            isSubscribed = false;

            ensureCanMutateNextListeners();
            const index = nextListeners.index(listener);
            nextListeners.splice(index, 1);
        };
    }

    // 返回當前的狀態(tài)
    function getState() {
        return currentState;
    }

    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?"
              )
        }

        if (isDispatching) {
              throw new Error("Reducers may not dispatch actions.")
        }

        try {
            isDispatching = true;
            // 根據(jù)recuder來更新當前狀態(tài)
            currentState = currentReducer(currentState, action);
        } finally {
            isDispatching = false;
        }

        const listeners = currentListeners = nextListeners;
        
        // 發(fā)布事件
        for (let i = 0; i < listeners.length; i++) {
            const listener = listeners;
            listener();
        }
        return action;
    }
    // 更新當前reducer,重置store
    function replaceReducer(nextReducer) {
        if (typeof nextReducer !== "function") {
            throw new Error("Expected the nextReducer to be a function.");
        }

        currentReducer = nextReducer;
        dispatch({ type: ActionTypes.INIT });
    }

    // 初始化store
    dispatch({ type: ActionTypes.INIT });

    // 與Rxjs這種交互,根本看不懂--
    function observable() { ... }
    return {
        getState,
        subscribe,
        dispatch,
        replaceReducer,
        [$$observable]: observable
    };    
};

需要注意的有以下幾點:

為什么需要兩個數(shù)組來保存監(jiān)聽函數(shù)

觀察源碼可以發(fā)現(xiàn),currentListenersnextListeners存儲的都是監(jiān)聽函數(shù),這樣做的目的是保證dispatch的過程不發(fā)生錯誤,加入使用一個隊列的話,當執(zhí)行過程中有監(jiān)聽函數(shù)被移除時,則會發(fā)生錯誤,如:當前監(jiān)聽函數(shù)隊列為:[a, b, c],當執(zhí)行回調(diào)函數(shù)a后將其移除,則隊列發(fā)生改變[b, c],而利用for循環(huán)來執(zhí)行回調(diào),執(zhí)行到i = 2時會拋出錯誤。

為什么不用forEach

forEachfor差在:

ie8不兼容forEach (ie8 is out)

forEach不能提前終止,但是在dispatch中無此問題

性能,這是forEach最大的問題for要比forEach快的多的多(差不多一倍左右),查看v8代碼也可以感覺出,forEach本質(zhì)上做的還是for循環(huán),每次loop進行一次判斷和函數(shù)調(diào)用,自然速度會慢。

applyMiddleware

中間件,expresskoa也就中間件,express中的中間件處理的請求到響應的這一過程,redux中的中間件處理的是從action發(fā)出到reducer接收到action這一過程,在這個過程中可以利用中間件進行寫日志,處理異步操作等過程,作用就是來增強createStore,給其添加更多的功能。先來看下下面這個簡化的例子:

const calc = (obj) => obj.value + 20;

// 簡單的模擬下stroe
const obj = { calc };

// 加強fn函數(shù)
function applyMiddleware(fn, middlewares) {
    middlewares = middlewares.slice();
    middlewares.reverse();
    // 每次改變fn,一次擴展一個功能
    let { calc } = obj;
    middlewares.forEach(
        middleware => calc = middleware(obj)(calc)
    );
    return calc;
}

// arrow function is cool!!!
const logger = obj => next => num => {
    console.log(`num is ${num.value}`);
    let result = next(num);
    console.log(`finish calc, result is ${result}`);
    return result;
};

const check = obj => next => num => {
    console.log(`typeof num.value is ${typeof num.value}`);
    let result = next(num);
    return result;
};

const fn = applyMiddleware(obj, [check, logger]);

fn({ value: 50 });

在上面簡單的例子中為calc做了兩個功能擴展,執(zhí)行起來就是一個高階函數(shù)check->logger->calc,理解了這個再來看看reduxapplyMiddleware的實現(xiàn):

export default function applyMiddleware(...middlewares) {
    // 利用閉包保存下來了middlewares
    return (createStore) => (reducer, preloadadState, enhancer) => {
        const store = createStore(reducer, preloadadState, enhancer);
        let dispatch = store.dispatch;
        let chain = [];

        // middleware不會改變store,利用閉包保存
        const middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action)
        };

        // chain中的元素仍未函數(shù)
        // 接受的參數(shù)為`next`    =>     下一個中間件
        chain = middlewares.map(middleware => middleware(middlewareAPI)); 

        // 本身的dispatch放在最后執(zhí)行
        // dispatch仍未函數(shù),接受的參數(shù)為`action`
        // 返回的是一個高階函數(shù),需要注意的是中間件并不會改變原本的action
        // dispatch變成了一個升級版
        dispatch = compose(...chain)(store.dispatch);

        return {
            ...store,
            dispatch
        };                                                                                                                                                                                                                                     
    };
};

附一張圖:

applyMiddleware代碼雖少,但是卻是最難理解的部分

redux-thunk

redux-thunk是一個十分剪短有用的中間件,尤其是在異步應用中,利用redux-thunk可以使action變?yōu)橐粋€函數(shù),而不僅僅是一個對象,并且在該函數(shù)中仍然可以觸發(fā)dispatch

// 讓action可以成為函數(shù)
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    // action為函數(shù)類型,執(zhí)行action,dispatch結束
    if (typeof action === "function") {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}
combineReducers

combineReducers用來合并多個reducer

assertReducerSanity來驗證reducer

利用createStore文件中定義的ActionTypes來進行初始化state,也就是定義的reducer會在一開始執(zhí)行一遍,而后對得到state進行檢測,為undefined拋出異常,同時利用隨機字符串測試,防止其處理type@@redux/INIT而未處理default的情況。

combineReducers

將我們所寫的reducer進行合并,返回一個函數(shù),每次dispatch時,執(zhí)行函數(shù),遍歷所有的reducer,計算出最終的state

export default function combineReducers(reducers) {
    const reducerKeys = Object.keys(reducers)
    const finalReducers = {}     // 有效reducer集合
    // 驗證reducer,必須是純函數(shù)才有效
    for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]

        if (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 (NODE_ENV !== "production") {
        unexpectedKeyCache = {}
    }

    let sanityError
    try {
        // 檢測reducer
        assertReducerSanity(finalReducers)
    } catch (e) {
        sanityError = e
    }

    return function combination(state = {}, action) {
        if (sanityError) {
            throw sanityError
        }
        // getUnexpectedStateShapeWarningMessage
        // 檢測state類型是否為0繼承對象
        // 用于找出多余的redcuer并給出警告
        if (NODE_ENV !== "production") {
            const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
            if (warningMessage) {
                warning(warningMessage)
            }
        }

        let hasChanged = false
        const nextState = {}
        // 每次發(fā)出一個action會遍歷所有的reducer
        for (let i = 0; i < finalReducerKeys.length; i++) {
            const key = finalReducerKeys[i]
            // 保留之前的state與新生成的state做對比
            const reducer = finalReducers[key]
            const previousStateForKey = state[key]
            const next     StateForKey = reducer(previousStateForKey, action)
            // state為undefined拋出異常
            if (typeof nextStateForKey === "undefined") {
                const errorMessage = getUndefinedStateErrorMessage(key, action)
                throw new Error(errorMessage)
            }
            nextState[key] = nextStateForKey
            // 比較是否數(shù)據(jù)發(fā)生變化
            hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        // 未發(fā)生變化返回之前的數(shù)據(jù)
        return hasChanged ? nextState : state
    }
}
bindActionCreators

這個基本上的作用不大,唯一運用的情況就是將其作為props傳入子組件,對于子組件來說可以全然不知redux的存在。如果利用UI庫的組件來操作頁面狀態(tài),bindActionCreators是一個很好的選擇,實現(xiàn)很簡單:

function bindActionCreator(actionCreator, dispatch) {
    return (...args) => dispatch(actionCreator(...args))
}

bindActionCreators僅僅是遍歷所有的actions返回一個鍵值對。

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

轉載請注明本文地址:http://www.ezyhdfw.cn/yun/81465.html

相關文章

  • 深入理解redux

    摘要:深入簡述在快速理解中,我簡單的介紹了的基礎內(nèi)容,本篇文章中,我們將再度深入。二修改我曾在快速理解中提起,為了解決模塊組件之間需要共享數(shù)據(jù)和數(shù)據(jù)可能被任意修改導致不可預料的結果的矛盾,團隊創(chuàng)建了。 深入Redux 簡述 在快速理解redux中,我簡單的介紹了redux的基礎內(nèi)容,本篇文章中,我們將再度深入redux。 redux解決的問題 數(shù)據(jù)和函數(shù)的層層傳遞 多個組件同時修改某全局變...

    pekonchan 評論0 收藏0
  • redux和react-redux理解和總結(一)

    摘要:使得在變化和異步中可預測。它是數(shù)據(jù)的唯一來源。指定了應用狀態(tài)的變化如何響應并發(fā)送到的,只是描述了有事情發(fā)生了這一事實,并沒有描述應用如何更新。更新的函數(shù)稱為,它是一個純函數(shù),接受舊的和,返回新的。是和之間的橋梁,是把它們聯(lián)系到一起的對象。 為什么使用redux 隨著前端單頁面開發(fā)越來越復雜,javascript需要管理越來越多的狀態(tài)state。如果一個model的變化引起另一個mode...

    skinner 評論0 收藏0
  • 理解 Redux

    摘要:我們知道狀態(tài)機是表示有限個狀態(tài)以及在這些狀態(tài)之間的轉移和動作等行為的數(shù)學模型。對照上面我們自己寫的狀態(tài)機代碼可以看出的作用告訴狀態(tài)樹發(fā)生什么變化,及所需要的數(shù)據(jù)是什么。 前言 我之前開發(fā)網(wǎng)站的時候一直用的是 Flux, 自從出了 Redux 之后由于種種原因沒有跟進了解,最近手頭上的事情基本忙的差不多了,抽空閱讀了 Redux 的源碼,并整理了這篇博文。 先說重點: Redux 與 R...

    leejan97 評論0 收藏0
  • Redux之旅-1

    摘要:我們約定,內(nèi)使用一個字符串類型的字段來表示將要執(zhí)行的動作。多數(shù)情況下,會被定義成字符串常量。要進去工廠加工產(chǎn)品,就得帶上相應得鑰匙,不同的鑰匙對應工廠中上不同的生產(chǎn)線里面的函數(shù),從而有不同的產(chǎn)出改變之后的 時間:2016.4.7-17:24作者:三月懶驢入門配置文章:鏈接 準備 在你的項目下面加入redux / react-redux npm install redux --s...

    hiyang 評論0 收藏0
  • Redux原理分析

    摘要:調(diào)用鏈中最后一個會接受真實的的方法作為參數(shù),并借此結束調(diào)用鏈??偨Y我們常用的一般是除了和之外的方法,那個理解明白了,對于以后出現(xiàn)的問題會有很大幫助,本文只是針對最基礎的進行解析,之后有機會繼續(xù)解析對他的封裝 前言 雖然一直使用redux+react-redux,但是并沒有真正去講redux最基礎的部分理解透徹,我覺得理解明白redux會對react-redux有一個透徹的理解。 其實,...

    sumory 評論0 收藏0
  • 精讀《重新思考 Redux

    摘要:本周精讀內(nèi)容是重新思考。數(shù)據(jù)流對數(shù)據(jù)緩存,性能優(yōu)化,開發(fā)體驗優(yōu)化都有進一步施展的空間,擁抱插件生態(tài)是一個良好的發(fā)展方向。 本周精讀內(nèi)容是 《重新思考 Redux》。 1 引言 《重新思考 Redux》是 rematch 作者 Shawn McKay 寫的一篇干貨軟文。 dva 之后,有許多基于 redux 的狀態(tài)管理框架,但大部分都很局限,甚至是倒退。但直到看到了 rematch,總算...

    IntMain 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<