预期生成静态页面但结果背离,两处问题排查

标签:nextjs, page-render-mode
分类:经验贴
3分钟阅读
概述:作者简要分享了自己在迭代博客版本中,意外发现页面生成模式不符合预期(静态生成)后,对导致问题出现的原因排查以及后续处理

背景

页面渲染模式(CSR/SSR/SSG)的基本概念可见另篇博文:区辩概念:CSR/SSR/SSG 以及 ...

NextJS 作为 React 的知名元框架,在社区收到广泛欢迎的的原因之一,在于其对 SSG(SSR) 页面渲染模式的友好支持。

而如果开发中忽略了(或不当的错误使用)一些细节,则页面的渲染模式可能背离预期。

以下简要记录两点吾辈在迭代博客版本中,发现并处理的问题。

正文

最前:可适当关注打包日志

Nextjs 默认打包构建行为下,打包日志中包含关于渲染模式的必要提示如下(以本博客项目的打包日志为例,摘选部分日志片段):

Route (app) Size First Load JS
┌ ○ /
├ ○ /\_not-found
├ ○ /about
├ ℇ /api/favicon
├ ℇ /api/reactions
├ ○ /blog
├ ● /blog/[slug]
├ ├ /blog/a6-frontend-code-quality
├ ├ /blog/a4-light-i18n
├ ├ /blog/a5-req-encapsulation
├ └ [+6 more paths]

○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses getStaticProps)
λ (Dynamic) server-rendered on demand using Node.js
ℇ (Edge Runtime) server-rendered on demand using the Edge Runtime

对于典型的静态博客站点,尽量多的页面处于 ○ (Static) / ● (SSG) 模式为佳。

SSR(SSG) 意外转为 CSR 模式 !?

吾辈原先一直忽略了对打包日志的关注,错过了关于渲染模式的预警信息,导致在博客部署上线后,前期的很长一段时间内,站点下的所有页面都实际被意外转为了 CSR (客户端渲染)模式,这无疑对客户端施加了更多来自于根据 JS 渲染页面的压力,(这也是可能导致在前期一些博文页面跳转过程中,排除网络波动不稳定原因后,加载时间过长的可能原因之一),

来看打包日志中给出的预警信息:

 ⚠ Entire page /about deopted into client-side rendering.
 https://nextjs.org/docs/messages/deopted-into-client-rendering /about

在官方文档 Entire page deopted into client-side rendering 中简要提及了原因:"If a route is statically rendered, calling useSearchParams() will cause the tree up to the closest Suspense boundary to be client-side rendered. ..."

而吾辈这里一时卡壳,因为搜索整个项目中,并没有一处使用到 useSearchParams

接下来就是具体的问题排查:

问题排查

显然的,项目代码本身没有调用 useSearchParams ,那么意外的调用情况(大概率)就发生在第三方依赖(的内部代码)中,

而对 useSearchParams 的调用动机,显然是和页面路由(或者说:url 路径信息)充分相关的,吾辈直觉思考,问题可能出现在了依赖 next13-progressbar 上。

next13-progressbar其主要功能是:在页面跳转间加上进度条效果,以缓解用户的等待焦虑,以此来必要提升 UX 体验)

所以吾辈产生这样的直觉,原因就在于:该依赖提供的功能,密切相关对 url 路径信息的处理,也就有着(意外)调用useSearchParams的动机。

随后吾辈去查看next13-progressbar源码,果然有调用 useSearchParams 方法。

问题解决

之后就是常规的按照 NextJS 官方文档的建议来解决问题:借助 React.Suspense 来包裹特定组件。

ProgressBar.tsx

通过上面的处理,最终解决了“重大的渲染异常问题:Entire page deopted into client-side rendering”。

“动态路由”预先生成, #generateStaticParams 的必要使用

NextJS 基于文件系统的页面路由模式,极大的提升了开发者声明路由页面的开发体验,在最新 App Router 模式下,以本博客项目的源代码为例,目录结构如下:

├ /blog/[slug]/ (目录)
├ ├ page.tsx
├ ├ layout.tsx
├ /blog/(idx)/ (目录)
├ ├ page.tsx (负责呈现第 1 页)
├ ├ layout.tsx
├ ├ p/[pagenum]/ (目录)
├ ├ ├ page.tsx (负责呈现第 2 、3、 4... 页)(手动排除第 1 页)

特别的,对于如 [slug] 这样的不确定路由,如果不使用官方功能函数generateStaticParams,来提前指定好动态路由可能涉及的路由字段(如此才能转为 SSG 模式),

⚠ 那么该动态路由关联的页面会实际转为:依赖服务端(随单次请求)按需生成页面的渲染模式。

👉🏻 关于动态路由的官方详实文档:Dynamic Routes

来直观的看打包日志:

未提前(通过 #generateStaticParams)指定动态路由:

├ λ /blog/[slug]

λ  (Dynamic)       server-rendered on demand using Node.js

提前指定动态路由;

├ ● /blog/[slug]
├   ├ /blog/a6-frontend-code-quality
├   ├ /blog/a4-light-i18n
├   └ [+6 more paths]

(SSG)           prerendered as static HTML (uses getStaticProps)

(略) 一些补充

预期生成静态页面但结果背离,两处问题排查

https://blog.ninoh.cc/loc-blog/16_nextjs-usage-some-details-1[Copy]
本文作者
ninohx96
创建/发布于
Published On
更新/发布于
Updated On
许可协议
CC BY-NC-SA 4.0

转载或引用本文时请遵守“署名-非商业性使用-相同方式共享 4.0 国际”许可协议,注明出处、不得用于商业用途!分发衍生作品时必须采用相同的许可协议。