next.js源碼解析getStaticProps?getStaticPaths使用場(chǎng)景
引言
?? 好久前寫了關(guān)于 getStaticProps
和 getStaticPaths
的內(nèi)容,然而半年過(guò)去了源碼解析就一直忘記了,不久前有人提醒才想起來(lái),補(bǔ)下坑。
本文主要是解讀下 getStaticProps
、getStaticPaths
相關(guān)的源碼,不了解這兩個(gè) API
的建議先看下之前的文章再看。?? http://www.dbjr.com.cn/javascript/288755r3l.htm
getStaticProps
首先 getStaticProps
是應(yīng)用于 SSG
場(chǎng)景,我們先看下 packages/next/server/render.tsx
中相關(guān)的代碼:
const isSSG = !!getStaticProps; const pageIsDynamic = isDynamicRoute(pathname); if (isSSG && !isFallback) { let data: UnwrapPromise<ReturnType<GetStaticProps>>; try { data = await getStaticProps!({ ...(pageIsDynamic ? { params: query as ParsedUrlQuery } : undefined), ...(isPreview ? { preview: true, previewData: previewData } : undefined), locales: renderOpts.locales, locale: renderOpts.locale, defaultLocale: renderOpts.defaultLocale }); } catch (staticPropsError: any) { // .... } // ... }
isFallback
可以先不管??梢钥吹?nbsp;getStaticProps
同樣可以為異步函數(shù),而是否為 SSG
就是由是否存在 getStaticProps
函數(shù)來(lái)決定的,SSG
場(chǎng)景下的 pageIsDynamic
則必須配合 getStaticPaths
使用,可以看到 getStaticProps
會(huì)接收幾個(gè)參數(shù):
params
是在動(dòng)態(tài)頁(yè)面的路由參數(shù)previewData
和preview: preview
模式的相關(guān)數(shù)據(jù)locales, locale
和defaultLocale
多語(yǔ)言相關(guān)參數(shù)
執(zhí)行完成后 getStaticProps
的返回值會(huì)被放入 pageProps
中。
再看看 invalidKeys
相關(guān)部分,除了 revalidate
、props
、redirect
和 notFound
外別的屬性都會(huì)被視為非法。
const invalidKeys = Object.keys(data).filter( key => key !== 'revalidate' && key !== 'props' && key !== 'redirect' && key !== 'notFound' ); if (invalidKeys.includes('unstable_revalidate')) { throw new Error(UNSTABLE_REVALIDATE_RENAME_ERROR); } if (invalidKeys.length) { throw new Error(invalidKeysMsg('getStaticProps', invalidKeys)); }
然后還有關(guān)于 notFound
和 redirect
的處理:
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; } if ('redirect' in data && data.redirect && typeof data.redirect === 'object') { checkRedirectValues(data.redirect as Redirect, req, 'getStaticProps'); if (isBuildTimeSSG) { throw new Error( `\`redirect\` can not be returned from getStaticProps during prerendering (${req.url})\n` + `See more info here: https://nextjs.org/docs/messages/gsp-redirect-during-prerender` ); } (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; }
notFound
會(huì)使用 renderOpts.isNotFound
來(lái)標(biāo)識(shí),而 redirect
則會(huì)在 props
中通過(guò) __N_REDIRECT
相關(guān)的參數(shù)來(lái)進(jìn)行標(biāo)識(shí)。
當(dāng)然這里省略很多的校驗(yàn),比如 getStaticProps
和 getServerSideProps
沖突、getStaticPaths
的檢查、notFound
和 redirect
不能同時(shí)存在等。
props.pageProps = Object.assign({}, props.pageProps, 'props' in data ? data.props : undefined);
然后其中還包含了一部分與 revalidate
相關(guān)的內(nèi)容,主要是一些檢測(cè)和值的處理,主要與 ISR
相關(guān)的此處先跳過(guò)。
getStaticPaths
getStaticPaths
的相關(guān)的調(diào)用源碼主要在 packages/next/build/utils.ts
文件中的 buildStaticPaths
中,buildStaticPaths
會(huì)在兩個(gè)時(shí)候被調(diào)用,一個(gè)是 next.js
構(gòu)建的時(shí)候,第二個(gè)是 next.js
的 devServer
中。在 next.js
遇到動(dòng)態(tài)路由時(shí),會(huì)按照 buildStaticPaths
和 getStaticProps
來(lái)決定是否啟用 SSG
模式,啟用則會(huì)調(diào)用 buildStaticPaths
獲取該動(dòng)態(tài)路由所對(duì)應(yīng)的需要構(gòu)建的所有靜態(tài)頁(yè)面。
if (getStaticPaths) { staticPathsResult = await getStaticPaths({ locales, defaultLocale }); } if (!staticPathsResult || typeof staticPathsResult !== 'object' || Array.isArray(staticPathsResult)) { throw new Error( `Invalid value returned from getStaticPaths in ${page}. Received ${typeof staticPathsResult} ${expectedReturnVal}` ); } const invalidStaticPathKeys = Object.keys(staticPathsResult).filter(key => !(key === 'paths' || key === 'fallback')); if (invalidStaticPathKeys.length > 0) { throw new Error( `Extra keys returned from getStaticPaths in ${page} (${invalidStaticPathKeys.join(', ')}) ${expectedReturnVal}` ); } if (!(typeof staticPathsResult.fallback === 'boolean' || staticPathsResult.fallback === 'blocking')) { throw new Error(`The \`fallback\` key must be returned from getStaticPaths in ${page}.\n` + expectedReturnVal); } const toPrerender = staticPathsResult.paths; if (!Array.isArray(toPrerender)) { throw new Error( `Invalid \`paths\` value returned from getStaticPaths in ${page}.\n` + `\`paths\` must be an array of strings or objects of shape { params: [key: string]: string }` ); }
在 buildStaticPaths
第一部分是獲取 getStaticPaths
的返回值,并對(duì)其返回值進(jìn)行檢查:
getStaticPaths
可以為async
方法getStaticPaths
接受兩個(gè)參數(shù):locales
和defaultLocale
- 返回值必須為
{paths: Array, fallback: boolean | 'blocking'}
結(jié)構(gòu)
而在拿到 toPrerender
之后,next.js
會(huì)將其轉(zhuǎn)換為 prerenderPaths
和 encodedPrerenderPaths
,這兩個(gè) set
的數(shù)據(jù)集基本一致,只是一個(gè) path
為已經(jīng)被解碼,一個(gè)沒(méi)有,猜測(cè)是為了性能考慮空間換時(shí)間。
toPrerender.forEach(entry => { if (typeof entry === 'string') { entry = removeTrailingSlash(entry); const localePathResult = normalizeLocalePath(entry, locales); let cleanedEntry = entry; if (localePathResult.detectedLocale) { cleanedEntry = entry.slice(localePathResult.detectedLocale.length + 1); } else if (defaultLocale) { entry = `/${defaultLocale}${entry}`; } const result = _routeMatcher(cleanedEntry); if (!result) { throw new Error(`The provided path \`${cleanedEntry}\` does not match the page: \`${page}\`.`); } // If leveraging the string paths variant the entry should already be // encoded so we decode the segments ensuring we only escape path // delimiters prerenderPaths.add( entry .split('/') .map(segment => escapePathDelimiters(decodeURIComponent(segment), true)) .join('/') ); encodedPrerenderPaths.add(entry); } else { // ... } });
針對(duì) string
類型的 entry
,簡(jiǎn)單的處理下語(yǔ)言、路徑即可。
const _validParamKeys = Object.keys(_routeMatcher(page)); if (typeof entry === 'string') { // ... } else { const invalidKeys = Object.keys(entry).filter(key => key !== 'params' && key !== 'locale'); if (invalidKeys.length) { throw new Error('...'); } const { params = {} } = entry; let builtPage = page; let encodedBuiltPage = page; _validParamKeys.forEach(validParamKey => { const { repeat, optional } = _routeRegex.groups[validParamKey]; let paramValue = params[validParamKey]; if ( optional && params.hasOwnProperty(validParamKey) && (paramValue === null || paramValue === undefined || (paramValue as any) === false) ) { paramValue = []; } if ((repeat && !Array.isArray(paramValue)) || (!repeat && typeof paramValue !== 'string')) { throw new Error('...'); } let replaced = `[${repeat ? '...' : ''}${validParamKey}]`; if (optional) { replaced = `[${replaced}]`; } builtPage = builtPage .replace( replaced, repeat ? (paramValue as string[]).map(segment => escapePathDelimiters(segment, true)).join('/') : escapePathDelimiters(paramValue as string, true) ) .replace(/(?!^)\/$/, ''); encodedBuiltPage = encodedBuiltPage .replace( replaced, repeat ? (paramValue as string[]).map(encodeURIComponent).join('/') : encodeURIComponent(paramValue as string) ) .replace(/(?!^)\/$/, ''); }); if (entry.locale && !locales?.includes(entry.locale)) { throw new Error('...'); } const curLocale = entry.locale || defaultLocale || ''; prerenderPaths.add(`${curLocale ? `/${curLocale}` : ''}${curLocale && builtPage === '/' ? '' : builtPage}`); encodedPrerenderPaths.add( `${curLocale ? `/${curLocale}` : ''}${curLocale && encodedBuiltPage === '/' ? '' : encodedBuiltPage}` ); }
而對(duì)于 Object
類型的 entry
,則會(huì)先檢查確保是 {params, locale}
結(jié)構(gòu),然后使用 params
對(duì)動(dòng)態(tài)路由進(jìn)行替換拼接。 _validParamKeys
是該動(dòng)態(tài)路由頁(yè)面中的參數(shù)的 key
數(shù)組。然后一樣是路徑和語(yǔ)言的處理。最終的返回值如下:
return { paths: [...prerenderPaths], fallback: staticPathsResult.fallback, encodedPaths: [...encodedPrerenderPaths] };
當(dāng)需要時(shí) next.js
就會(huì)使用這里的 paths
來(lái)生成對(duì)應(yīng)的靜態(tài)頁(yè)面,從而實(shí)現(xiàn)動(dòng)態(tài)路由的 SSG
。
總結(jié)
getStaticProps
、getStaticPaths
相關(guān)的源碼其實(shí)大部分都是在處理關(guān)于數(shù)據(jù)檢查、處理這類的事情,因?yàn)檫@兩個(gè) API
的指責(zé)也都很簡(jiǎn)單:getStaticPaths
負(fù)責(zé)為動(dòng)態(tài)路由的 SSG
場(chǎng)景提供頁(yè)面列表,getStaticProps
則為 SSG
頁(yè)面提供對(duì)應(yīng)的頁(yè)面數(shù)據(jù)。
以上就是next.js源碼解析getStaticProps getStaticPaths使用場(chǎng)景的詳細(xì)內(nèi)容,更多關(guān)于next.js getStaticProps getStaticPaths的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
lodash內(nèi)部方法getData和setData實(shí)例解析
本篇章我們將了解lodash里內(nèi)部關(guān)于Data的操作方法,重點(diǎn)關(guān)注getData、setData兩個(gè)內(nèi)部方法,同時(shí)由實(shí)現(xiàn)上引申其他內(nèi)部封裝的方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08微信小程序開(kāi)發(fā)一鍵登錄 獲取session_key和openid實(shí)例
這篇文章主要介紹了微信小程序開(kāi)發(fā)一鍵登錄 獲取session_key和openid實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-11-11package.json中browser?module?main字段優(yōu)先級(jí)對(duì)比
這篇文章主要介紹了package.json中browser?module?main字段的優(yōu)先級(jí)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07微信小程序之網(wǎng)絡(luò)請(qǐng)求簡(jiǎn)單封裝實(shí)例詳解
這篇文章主要介紹了微信小程序之網(wǎng)絡(luò)請(qǐng)求簡(jiǎn)單封裝實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06微信小程序 支付簡(jiǎn)單實(shí)例及注意事項(xiàng)
這篇文章主要介紹了微信小程序 支付簡(jiǎn)單實(shí)例的相關(guān)資料,這里參考官方文檔寫的簡(jiǎn)單實(shí)例,并提出注意事項(xiàng),需要的朋友可以參考下2017-01-01JavaScript設(shè)計(jì)模式之命令模式和狀態(tài)模式詳解
這篇文章主要為大家介紹了JavaScript設(shè)計(jì)模式之命令模式和狀態(tài)模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08