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

資訊專欄INFORMATION COLUMN

理解redux

piapia / 1756人閱讀

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

redux 為什么引入redux

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

Flux與redux Flux

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

store來保存數(shù)據(jù),一個(gè)數(shù)據(jù)對(duì)應(yīng)一個(gè)store,每一個(gè)store有一個(gè)監(jiān)聽器

dispatcher,含有兩個(gè)方法:

register,注冊(cè)事件

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

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

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

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

redux

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

redux與Flux的區(qū)別

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

入口

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

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

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

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

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

compose

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

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

對(duì)比上面代碼,利用compose會(huì)使代碼變得清晰,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強(qiáng)調(diào)的是單一數(shù)據(jù)源,把所有的state放入到一棵object tree中,這棵樹只能唯一的存在于一個(gè)store中,也就是說redux強(qiáng)調(diào)整個(gè)應(yīng)用只有一個(gè)storestore可以包含

解析依賴函數(shù)

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

function isPlainObject(val) {
    // 非對(duì)象的情況直接返回false
    if (!isObjectLike(value) || baseGetTag(value) != "[object Object]") {
        return false
      }
      const proto = Object.getPrototypeOf(value)
      // 針對(duì)Object.create(null)創(chuàng)建出來的對(duì)象
      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       增強(qiáng)函數(shù),對(duì)createStoren能力進(jìn)行增強(qiáng),如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)增強(qiáng)后的store
        return enhancer(createStore)(reducer, preloadedState);
    }
    if (typeof reducer !== "function") {
        throw new Error("Expected the reducer to be a function.");
    }
    // 記錄當(dāng)前值
    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新增一個(gè)監(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);
        // 返回一個(gè)卸載方法
        return function unsubscribe() {
            if (!isSubscribed) {
                return
            }
            isSubscribed = false;

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

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

    function dispatch(action) {
        // 一些類型檢測(cè)
        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來更新當(dāng)前狀態(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;
    }
    // 更新當(dāng)前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
    };    
};

需要注意的有以下幾點(diǎn):

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

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

為什么不用forEach

forEachfor差在:

ie8不兼容forEach (ie8 is out)

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

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

applyMiddleware

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

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

// 簡(jiǎn)單的模擬下stroe
const obj = { calc };

// 加強(qiáng)fn函數(shù)
function applyMiddleware(fn, middlewares) {
    middlewares = middlewares.slice();
    middlewares.reverse();
    // 每次改變fn,一次擴(kuò)展一個(gè)功能
    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 });

在上面簡(jiǎn)單的例子中為calc做了兩個(gè)功能擴(kuò)展,執(zhí)行起來就是一個(gè)高階函數(shù)check->logger->calc,理解了這個(gè)再來看看reduxapplyMiddleware的實(shí)現(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不會(huì)改變store,利用閉包保存
        const middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action)
        };

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

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

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

附一張圖:

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

redux-thunk

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

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

    return next(action);
  };
}
combineReducers

combineReducers用來合并多個(gè)reducer

assertReducerSanity來驗(yàn)證reducer

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

combineReducers

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

export default function combineReducers(reducers) {
    const reducerKeys = Object.keys(reducers)
    const finalReducers = {}     // 有效reducer集合
    // 驗(yàn)證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 {
        // 檢測(cè)reducer
        assertReducerSanity(finalReducers)
    } catch (e) {
        sanityError = e
    }

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

        let hasChanged = false
        const nextState = {}
        // 每次發(fā)出一個(gè)action會(huì)遍歷所有的reducer
        for (let i = 0; i < finalReducerKeys.length; i++) {
            const key = finalReducerKeys[i]
            // 保留之前的state與新生成的state做對(duì)比
            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

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

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

bindActionCreators僅僅是遍歷所有的actions返回一個(gè)鍵值對(duì)。

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

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

相關(guān)文章

  • 深入理解redux

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

    pekonchan 評(píng)論0 收藏0
  • 對(duì)redux和react-redux理解和總結(jié)(一)

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

    skinner 評(píng)論0 收藏0
  • 理解 Redux

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

    leejan97 評(píng)論0 收藏0
  • Redux之旅-1

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

    hiyang 評(píng)論0 收藏0
  • Redux原理分析

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

    sumory 評(píng)論0 收藏0
  • 精讀《重新思考 Redux

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

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

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

0條評(píng)論

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