在 Next.js 9 Release 之后最受关注的是动态路由,它是来自社区的第一个 Feature 提案,新的动态路由系统用很好的方式回答了系统文件映射路由的解决方案如何处理动态路由的问题。尽管这些 Rails 风格的路由系统看起来很好用,但对于服务端渲染框架来说动态路由也引入了新问题,比如在所有动态页面中规划 SSG 与 SSR,在 9.3 版本发布之后,Next.js 给出了新的答案。

我们知道 SSR (server-side rendering / 服务端渲染) 框架的内容渲染是在每次请求之后,由服务端生成一个最小可用页面,这是确保 SEO 和首屏体验的重要方式,由于 TCP 的 slow start 与物理延迟等原因,SSR 仍旧在首屏体验上远超 SPA 应用,关于这一点你可以参阅 构建 Web 应用 7 要素 这篇文章。但 SSR 应用无法充分的利用首屏缓存,比如应用动态页面中的一部分是根据用户分发的私有信息,它们必须在服务端从持久化存储服务中检索,这会使所有的页面,即便是那些首屏需要缓存的公共静态页面都丢失 SPA (Single Page Applications / 单页面应用) 带来的优势。在 Next.js 9 中,一个被称为 Auto SSG 的方案解决这个问题。

Auto SSG (Automatic Static Site Generation / 自动静态站点生成) 不是我们熟知的 next export,新的方案不会把整站导出为静态文件,而是针对站点中一部分不需要服务端渲染的页面导出静态文件,在余下的部分我们仍旧使用高体验的 SSR。这意味着我们的单个应用也是 混合构建 (hybrid built),由开发者自己分配哪些页面需要 SSR 或是 SSG,这带来的进步和体验是难以想象的。从技术实现上来看,Next.js 总是把没有要求 getInitialProps 数据预获取的页面标记为静态的,这给我们省去了极大的工作量,现在看来也是合理的。但我们知道动态路由在共用一个页面文件,那么在动态路由的页面文件中,要么我们使用 getInitialProps 标记所有 [params] 为动态的,要么所有的 [params] 页面都是静态的,只能选择一种,这是混合渲染在动态路由系统中遇到的新问题。

在大型的电商或营销系统中,即便是处于同一个 url pathname 页面,也有可能需要完全不同的数据策略,比如 /foods/apple 正在面向所有用户,此页面可以受到缓存保持长时间不变化,但 /foods/organic 是一个新推出的产品,希望能够在一定的维度上进行灰度试验,通常的做法是由 SPA 的客户端向服务端发送检查来确认当前用户可以浏览页面的哪些内容,如果你想要更好的优化,SSR 会在服务端预渲染之前拼接页面内容,但在 /foods/[params] 文件中添加 getInitialProps 会让整个动态路由转变为服务端渲染,这不是你想要的。你可能要更多的 hack 来解决这个问题。Next.js 9.3 之后,为了解决此类问题,将数据的预获取分为三个部分:

现在我们可以在动态路由的页面中指定 getStaticPaths 返回哪些 paths,从而精细的控制自己的动态页面使用哪种方式展示。这里是 更详细的文档

为了保持 API 的一致性和扩展静态能力,Next.js 也增加了 getStaticProps 纯静态页面在构建时的数据问题。这些 9.3 以后带来的变化在我看来是非常了不起的,这些 API 让 Next.js 成为了一个名副其实的混合构建框架。

在通常场景中,我们构建一个包含指定内容的静态页面有两种选择,一种是 SPA 式的,你需要在 webpack / rollup 等构建工具运行时从持久化存储服务中取得数据,再分发给每个页面或是存储为结构化可缓存的公共文件,一种是 SSR 式,在服务端对每个请求进行查询分发。目前看来这两种方式或是包括传统模板渲染的解决方案,都会面临我上文所说的困境,同时开发体验也是低下的。但 Next.js 9.3+ 给了我们一个非常巧妙的答案,至少在当下看,仍旧是最优的解决方案。

值得关注的是这些工作都是对 CMS 解决方案的一种改良,以优雅的方式解决静态与动态最大的受益者是营销站点、广告、媒体等 Web 应用,从 Now 去年移除掉非常多体验不那么友好的 Serverless 语言支持来看,这些领域将是他们未来的主要客户,在对原来横向扩展过多的 Serverless 语言、域名等服务进行收敛之后,似乎找到了领域内的发展方向,Next.js 也非常出色的完成企业内部的技术驱动任务。