欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

next.js源碼解析getStaticProps?getStaticPaths使用場(chǎng)景

 更新時(shí)間:2023年08月16日 09:11:19   作者:嘿嘿不務(wù)正業(yè)  
這篇文章主要為大家介紹了next.js源碼解析getStaticProps?getStaticPaths使用場(chǎng)景,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

?? 好久前寫了關(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)部分,除了 revalidateprops、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)文章

最新評(píng)論