今天我们自豪地推出生产就绪的 Next.js 8,主要特性包括:
- 无服务器 Next.js
- 大幅减少构建时内存占用
- 构建时环境配置
- 预取性能优化
- 更小的初始 HTML 体积
- 改进的按需条目加载
- 开发模式下更快的端口监听
- 更快的静态导出
- Head 元素去重
- 新增 crossOrigin 配置选项
- 移除内联 JavaScript
- API 认证示例
一如既往,我们确保所有这些改进都完全向后兼容。对于大多数 Next.js 应用,只需运行:
我们感谢社区和所有支持我们的人。自上次博客发布以来,我们看到 AT&T、星巴克 和 Twitch 等公司使用 Next.js 重构了他们的对外网站和应用。
无服务器 Next.js
Next.js 的无服务器构建目标会将页面输出为无服务器函数 (Serverless Functions)
无服务器部署通过将应用拆分为更小的部分(也称为 lambdas)显著提高了可靠性和可扩展性。在 Next.js 中,pages
目录下的每个页面都会成为一个无服务器函数。
无服务器架构有诸多优势。虽然参考链接中讨论的是 Express 场景,但这些原则具有普适性:无服务器架构支持分布式故障点、无限扩展,并且采用"按使用付费"模式非常经济。
要在 Next.js 中启用无服务器模式,在 next.config.js
中添加 serverless
构建目标:
serverless
目标会为每个页面输出独立的 lambda 函数。这些文件完全独立,无需任何依赖即可运行:
pages/index.js
=>.next/serverless/pages/index.js
pages/about.js
=>.next/serverless/pages/about.js
Next.js 无服务器函数的签名与 Node.js HTTP 服务器回调类似:
- http.IncomingMessage
- http.ServerResponse
void
表示函数没有返回值,等同于 JavaScript 的undefined
。调用函数将完成请求处理。
Next.js 为无服务器部署提供了底层 API,因为不同托管平台的函数签名各异。通常你需要用兼容层包装 Next.js 无服务器构建的输出。
例如,如果平台支持 Node.js 的 http.Server 类:
总结
- 提供实现无服务器部署的底层 API
pages
目录下的每个页面都会成为无服务器函数 (lambda)- 生成尽可能小的无服务器函数(基础 zip 包仅 50 KB)
- 针对函数的快速冷启动进行了优化
- 无服务器函数零依赖(所有依赖已打包进函数)
- 使用 Node.js 的 http.IncomingMessage 和 http.ServerResponse
- 通过
next.config.js
中的target: 'serverless'
启用 - 仍完全支持并维护
server
目标 - 无服务器模式下不支持
publicRuntimeConfig
和serverRuntimeConfig
,请改用构建时配置
大幅减少构建时内存占用
我们为 webpack 贡献了代码,以改进 Next.js(以及整个 webpack 生态)的构建性能和资源利用率。
这些优化使得内存使用量最高可减少 16 倍,且性能不受影响。
内存释放更快,在多页面场景下进程也不再崩溃。
我们将在 Next.js 博客详细介绍这些优化实现。
构建时环境配置
在审查 Next.js 应用时,我们经常发现开发者会添加 babel-plugin-transform-define
或 webpack.DefinePlugin
来为应用提供配置值。
Next.js 8 在 next.config.js
中引入了新的 env
键,以向后兼容的方式提供相同功能:
这允许你在代码中使用 process.env.customKey
。例如:
process.env.customKey
会在构建时被替换为 'MyValue'
。
预取性能优化
Next.js 路由允许预取页面以实现更快导航:
其工作原理是预取带有 prefetch
属性的链接对应的 JavaScript 包。
在 Next.js 8 之前版本中,这会通过向文档 <body>
注入 <script>
标签实现。
但这会导致页面打开时产生额外开销,最明显的是即使页面已可交互,浏览器"加载"指示仍会显示较长时间。
在 Next.js 8 中,prefetch
改用 <link rel="preload">
而非 <script>
标签,并且仅在 onload
后开始预取,以便浏览器管理资源。
此外,Next.js 现在会检测 2G 网络和 navigator.connection.saveData
模式,在慢速网络连接下禁用预取。
更小的初始 HTML 体积
由于 Next.js 预渲染 HTML,它会将页面包裹在包含 <html>
、<head>
、<body>
和渲染页面所需 JavaScript 文件的默认结构中。
Next.js 7 我们将初始负载优化至 1.50KB,比前一版本减少了 7.4%。
现在我们进一步将初始负载减小至 1.16KB,再次减少 23%:
7.0 | 8.0 | 差异 | |
---|---|---|---|
文档大小(服务端渲染) | 1.50KB | 1.16KB | 23% 减小 |
主要优化方式包括:
- 移除了页面初始化内联脚本
- 不再在每次页面加载时包含
/_error
页面
按需加载 /_error
当生产环境发生错误时,会渲染 /_error
页面显示错误信息。
自 Next.js 首个版本起,/_error
页面的脚本标签就是初始 HTML 的一部分,意味着即使没有运行时错误也会加载。
从 Next.js 8 开始,/_error
页面会在错误发生时按需加载。
这意味着默认情况下需要加载、解析和执行的代码更少。
开发者体验改进
Next.js 的主要目标之一是在提供最佳生产性能的同时,提供最佳的开发者体验。此版本包含许多基于用户反馈的细微改进。
改进的按需条目加载
开箱即用,Next.js 会自动编译仅正在开发的页面。运行 next dev
时,Next.js 不会每次都编译 pages 目录中的所有页面,而是按需编译。
例如,当访问 http://localhost:3000/my-page
时,pages/my-page.js
文件会被按需编译,然后渲染页面。
这确保开发者在启动开发服务器时无需等待所有页面编译完成,这在大型应用中可能耗时较长。它保持内存使用量低且编译器快速,因为打包时编译器不需要考虑所有页面。
按需条目加载流程
当页面超过 25 秒未被访问时,它会被从编译器的构建缓存中清除,以保持编译器快速并减少内存使用。
Next.js 通过轮询机制跟踪页面访问。每 5 秒发送一次"on-demand-entries-ping",让 Next.js 开发服务器知道某个页面正在被访问。
自该功能首次发布以来,轮询是通过 window.fetch
调用实现的,意味着每次触发轮询时,都会在浏览器开发者工具的 console
和 network
标签中显示。
最常被请求的功能之一就是隐藏这些请求,因为它们会带来不必要的干扰。
我们很高兴地宣布,在 Next.js 8 中,基于 fetch
的轮询已被 WebSocket 方案取代,意味着轮询仍在进行,但仅在检查 WebSocket 连接时可见。
特别感谢 JJ Kasper 协助完成向 WebSocket 的转换。
开发模式下更快的端口监听
启动 Next.js 开发服务器时,需要运行一些初始编译才能处理请求。默认情况下,Next.js 会等待此编译步骤完成后再启动 HTTP 服务器,意味着如果你运行 next dev
后立即打开浏览器,有时可能会看到"无法访问此网站"的消息,因为 HTTP 服务器尚未开始监听连接。
在 Next.js 8 中,HTTP 服务器会在编译开始前就开始监听连接,意味着如果你在编译完成前访问 http://localhost:3000/
,请求会等待初始编译完成后再响应,而不需要反复刷新页面直到可用。
特别感谢 Brian Beck 实现此功能。
更快的静态导出
Next.js 专注于预渲染作为实现高性能的手段。预渲染有两种形式:
- 服务端渲染:每个请求触发一次渲染。最终用户无需等待任何 JS 下载即可开始消费数据
- 静态渲染:输出可直接服务的静态文件,无需在服务器上执行任何代码
从 Next.js 8 开始,如果你的机器有多个 CPU,通过 next export
进行的静态渲染速度会更快。
基于 4 核 MacBook 的测试显示,通过利用所有核心预渲染页面,导出速度从每秒约 25 页提升至每秒 75 页。
Next.js 会自动检测 CPU 核心数并相应分配页面,无需任何代码变更。
特别感谢 Benjamin Kniffler 实现此功能。
Head 元素去重
构建应用时的常见需求是更新页面的 <head>
元素。例如设置响应式设计的 <title>
或 <meta name="viewport">
。
Next.js 提供了内置组件来修改 <head>
:
<Head>
组件甚至可以在不同组件中多次使用,例如你的布局组件可以设置一些默认的 head 标签。
但你可能想用不同的值覆盖默认 head 标签,在旧版 Next.js 中这会导致输出中出现重复标签,因为无法去重。
因此,现在可以为 <Head>
组件中的每个元素提供 key
属性,这将自动去重具有相同 key
值的标签。
当两个标签都设置 key="viewport"
时,只有最后一个会被渲染。
安全性改进
新增 crossOrigin
配置选项
在 Next.js 6 中,我们引入了在 pages/_document.js
中为 <Head>
和 <NextScript>
添加 crossOrigin
属性的选项,但这并未涵盖设置 cross-origin
的所有用例。
Next.js 有一个客户端路由,会动态注入 <script>
标签,这些标签在注入时缺少 cross-origin
属性。
为确保所有 <script>
标签都设置了 cross-origin
属性,我们在 next.config.js
中引入了新的配置选项:
引入此选项的另一个好处是,你的应用不再需要自定义 pages/_document.js
来设置 cross-origin
。
旧行为仍受支持,但会在开发时发出警告,以帮助开发者迁移到新引入的选项。
移除内联 JavaScript
在使用 Next.js 7 及以下版本时,为了启用 内容安全策略 (CSP),用户必须在策略中包含 script-src 'unsafe-inline'
,因为 Next.js 会创建一个内联的 <script>
标签来传递数据,例如将 getInitialProps
的结果传递到客户端。
从 Next.js 8 开始,我们将这个内联脚本标签改为了 JSON 标签,以便安全地传输到客户端。这意味着 Next.js 不再包含任何内联脚本。
经过慎重考虑,现在可以使用 script-src 'self'
了。
API 认证示例
最常被请求的示例之一是如何在 Next.js 中针对外部 API(任何编程语言的任何 API)进行认证。
随着 Next.js 8 的发布,我们还引入了一个新创建的示例:with-cookie-auth
这个示例展示了如何针对一个外部的 Node.js API 进行认证,但所应用的概念适用于任何无状态的 API。
该示例使用 cookie 在服务端渲染和客户端渲染之间共享令牌。
这样,如果应用在服务端渲染,它仍然可以代表用户获取认证数据。
特别感谢贡献此示例的 Juan Olvera。
社区
自首次发布以来,Next.js 已被用于从财富 500 强公司到个人博客的各类场景。我们非常高兴看到 Next.js 的采用率持续增长。
- 已有超过 600 名贡献者 提交了至少 1 次提交。
- 在 GitHub 上,该项目已获得超过 34,400 次星标。
- 自首次发布以来,已提交了超过 2600 个拉取请求。
Next.js 社区拥有超过 4,570 名成员。加入我们吧!