next.js?getServerSideProps源碼解析
SSR 處理
老規(guī)矩,昨天寫了關(guān)于 getServerSideProps 的內(nèi)容,今天趁熱寫一下 getServerSideProps 相應(yīng)的源碼,看看 next.js getServerSideProps 是怎么實現(xiàn)的,還有什么從文檔無法知曉的細節(jié)。
我們先從 SSR 時相關(guān)的 getServerSideProps 處理看起,源碼排查步驟上一步已經(jīng)有所介紹,本篇不再多說,在 SSR 時,next.js 會調(diào)用 doRender 來進行渲染,其中會再次調(diào)用 renderHTML,進過各種判斷和調(diào)用最終會進入 packages/next/server/render.tsx 中的 renderToHTML 進行處理。
// const SERVER_PROPS_ID = "__N_SSP";
if (getServerSideProps) {
props[SERVER_PROPS_ID] = true;
}
next.js 會先將 props 中的 SERVER_PROPS_ID 設(shè)置為 true,用做標識。
try {
data = await getServerSideProps({
req: req as IncomingMessage & {
cookies: NextApiRequestCookies;
},
res: resOrProxy,
query,
resolvedUrl: renderOpts.resolvedUrl as string,
...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined),
...(previewData !== false ? { preview: true, previewData: previewData } : undefined),
locales: renderOpts.locales,
locale: renderOpts.locale,
defaultLocale: renderOpts.defaultLocale
});
canAccessRes = false;
} catch (serverSidePropsError: any) {
if (isError(serverSidePropsError) && serverSidePropsError.code === 'ENOENT') {
delete serverSidePropsError.code;
}
throw serverSidePropsError;
}
if (data == null) {
throw new Error(GSSP_NO_RETURNED_VALUE);
}
if ((data as any).props instanceof Promise) {
deferredContent = true;
}
const invalidKeys = Object.keys(data).filter(key => key !== 'props' && key !== 'redirect' && key !== 'notFound');
if (invalidKeys.length) {
throw new Error(invalidKeysMsg('getServerSideProps', invalidKeys));
}
注意這里的 getServerSideProps 是從外層傳進來了,因為涉及的代碼較多,就不貼了,主要是通過 packages/next/server/load-components 中的 loadComponents,將路由文件中的 getServerSideProps 通過從 require 后的頁面中取出。不過挺好奇他在 node 端是怎么 require 頁面代碼而不報錯的,畢竟頁面代碼中很可能會存在依賴瀏覽器環(huán)境的代碼,估計是做了一些類似于 runtime shim 之類的操作?此處先挖個坑,以后有空研究下。突然想起頁面都是 SSR 了,初始化代碼肯定都做過處理了 ??。
上面的代碼可以看出 SSR 的時候是直接調(diào)用 getServerSideProps 傳入 context 內(nèi)容,context 的內(nèi)容也一目了然。然后 next.js 會校驗返回值是否為空,或者是否包含非法參數(shù)等。
然后回去檢查 notFound 和 redirect 參數(shù),進行特殊處理。
if ('notFound' in data && data.notFound) {
if (pathname === '/404') {
throw new Error(`The /404 page can not return notFound in "getStaticProps", please remove it to continue!`);
}
(renderOpts as any).isNotFound = true;
return null;
}
if ('redirect' in data && typeof data.redirect === 'object') {
checkRedirectValues(data.redirect as Redirect, req, 'getServerSideProps');
(data as any).props = {
__N_REDIRECT: data.redirect.destination,
__N_REDIRECT_STATUS: getRedirectStatus(data.redirect)
};
if (typeof data.redirect.basePath !== 'undefined') {
(data as any).props.__N_REDIRECT_BASE_PATH = data.redirect.basePath;
}
(renderOpts as any).isRedirect = true;
}
此外從上面這段代碼還發(fā)現(xiàn)一個有意思的就是 props 是可以為 Promise 的:
if ((data as any).props instanceof Promise) {
deferredContent = true;
}
返回 Promise 時,next.js 會在異常處理完畢后獲取值:
if (deferredContent) {
(data as any).props = await(data as any).props;
}
最后 next.js 會將獲取到的 props 值放入到頂層的 props 中:
props.pageProps = Object.assign({}, props.pageProps, (data as any).props);
(renderOpts as any).pageData = props;
在 SSR 時,我們在頁面中看到的用于 hydrate 的 __NEXT_DATA__ 中的 props 就是這個 props,可以再看一眼其中的數(shù)據(jù):
<script id="__NEXT_DATA__" type="application/json">
{
"props": {
"pageProps": {
"feature": {
"name": "xxxx",
"desc": "xxxx",
"tags": ["xxx"],
"id": "account-manage"
}
},
"__N_SSP": true
},
"page": "/feature/[fid]",
"query": { "fid": "account-manage" },
"buildId": "development",
"isFallback": false,
"gssp": true,
"scriptLoader": []
}
</script>
可以看到 pageProps 和 __N_SSP 都是上面放進去的。
動態(tài)加載處理
看完了 SSR 場景下,next.js 如何處理 getServerSideProps,我們再看下頁面為動態(tài)加載時的處理。
通過跳轉(zhuǎn)時發(fā)起請求的調(diào)用棧,我們很輕松就能找到在頁面為動態(tài)加載時,next.js 將會通過 packages/next/shared/lib/router.ts 中的 getRouteInfo 來獲取要跳轉(zhuǎn)的頁面信息,然后會通過 routeInfo 的 __N_SSP 判定是否要去獲取數(shù)據(jù):
const shouldFetchData = routeInfo.__N_SSG || routeInfo.__N_SSP;
if (shouldFetchData) {
const { json, cacheKey: _cacheKey } = data?.json
? data
: await fetchNextData({
dataHref: this.pageLoader.getDataHref({
href: formatWithValidation({ pathname, query }),
asPath: resolvedAs,
locale
}),
isServerRender: this.isSsr,
parseJSON: true,
inflightCache: this.sdc,
persistCache: !isPreview,
isPrefetch: false,
unstable_skipClientCache
});
return {
cacheKey: _cacheKey,
props: json || {}
};
}
然后通過 fetchNextData 來獲取數(shù)據(jù),而我們上文提到的 _next/data/development/{url}.json?{query} 這段 URL 就是由 formatWithValidation 構(gòu)建生成的。
而請求發(fā)送后服務(wù)端的處理就七繞八繞邏輯太深了,這里不一一列舉代碼,簡單說下:next.js 會通過 /_next/data/ 匹配請求判斷是否是數(shù)據(jù)請求,如果是數(shù)據(jù)請求將會一樣執(zhí)行 SSR代碼,然后可以理解為走的就是上面 SSR 初始化時的那套邏輯,只是最后會按照數(shù)據(jù)請求標識,將 props 抽離出來,放到響應(yīng)中中返回。
總結(jié)
getServerSideProps 相關(guān)的源碼還是有點繞的,其中應(yīng)該還少了一些其它場景的相關(guān)代碼,不過只看主場景應(yīng)該就是這些了。
以上就是next.js getServerSideProps源碼解析的詳細內(nèi)容,更多關(guān)于next.js getServerSideProps的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
深入理解JavaScript系列(29):設(shè)計模式之裝飾者模式詳解
這篇文章主要介紹了深入理解JavaScript系列(29):設(shè)計模式之裝飾者模式詳解,裝飾者用用于包裝同接口的對象,不僅允許你向方法添加行為,而且還可以將方法設(shè)置成原始對象調(diào)用(例如裝飾者的構(gòu)造函數(shù)),需要的朋友可以參考下2015-03-03
下面就結(jié)合我自己的體會和所學(xué)習(xí)的東東和大家一起來學(xué)習(xí)在JS中如何使用面向?qū)ο蟮木幊獭?/div> 2011-08-08
uniapp中scroll-view實現(xiàn)自動滾動到最底部的方法
這篇文章主要給大家介紹了關(guān)于uniapp中scroll-view實現(xiàn)自動滾動到最底部的相關(guān)資料,在uniapp日常開發(fā)的過程中經(jīng)常會有局部滾動的需求,而scroll-view組件正好可以滿足這一需求,需要的朋友可以參考下2023-10-10
解決微信小程序調(diào)用moveToLocation失效問題【超簡單】
這篇文章主要介紹了解決微信小程序調(diào)用moveToLocation失效問題,解決方法超級簡單,需要的朋友可以參考下2019-04-04最新評論

