- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
😂 好久前写了关于 getStaticProps 和 getStaticPaths 的内容,然而半年过去了源码解析就一直忘记了,不久前有人提醒才想起来,补下坑.
本文主要是解读下 getStaticProps 、 getStaticPaths 相关的源码,不了解这两个 API 的建议先看下之前的文章再看。👀 。
首先 getStaticProps 是应用于 SSG 场景,我们先看下 packages/next/server/render.tsx 中相关的代码:
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 可以先不管。可以看到 getStaticProps 同样可以为异步函数,而是否为 SSG 就是由是否存在 getStaticProps 函数来决定的, SSG 场景下的 pageIsDynamic 则必须配合 getStaticPaths 使用,可以看到 getStaticProps 会接收几个参数:
params
是在动态页面的路由参数 previewData
和 preview: preview
模式的相关数据 locales, locale
和 defaultLocale
多语言相关参数 执行完成后 getStaticProps 的返回值会被放入 pageProps 中.
再看看 invalidKeys 相关部分,除了 revalidate 、 props 、 redirect 和 notFound 外别的属性都会被视为非法.
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));
}
然后还有关于 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 会使用 renderOpts.isNotFound 来标识,而 redirect 则会在 props 中通过 __N_REDIRECT 相关的参数来进行标识.
当然这里省略很多的校验,比如 getStaticProps 和 getServerSideProps 冲突、 getStaticPaths 的检查、 notFound 和 redirect 不能同时存在等.
props.pageProps = Object.assign({}, props.pageProps, 'props' in data ? data.props : undefined);
然后其中还包含了一部分与 revalidate 相关的内容,主要是一些检测和值的处理,主要与 ISR 相关的此处先跳过.
getStaticPaths 的相关的调用源码主要在 packages/next/build/utils.ts 文件中的 buildStaticPaths 中, buildStaticPaths 会在两个时候被调用,一个是 next.js 构建的时候,第二个是 next.js 的 devServer 中。在 next.js 遇到动态路由时,会按照 buildStaticPaths 和 getStaticProps 来决定是否启用 SSG 模式,启用则会调用 buildStaticPaths 获取该动态路由所对应的需要构建的所有静态页面.
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 的返回值,并对其返回值进行检查:
getStaticPaths
可以为 async
方法 getStaticPaths
接受两个参数: locales
和 defaultLocale
{paths: Array, fallback: boolean | 'blocking'}
结构 而在拿到 toPrerender 之后, next.js 会将其转换为 prerenderPaths 和 encodedPrerenderPaths ,这两个 set 的数据集基本一致,只是一个 path 为已经被解码,一个没有,猜测是为了性能考虑空间换时间.
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 {
// ...
}
});
针对 string 类型的 entry ,简单的处理下语言、路径即可.
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}`
);
}
而对于 Object 类型的 entry ,则会先检查确保是 {params, locale} 结构,然后使用 params 对动态路由进行替换拼接。 _validParamKeys 是该动态路由页面中的参数的 key 数组。然后一样是路径和语言的处理。最终的返回值如下:
return {
paths: [...prerenderPaths],
fallback: staticPathsResult.fallback,
encodedPaths: [...encodedPrerenderPaths]
};
当需要时 next.js 就会使用这里的 paths 来生成对应的静态页面,从而实现动态路由的 SSG .
getStaticProps 、 getStaticPaths 相关的源码其实大部分都是在处理关于数据检查、处理这类的事情,因为这两个 API 的指责也都很简单: getStaticPaths 负责为动态路由的 SSG 场景提供页面列表, getStaticProps 则为 SSG 页面提供对应的页面数据.
最后此篇关于next.js源码解析-getStaticProps、getStaticPaths篇的文章就讲到这里了,如果你想了解更多关于next.js源码解析-getStaticProps、getStaticPaths篇的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在学习构建单页应用程序 (SPA) 所需的所有技术。总而言之,我想将我的应用程序实现为单独的层,其中前端仅使用 API Web 服务(json 通过 socket.io)与后端通信。前端基本上是
当我看到存储在我的数据库中的日期时。 这是 正常 。日期和时间就是这样。 但是当我运行 get 请求来获取数据时。 此格式与存储在数据库 中的格式不同。为什么会发生这种情况? 最佳答案 我认为您可以将
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在尝试使用backbone.js 实现一些代码 和 hogan.js (http://twitter.github.com/hogan.js/) Hogan.js was developed ag
我正在使用 Backbone.js、Node.js 和 Express.js 制作一个 Web 应用程序,并且想要添加用户功能(登录、注销、配置文件、显示内容与该用户相关)。我打算使用 Passpor
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
我尝试在 NodeJS 中加载数据,然后将其传递给 ExpressJS 以在浏览器中呈现 d3 图表。 我知道我可以通过这种方式加载数据 - https://github.com/mbostock/q
在 node.js 中,我似乎遇到了相同的 3 个文件名来描述应用程序的主要入口点: 使用 express-generator 包时,会创建一个 app.js 文件作为生成应用的主要入口点。 通过 n
最近,我有机会观看了 john papa 关于构建单页应用程序的精彩类(class)。我会喜欢的。它涉及服务器端和客户端应用程序的方方面面。 我更喜欢客户端。在他的实现过程中,papa先生在客户端有类
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我是一个图形新手,需要帮助了解各种 javascript 2D 库的功能。 . . 我从 Pixi.js 中得到了什么,而我没有从 Konva 等基于 Canvas 的库中得到什么? 我从 Konva
我正在尝试将一些 LESS 代码(通过 ember-cli-less)构建到 CSS 文件中。 1) https://almsaeedstudio.com/ AdminLTE LESS 文件2) Bo
尝试查看 Express Passport 中所有登录用户的所有 session ,并希望能够查看当前登录的用户。最好和最快的方法是什么? 我在想也许我可以在登录时执行此操作并将用户模型数据库“在线”
我有一个 React 应用程序,但我需要在组件加载完成后运行一些客户端 js。一旦渲染函数完成并加载,运行与 DOM 交互的 js 的最佳方式是什么,例如 $('div').mixItUp() 。对
请告诉我如何使用bodyparser.raw()将文件上传到express.js服务器 客户端 // ... onFilePicked(file) { const url = 'upload/a
我正在尝试从 Grunt 迁移到 Gulp。这个项目在 Grunt 下运行得很好,所以我一定是在 Gulp 中做错了什么。 除脚本外,所有其他任务均有效。我现在厌倦了添加和注释部分。 我不断收到与意外
我正在尝试更改我的网站名称。找不到可以设置标题或应用程序名称的位置。 最佳答案 您可以在 config/ 目录中创建任何文件,例如 config/app.js 包含如下内容: module.expor
经过多年的服务器端 PHP/MySQL 开发,我正在尝试探索用于构建现代 Web 应用程序的新技术。 我正在尝试对所有 JavaScript 内容进行排序,如果我理解得很好,一个有效的解决方案可以是服
我是 Nodejs 的新手。我在 route 目录中有一个 app.js 和一个 index.js。我有一个 app.use(multer....)。我还定义了 app.post('filter-re
我正在使用 angular-seed用于构建我的应用程序的模板。最初,我将所有 JavaScript 代码放入一个文件 main.js。该文件包含我的模块声明、 Controller 、指令、过滤器和
我是一名优秀的程序员,十分优秀!