最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

next-redux-wrapper使用細節(jié)及源碼分析

 更新時間:2023年02月12日 14:40:12   作者:拾光者  
這篇文章主要為大家介紹了next-redux-wrapper使用細節(jié)及源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

前幾天寫了一篇文章講解了 wrapper 概念,本篇文章主要從源碼分析它的實現(xiàn)過程,去更好的理解服務端使用 redux 的注意事項。

先附上一份主要代碼結構圖:

目錄結構

.
├── lerna.json
├── package.json
├── packages
│   ├── configs
│   ├── demo
│   ├── demo-page
│   ├── demo-redux-toolkit
│   ├── demo-saga
│   ├── demo-saga-page
│   └── wrapper
└── yarn.lock

可以明顯的看出來這是一個用 lerna 管理的 monorepo 倉庫

demo-* 開頭的是案例庫,而configs 庫也是為案例庫服務的,那么只用管 wrapper 庫即可。

wrapper 結構:

packages/wrapper
├── jest.config.js jest 配置
├── next-env.d.ts
├── package.json
├── src
│   └── index.tsx 核心實現(xiàn)代碼
├── tests 測試代碼
│   ├── client.spec.tsx
│   ├── server.spec.tsx
│   └── testlib.tsx
├── tsconfig.es6.json
└── tsconfig.json

核心代碼都在 packages/wrapper/src/index.tsx文件,下面開始分析。

代碼結構

把大部分代碼的具體實現(xiàn)去掉,然后留下整體代碼的頂層聲明:

// 用于客戶端 hydrate 的時發(fā)送初始化指令用的 action key
export const HYDRATE = '__NEXT_REDUX_WRAPPER_HYDRATE__';
// 用于判斷是否是服務器
const getIsServer = () => typeof window === 'undefined';
// 用于反序列化 State,類似解密函數(shù)
const getDeserializedState = <S extends Store>(initialState: any, {deserializeState}: Config<S> = {}) =>
    deserializeState ? deserializeState(initialState) : initialState;
// 用于序列化 State,類似加密函數(shù)
const getSerializedState = <S extends Store>(state: any, {serializeState}: Config<S> = {}) =>
    serializeState ? serializeState(state) : state;
// 客戶端存儲 store 用的變量
let sharedClientStore: any;
// 初始化 redux store
const initStore = <S extends Store>({makeStore, context = {}}: InitStoreOptions<S>): S => {
// ...
};
// 創(chuàng)建 wrapper
export const createWrapper = <S extends Store>(makeStore: MakeStore<S>, config: Config<S> = {}) => {
    // ...
    return {
        getServerSideProps,
        getStaticProps,
        getInitialAppProps,
        getInitialPageProps,
        // 以前的函數(shù),忽略掉,不建議使用了
        withRedux,
        useWrappedStore,
    };
};
// 以前的函數(shù),忽略掉,不建議使用了
export default <S extends Store>(makeStore: MakeStore<S>, config: Config<S> = {}) => {
    // ...
    return createWrapper(makeStore, config).withRedux;
};

代碼分析:

  • HYDRATE,常量,用于客戶端 react 執(zhí)行 hydrate 的時發(fā)送初始化指令用的 action key。
  • getIsServer 用于判斷是否是服務器.
  • getDeserializedState 用于反序列化 State ,類似解密函數(shù),依賴外部使用方傳入。
  • getSerializedState 用于序列化 State ,類似加密函數(shù),依賴外部使用方傳入。
  • sharedClientStore 變量,用于客戶端存儲 redux store 用的,用在 initStore 中。
  • initStore 初始化 redux store ,主要是處理 服務端 和 客戶端 在創(chuàng)建時緩存的位置問題。
  • createWrapper 創(chuàng)建 wrapper 核心
  • default 默認導出函數(shù),以前的函數(shù),忽略掉,不建議使用了,主要使用 createWrapper 函數(shù)返回的 withRedux 函數(shù),因此本篇文章不進行講解 withRedux 函數(shù)。

一共就 8 個聲明,其中只暴露了 HYDRATE 、 createWrapper 和默認導出函數(shù),忽略掉默認導出函數(shù)。

下面進行對 createWrapper 函數(shù)的分析。

核心實現(xiàn)

createWrapper 整體實現(xiàn)概覽

// 創(chuàng)建 wrapper
export const createWrapper = <S extends Store>(makeStore: MakeStore<S>, config: Config<S> = {}) => {
    // 幾個 wrapper 函數(shù)的實際核心實現(xiàn)函數(shù)
    const makeProps = async (): Promise<WrapperProps> => {/** ... */};
    // 頁面級 getInitialProps 函數(shù)的包裹函數(shù)
    const getInitialPageProps = (callback) => async (context) => {/** ... */};
    // 應用級 getInitialProps 函數(shù)的包裹函數(shù)
    const getInitialAppProps = (callback) => async (context) => {/** ... */};
    // getStaticProps 函數(shù)的包裹函數(shù)
    const getStaticProps = (callback) => async (context) => {/** ... */};
    // getServerSideProps 函數(shù)的包裹函數(shù)
    const getServerSideProps = (callback) => async (context) => await getStaticProps(callback)(context);
    // hydrate 處理函數(shù),調用 store.dispatch 發(fā)起初始化 
    const hydrate = (store: S, state: any) => {
        if (!state) {
            return;
        }
        store.dispatch({
            type: HYDRATE,
            payload: getDeserializedState<S>(state, config),
        } as any);
    };
    // hydrate 處理中間數(shù)據(jù)的函數(shù)(用于分析處理各種情況)
    const hydrateOrchestrator = (store: S, giapState: any, gspState: any, gsspState: any, gippState: any) => {
        //...
    };
    // redux hydrate 執(zhí)行 hook
    const useHybridHydrate = (store: S, giapState: any, gspState: any, gsspState: any, gippState: any) => {
        // ...
    };
    // 用于在應用中關聯(lián) store 的 hook。一般用在 App 組件
    // giapState stands for getInitialAppProps state
    const useWrappedStore = <P extends AppProps>(incomingProps: P, displayName = 'useWrappedStore'): {store: S; props: P} => {
        // ...
        return {store, props: {...initialProps, ...resultProps}};
    };
    return {
        getServerSideProps,
        getStaticProps,
        getInitialAppProps,
        getInitialPageProps,
        useWrappedStore,
    };
};

可以把 createWrapper 函數(shù)的內容分兩類:

  • Next.js 數(shù)據(jù)獲取函數(shù)的包裹函數(shù)實現(xiàn)。
  • Next.js 渲染頁面內容 時的處理實現(xiàn)。

從 數(shù)據(jù)獲取 到 渲染頁面內容 本身就有一個前后順序,這里也按照這個順序來分析。

createWrapper 中 數(shù)據(jù)獲取函數(shù) 的包裹函數(shù)實現(xiàn)分析

callback 函數(shù)說明,就是我們調用 wrapper.[wrapperFunctionName] 函數(shù)時傳遞的參數(shù)。

比如:

App.getInitialProps = wrapper.getInitialAppProps(({ store }) => async (ctx) => {
  // ...
});

callback 就是 ({ store }) => async (ctx) => {} 這部分代碼.

我們來看幾個 數(shù)據(jù)獲取函數(shù) 的代碼:

// 頁面級 getInitialProps 函數(shù)的包裹函數(shù)
const getInitialPageProps =
    <P extends {} = any>(callback: PageCallback<S, P>): GetInitialPageProps<P> =>
    async (
        context: NextPageContext | any, // legacy
    ) => {
        // context 中會存儲 store — 避免雙重包裝,因為有可能上層組件也調用了 `getInitialPageProps` 或者 `getInitialAppProps` 函數(shù)。
        if ('getState' in context) {
            // 有緩存的 store ,直接給 callback 傳入的 store
            return callback && callback(context as any);
        }
        // 調用 makeProps 執(zhí)行開發(fā)者傳入要執(zhí)行的函數(shù)
        return await makeProps({callback, context, addStoreToContext: true});
    };
// 應用級 getInitialProps 函數(shù)的包裹函數(shù)
const getInitialAppProps =
    <P extends {} = any>(callback: AppCallback<S, P>): GetInitialAppProps<P> =>
    async (context: AppContext) => {
        // 調用 makeProps 執(zhí)行開發(fā)者傳入要執(zhí)行的函數(shù)
        const {initialProps, initialState} = await makeProps({callback, context, addStoreToContext: true});
        // 每個 `數(shù)據(jù)獲取函數(shù)` 最后需要的返回函數(shù)類型不一樣,因此需要處理成 Next.js 需要的類型
        return {
            ...initialProps,
            initialState,
        };
    };
// getStaticProps 函數(shù)的包裹函數(shù)
const getStaticProps =
    <P extends {} = any>(callback: GetStaticPropsCallback<S, P>): GetStaticProps<P> =>
    async context => {
        // 調用 makeProps 函數(shù)時,相比 getInitialPageProps 和 getInitialAppProps 缺少了 addStoreToContext 參數(shù)
        const {initialProps, initialState} = await makeProps({callback, context});
        return {
            ...initialProps,
            props: {
                ...initialProps.props,
                initialState,
            },
        } as any;
    };
// getServerSideProps 函數(shù)的包裹函數(shù)
const getServerSideProps =
    <P extends {} = any>(callback: GetServerSidePropsCallback<S, P>): GetServerSideProps<P> =>
    async context =>
        await getStaticProps(callback as any)(context); // 返回參數(shù)和static一樣,因此直接調用 getStaticProps 函數(shù)即可

我們可以看出幾個函數(shù)的主要區(qū)別點在于 參數(shù)類型(也就是context 類型) 和 返回值類型 不一致,這也是 Next.js 不同數(shù)據(jù)獲取函數(shù) 的區(qū)別之一。

getInitialPageProps 需要注意有可能上層組件也調用了 getInitialPageProps 或者 getInitialAppProps 函數(shù)。 因此需要判斷是否把 store 存儲到了 context 中,是的話,需要直接從 context 中獲取。(注釋是這樣的,這里有點問題,待驗證)

getInitialAppPropsgetInitialPageProps 實現(xiàn)類似,只是只需要執(zhí)行一次,不用考慮緩存,還有就是 返回值類型 不一樣

getStaticProps 調用 makeProps 函數(shù)時,相比 getInitialPagePropsgetInitialAppProps 缺少了 addStoreToContext 參數(shù),返回值也不一樣。

getServerSideProps 返回參數(shù)和 getStaticProps 一樣,因此直接調用 getStaticProps 函數(shù)即可。

接下來就是幾個數(shù)據(jù)獲取的 wrapper 函數(shù) 的實際核心實現(xiàn),也就是 makeProps 函數(shù):

// 數(shù)據(jù)獲取的 `wrapper 函數(shù)` 的實際核心
const makeProps = async ({
    callback,
    context,
    addStoreToContext = false,
}: {
    callback: Callback<S, any>;
    context: any;
    addStoreToContext?: boolean;
}): Promise<WrapperProps> => {
    // 服務端初始化 store
    const store = initStore({context, makeStore});
    if (config.debug) {
        console.log(`1. getProps created store with state`, store.getState());
    }
    // Legacy stuff - 把 store 放入 context 中
    if (addStoreToContext) {
        if (context.ctx) {
            context.ctx.store = store;
        } else {
            context.store = store;
        }
    }
    // 這里實現(xiàn)了 callback 雙層函數(shù)執(zhí)行,先傳遞 store 給 callback 生成新的函數(shù)
    const nextCallback = callback && callback(store);
    // 再用新的函數(shù)傳遞 context 參數(shù),獲取 initialProps
    const initialProps = (nextCallback && (await nextCallback(context))) || {};
    if (config.debug) {
        console.log(`3. getProps after dispatches has store state`, store.getState());
    }
    const state = store.getState();
    return {
        initialProps,
        // 在服務端可以對 state 數(shù)據(jù)加密,防止被輕易解析
        initialState: getIsServer() ? getSerializedState<S>(state, config) : state,
    };
};

makeProps 函數(shù)主要初始化 store ,然后把 store 傳遞給 callback 函數(shù)并執(zhí)行獲取到一個函數(shù),這個函數(shù)傳入 context 再次執(zhí)行,就可以獲取到對應 數(shù)據(jù)獲取函數(shù) 的返回值,處理后進行返回即可。

其中需要注意下面幾點:

  • callback 函數(shù)必須返回一個函數(shù),否則其返回的 initialProps 會被是空對象
  • 調用 createWrapper 時傳遞參數(shù)中的 debug 參數(shù)在這里會起作用
  • store 放入 context 是一個遺留的處理方式,后續(xù)可能會去掉。但并沒說明后續(xù)處理方式。
  • 返回的 initialState 在服務端可能自定義加密方法。

createWrapper 中 渲染頁面內容 時的處理實現(xiàn)分析

服務端把數(shù)據(jù)獲取后,下面就要進行的步驟是:渲染頁面內容。

有幾個概念需要先理清楚,不然會直接看前面幾個函數(shù)的實現(xiàn)會很懵逼。

  • giapState:對應 getInitialAppProps 處理后的 state
  • gspState:對應 getStaticProps 處理后的 state
  • gsspState:對應 getServerSideProps 處理后的 state
  • gippState:對應 getInitialPageProps 處理后的 state

useWrappedStore hook

// 用于在應用中關聯(lián) store 的hook。
const useWrappedStore = <P extends AppProps>(incomingProps: P, displayName = 'useWrappedStore'): {store: S; props: P} => {
    // createWrapper 給 incomingProps 添加了 WrapperProps 類型,因為它們不是從 P 類型獲取,因此在這里進行強制賦予類型
    const {initialState: giapState, initialProps, ...props} = incomingProps as P & WrapperProps;
    // getStaticProps state
    const gspState = props?.__N_SSG ? props?.pageProps?.initialState : null;
    // getServerSideProps state
    const gsspState = props?.__N_SSP ? props?.pageProps?.initialState : null;
    // getInitialPageProps state
    const gippState = !gspState && !gsspState ? props?.pageProps?.initialState ?? null : null;
    // debug 打印代碼,刪除掉了
    if (config.debug) {}
    // 獲取store,initStore 會緩存 store ,有則直接緩存,沒有則進行初始化。
    const store = useMemo<S>(() => initStore<S>({makeStore}), []);
    // state 初始化
    useHybridHydrate(store, giapState, gspState, gsspState, gippState);
    // 后續(xù)余state 無關,主要是去掉
    let resultProps: any = props;
    // 順序很重要! Next.js 中 page 的 getStaticProps 應該覆蓋 pages/_app 中的 props
    // @see https://github.com/zeit/next.js/issues/11648
    if (initialProps && initialProps.pageProps) {
        resultProps.pageProps = {
            ...initialProps.pageProps, // this comes from wrapper in _app mode
            ...props.pageProps, // this comes from gssp/gsp in _app mode
        };
    }
    // 防止props中的 initialState被到處傳遞,為了數(shù)據(jù)安全,清除掉 initialState
    if (props?.pageProps?.initialState) {
        resultProps = {...props, pageProps: {...props.pageProps}};
        delete resultProps.pageProps.initialState;
    }
    // 處理 getInitialPageProps 的數(shù)據(jù), 清除掉 initialProps
    if (resultProps?.pageProps?.initialProps) {
        resultProps.pageProps = {...resultProps.pageProps, ...resultProps.pageProps.initialProps};
        delete resultProps.pageProps.initialProps;
    }
    return {store, props: {...initialProps, ...resultProps}};
};

useWrappedStore hook 主要做了以下幾件事:

  • App props 中獲取數(shù)據(jù),并進行分類
  • 初始化 store :服務端不會再初始化,會從緩存獲取,客戶端則會在這里初始化。
  • 處理數(shù)據(jù)覆蓋排序及清除 wrapper 函數(shù)注入的自定義數(shù)據(jù)字段:initialPropsinitialState

initStore

// 初始化 redux store
const initStore = <S extends Store>({makeStore, context = {}}: InitStoreOptions<S>): S => {
    const createStore = () => makeStore(context);
    if (getIsServer()) {
        // 服務端則存儲到 req.__nextReduxWrapperStore 參數(shù) 中進行服用
        const req: any = (context as NextPageContext)?.req || (context as AppContext)?.ctx?.req;
        if (req) {
            // ATTENTION! THIS IS INTERNAL, DO NOT ACCESS DIRECTLY ANYWHERE ELSE
            // @see https://github.com/kirill-konshin/next-redux-wrapper/pull/196#issuecomment-611673546
            if (!req.__nextReduxWrapperStore) {
                req.__nextReduxWrapperStore = createStore(); // Used in GIP/GSSP
            }
            return req.__nextReduxWrapperStore;
        }
        return createStore();
    }
    // Memoize the store if we're on the client
    // 客戶端則存儲到 sharedClientStore 上
    if (!sharedClientStore) {
        sharedClientStore = createStore();
    }
    return sharedClientStore;
};

initStore 函數(shù)中,服務端利用每次請求都會新生成的對象 req 來緩存服務端請求上下文的 store,客戶端利用 sharedClientStore 變量來存儲。

useHybridHydrate hook

// hydrate 初始化前置邏輯處理
const useHybridHydrate = (store: S, giapState: any, gspState: any, gsspState: any, gippState: any) => {
    const {events} = useRouter();
    const shouldHydrate = useRef(true);
    // 客戶端路由改變的時候好應該重新初始化。
    useEffect(() => {
        const handleStart = () => {
            shouldHydrate.current = true;
        };
        events?.on('routeChangeStart', handleStart);
        return () => {
            events?.off('routeChangeStart', handleStart);
        };
    }, [events]);
    // 這里寫了一大堆注釋,就是表面 useMemo 里面進行 hydrate 并不會導致頁面重新不斷渲染,主要是當作 constructor 來使用
    useMemo(() => {
        if (shouldHydrate.current) {
            hydrateOrchestrator(store, giapState, gspState, gsspState, gippState);
            shouldHydrate.current = false;
        }
    }, [store, giapState, gspState, gsspState, gippState]);
};

useHybridHydrate 函數(shù)主要用于處理 初始化 state 的前置邏輯,主要使用 useMemo 來盡早調用 hydrateOrchestrator 函數(shù)初始化 state , 并使用監(jiān)聽路由的方式來處理客戶端路由切換時需要重新初始化的問題。

hydrateOrchestrator

// hydrate 處理中間數(shù)據(jù)的函數(shù)(用于分析處理各種情況)
const hydrateOrchestrator = (store: S, giapState: any, gspState: any, gsspState: any, gippState: any) => {
    if (gspState) {
        // `getStaticProps` 并不能和其他數(shù)據(jù)獲取函數(shù)同時存在在一個頁面,但可能存在 `getInitialAppProps`,這時候只會在構建的時候執(zhí)行,因此需要覆蓋 `giapState`
        hydrate(store, giapState);
        hydrate(store, gspState);
    } else if (gsspState || gippState || giapState) {
        // 處理優(yōu)先級問題,getServerSideProps > getInitialPageProps > getInitialAppProps
        hydrate(store, gsspState ?? gippState ?? giapState);
    }
};

hydrateOrchestrator 函數(shù)主要用于處理獲取state的覆蓋和優(yōu)先級問題:

  • getStaticProps 并不能和其他數(shù)據(jù)獲取函數(shù)同時存在在一個頁面,但可能存在 getInitialAppProps,這時候只會在構建的時候執(zhí)行,因此需要覆蓋 giapState 。
  • SSG 情況,優(yōu)先級 getServerSideProps > getInitialPageProps > getInitialAppProps

hydrate

// hydrate 處理函數(shù),觸發(fā)服務端渲染過程中的數(shù)據(jù) HYDRATE
const hydrate = (store: S, state: any) => {
    if (!state) {
        return;
    }
    store.dispatch({
        type: HYDRATE,
        payload: getDeserializedState<S>(state, config),
    } as any);
};

hydrate 函數(shù)就是用于發(fā)起 HYDRATE action 的。

總結

本文主要從 目錄結構 分析,然后再到 代碼結構分析,最好再分析重點代碼實現(xiàn),這樣一步一步的去讀源碼,可以讓思路更加清晰。

讀源碼之前,有使用經驗也是一個很重要的點,這樣才能帶著疑問和思考去讀,讓閱讀源碼更有意思

以上就是next-redux-wrapper使用細節(jié)及源碼分析的詳細內容,更多關于next-redux-wrapper使用的資料請關注腳本之家其它相關文章!

相關文章

  • React?Context源碼實現(xiàn)原理詳解

    React?Context源碼實現(xiàn)原理詳解

    這篇文章主要為大家介紹了React?Context源碼實現(xiàn)原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • react?redux及redux持久化示例詳解

    react?redux及redux持久化示例詳解

    這篇文章主要為大家介紹了react?redux及redux持久化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • React Fiber與調和深入分析

    React Fiber與調和深入分析

    Fiber可以理解為一個執(zhí)行單元,每次執(zhí)行完一個執(zhí)行單元,React Fiber就會檢查還剩多少時間,如果沒有時間則將控制權讓出去,然后由瀏覽器執(zhí)行渲染操作,這篇文章主要介紹了React Fiber架構原理剖析,需要的朋友可以參考下
    2022-11-11
  • 關于React中setState同步或異步問題的理解

    關于React中setState同步或異步問題的理解

    相信很多小伙伴們都一直在疑惑,setState 到底是同步還是異步。本文就詳細的介紹一下React中setState同步或異步問題,感興趣的可以了解一下
    2021-11-11
  • React創(chuàng)建組件的三種方式及其區(qū)別是什么

    React創(chuàng)建組件的三種方式及其區(qū)別是什么

    在React中,創(chuàng)建組件的三種主要方式是函數(shù)式組件、類組件和使用React Hooks的函數(shù)式組件,本文就詳細的介紹一下如何使用,感興趣的可以了解一下
    2023-08-08
  • React組件實例三大核心屬性State props Refs詳解

    React組件實例三大核心屬性State props Refs詳解

    組件實例的三大核心屬性是:State、Props、Refs。類組件中這三大屬性都存在。函數(shù)式組件中訪問不到 this,也就不存在組件實例這種說法,但由于它的特殊性(函數(shù)可以接收參數(shù)),所以存在Props這種屬性
    2022-12-12
  • React 數(shù)據(jù)獲取條件競爭原理解析

    React 數(shù)據(jù)獲取條件競爭原理解析

    這篇文章主要為大家介紹了React 數(shù)據(jù)獲取條件競爭原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • 基于Node的React圖片上傳組件實現(xiàn)實例代碼

    基于Node的React圖片上傳組件實現(xiàn)實例代碼

    本篇文章主要介紹了基于Node的React圖片上傳組件實現(xiàn)實例代碼,非常具有實用價值,需要的朋友可以參考下
    2017-05-05
  • 淺談React和Redux的連接react-redux

    淺談React和Redux的連接react-redux

    本篇文章主要介紹了淺談React和Redux的連接react-redux,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • 可定制React自動完成搜索組件Turnstone實現(xiàn)示例

    可定制React自動完成搜索組件Turnstone實現(xiàn)示例

    這篇文章主要為大家介紹了可定制React自動完成搜索組件Turnstone實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10

最新評論

田东县| 永福县| 长治县| 大连市| 吐鲁番市| 墨竹工卡县| 大姚县| 湘阴县| 达拉特旗| 雷山县| 镇巴县| 阳曲县| 兰考县| 平顺县| 洪洞县| 余姚市| 饶平县| 沾益县| 邵阳县| 天水市| 扎鲁特旗| 柯坪县| 东乌珠穆沁旗| 隆德县| 嘉义市| 临漳县| 荔波县| 石景山区| 高碑店市| 固安县| 永城市| 湘乡市| 廊坊市| 宁乡县| 都匀市| 乌恰县| 稷山县| 中山市| 宣恩县| 安康市| 德阳市|