next.js?getServerSideProps源碼解析
SSR 處理
老規(guī)矩,昨天寫了關(guān)于 getServerSideProps
的內(nèi)容,今天趁熱寫一下 getServerSideProps
相應(yīng)的源碼,看看 next.js getServerSideProps
是怎么實(shí)現(xiàn)的,還有什么從文檔無法知曉的細(xì)節(jié)。
我們先從 SSR
時(shí)相關(guān)的 getServerSideProps
處理看起,源碼排查步驟上一步已經(jīng)有所介紹,本篇不再多說,在 SSR
時(shí),next.js
會(huì)調(diào)用 doRender
來進(jìn)行渲染,其中會(huì)再次調(diào)用 renderHTML
,進(jìn)過各種判斷和調(diào)用最終會(huì)進(jìn)入 packages/next/server/render.tsx
中的 renderToHTML
進(jìn)行處理。
// const SERVER_PROPS_ID = "__N_SSP"; if (getServerSideProps) { props[SERVER_PROPS_ID] = true; }
next.js
會(huì)先將 props
中的 SERVER_PROPS_ID
設(shè)置為 true
,用做標(biāo)識(shí)。
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
是從外層傳進(jìn)來了,因?yàn)樯婕暗拇a較多,就不貼了,主要是通過 packages/next/server/load-components
中的 loadComponents
,將路由文件中的 getServerSideProps
通過從 require
后的頁面中取出。不過挺好奇他在 node
端是怎么 require
頁面代碼而不報(bào)錯(cuò)的,畢竟頁面代碼中很可能會(huì)存在依賴瀏覽器環(huán)境的代碼,估計(jì)是做了一些類似于 runtime shim
之類的操作?此處先挖個(gè)坑,以后有空研究下。突然想起頁面都是 SSR
了,初始化代碼肯定都做過處理了 ??。
上面的代碼可以看出 SSR
的時(shí)候是直接調(diào)用 getServerSideProps
傳入 context
內(nèi)容,context
的內(nèi)容也一目了然。然后 next.js
會(huì)校驗(yàn)返回值是否為空,或者是否包含非法參數(shù)等。
然后回去檢查 notFound
和 redirect
參數(shù),進(jìn)行特殊處理。
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)一個(gè)有意思的就是 props
是可以為 Promise
的:
if ((data as any).props instanceof Promise) { deferredContent = true; }
返回 Promise
時(shí),next.js
會(huì)在異常處理完畢后獲取值:
if (deferredContent) { (data as any).props = await(data as any).props; }
最后 next.js
會(huì)將獲取到的 props
值放入到頂層的 props
中:
props.pageProps = Object.assign({}, props.pageProps, (data as any).props); (renderOpts as any).pageData = props;
在 SSR
時(shí),我們?cè)陧撁嬷锌吹降挠糜?hydrate
的 __NEXT_DATA__
中的 props
就是這個(gè) 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
都是上面放進(jìn)去的。
動(dòng)態(tài)加載處理
看完了 SSR
場景下,next.js
如何處理 getServerSideProps
,我們?cè)倏聪马撁鏋閯?dòng)態(tài)加載時(shí)的處理。
通過跳轉(zhuǎn)時(shí)發(fā)起請(qǐng)求的調(diào)用棧,我們很輕松就能找到在頁面為動(dòng)態(tài)加載時(shí),next.js
將會(huì)通過 packages/next/shared/lib/router.ts
中的 getRouteInfo
來獲取要跳轉(zhuǎn)的頁面信息,然后會(huì)通過 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)建生成的。
而請(qǐng)求發(fā)送后服務(wù)端的處理就七繞八繞邏輯太深了,這里不一一列舉代碼,簡單說下:next.js
會(huì)通過 /_next/data/
匹配請(qǐng)求判斷是否是數(shù)據(jù)請(qǐng)求,如果是數(shù)據(jù)請(qǐng)求將會(huì)一樣執(zhí)行 SSR
代碼,然后可以理解為走的就是上面 SSR
初始化時(shí)的那套邏輯,只是最后會(huì)按照數(shù)據(jù)請(qǐng)求標(biāo)識(shí),將 props
抽離出來,放到響應(yīng)中中返回。
總結(jié)
getServerSideProps
相關(guān)的源碼還是有點(diǎn)繞的,其中應(yīng)該還少了一些其它場景的相關(guān)代碼,不過只看主場景應(yīng)該就是這些了。
以上就是next.js getServerSideProps源碼解析的詳細(xì)內(nèi)容,更多關(guān)于next.js getServerSideProps的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
發(fā)一個(gè)數(shù)據(jù)過濾的代碼,很簡單,有用的著的拿去
發(fā)一個(gè)數(shù)據(jù)過濾的代碼,很簡單,有用的著的拿去...2007-02-02JavaScript使用html2canvas實(shí)現(xiàn)截取HTML并生成圖片
在前端開發(fā)中,有時(shí)我們需要將網(wǎng)頁的一部分或整個(gè)頁面截取并保存為圖片,這在生成報(bào)告、分享內(nèi)容或保存用戶界面狀態(tài)等場景中非常有用,本文將介紹如何使用 JavaScript 庫 html2canvas 來實(shí)現(xiàn)這一功能,并提供一個(gè)完整的示例,需要的朋友可以參考下2024-10-10