2019年2月11日,星期一
Next.js 8
发布者今天,我们很自豪地推出生产就绪的 Next.js 8,其特点包括
- Serverless Next.js
- 大幅减少构建时内存使用量
- 构建时环境配置
- 预取性能改进
- 更小的初始 HTML 大小
- 改进的按需条目
- 更快的开发端口监听
- 更快的静态导出
- Head 元素去重
- 新的 crossOrigin 配置选项
- 移除内联 Javascript
- API 身份验证示例
与往常一样,我们力求确保所有这些优势都完全向后兼容。对于大多数 Next.js 应用程序,您只需运行
npm i next@latest react@latest react-dom@latest
我们感谢我们的社区和所有信任我们成功的人。自上次发布博客文章以来,我们已经看到 AT&T、Starbucks 和 Twitch 等公司使用 Next.js 重新发布其面向公众的网站和应用程序。
Serverless Next.js
Serverless 部署通过将您的应用程序拆分为更小的部分(也称为 lambda 函数)来显着提高可靠性和可扩展性。对于 Next.js 而言,pages
目录中的每个页面都成为一个 serverless lambda 函数。
Serverless 有 许多优点。引用的链接讨论了其中一些在 Express 上下文中的应用,但这些原则普遍适用:serverless 允许分布式故障点、无限的可扩展性,并且采用“按使用量付费”模式,因此非常经济实惠。
要在 Next.js 中启用 serverless 模式,请在 next.config.js
中添加 serverless
构建 target
module.exports = {
target: 'serverless',
};
serverless
目标将为每个页面输出一个 lambda 函数。此文件是完全独立的,无需任何依赖项即可运行
pages/index.js
=>.next/serverless/pages/index.js
pages/about.js
=>.next/serverless/pages/about.js
Next.js Serverless 函数的签名类似于 Node.js HTTP 服务器回调
type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;
- http.IncomingMessage
- http.ServerResponse
void
指的是函数没有返回值,等同于 JavaScript 的undefined
。调用该函数将完成请求。
Next.js 为 serverless 部署提供了底层 API,因为托管平台具有不同的函数签名。一般来说,您会希望使用兼容性层来包装 Next.js serverless 构建的输出。
例如,如果平台支持 Node.js http.Server 类
const http = require('http');
const page = require('./.next/serverless/about.js');
const server = new http.Server((req, res) => page.render(req, res));
server.listen(3000, () => console.log('Listening on https://127.0.0.1:3000'));
总结
- 用于实现 serverless 部署的底层 API
pages
目录中的每个页面都变成一个 serverless 函数 (lambda)- 创建尽可能小的 serverless 函数(50 KB 基础 zip 大小)
- 针对函数的快速 冷启动进行了优化
- serverless 函数具有 0 个依赖项(它们包含在函数捆绑包中)
- 使用 Node.js 中的 http.IncomingMessage 和 http.ServerResponse
- 在
next.config.js
中选择使用target: 'serverless'
server
目标仍然完全受支持和维护publicRuntimeConfig
和serverRuntimeConfig
在serverless
模式下不受支持。请改用构建时配置。
大幅减少构建时内存使用量
我们为 webpack 贡献了代码,以提高 Next.js(以及 webpack 生态系统的其余部分!)的构建性能和资源利用率。
这项努力使得内存使用量提高了 16 倍,而性能没有任何下降。
内存释放速度更快,并且在大量压力(许多页面)下进程不再崩溃。
我们很快将深入探讨我们是如何实现这种优化的。请密切关注 Next.js 博客。
构建时环境配置
在审查 Next.js 应用程序时,我们观察到一个经常出现的模式是添加 babel-plugin-transform-define
或 webpack.DefinePlugin
以向应用程序提供配置值。
借助 Next.js 8,我们在 next.config.js
中引入了一个名为 env
的新键,以向后兼容的方式提供相同的功能
module.exports = {
env: {
customKey: 'MyValue',
},
};
这将允许您在代码中使用 process.env.customKey
。例如
export default function IndexPage() {
return <h1>The value of customKey is: {process.env.customKey}</h1>;
}
process.env.customKey
将在构建时替换为 'MyValue'
。
预取性能改进
Next.js 路由器允许您预取页面以加快导航速度
import Link from 'next/link';
export default function IndexPage() {
return (
<>
<Link href="/about" prefetch>
<a>To About Page</a>
</Link>
</>
);
}
它的工作原理是预取每个具有 prefetch
属性的链接的 Javascript 捆绑包。
在 Next.js 8 之前的版本中,这意味着将 <script>
标签注入到文档 <body>
中。
但是,这在打开页面时引入了一些开销,最明显的是浏览器“加载”指示会比您预期的显示时间更长,即使页面已经可以交互了。
在 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
页面在发生错误时按需加载。
这意味着默认情况下需要加载、解析和执行的代码更少。
DX 改进
Next.js 的主要目标之一是以最佳的开发者体验提供最佳的生产性能。此版本包含许多基于用户反馈的细微改进。
改进的按需条目
开箱即用,Next.js 自动仅编译正在积极开发的页面。Next.js 不会在每次运行 next dev
时都编译 pages 目录中的所有页面。相反,它会在您访问页面时编译页面。
例如,当访问 https://127.0.0.1:3000/my-page
时,pages/my-page.js
文件会按需编译,然后渲染页面。
这确保了开发人员在启动开发服务器时不必等待所有页面编译完成,这在较大的应用程序上可能需要相当长的时间。它保持较低的内存使用率并加快了编译器的速度,因为编译器在捆绑时不需要考虑所有页面。

当页面 25 秒未被访问时,它将从编译器的构建缓存中移除,以保持编译器的快速并减少内存使用量。
Next.js 跟踪页面访问的方式是使用轮询机制。每隔 5 秒,就会发送一个“on-demand-entries-ping”以使 Next.js 开发服务器知道正在访问给定的页面。
自此功能首次发布以来,ping 操作是使用 window.fetch
调用完成的,这意味着每次触发 ping 操作时,它都会显示在浏览器开发工具的 console
和 network
选项卡上。
最受欢迎的功能之一是能够从浏览器开发者工具中隐藏这些请求,因为这些请求会增加不必要的噪音。
我们很高兴地宣布,在 Next.js 8 中,基于 fetch
的 ping 已被基于 WebSockets 的方法取代,这意味着 ping 操作仍然会发生,但仅在检查 WebSocket 连接时才可见。
特别感谢 JJ Kasper 在转换为 WebSockets 方面的合作。
更快的开发端口监听
启动 Next.js 开发服务器时,它必须运行一些初始编译才能处理请求,默认情况下,Next.js 会等待此编译步骤完成后再启动 HTTP 服务器,这意味着如果您运行 next dev
然后转到浏览器,有时可能会收到“无法访问此站点”消息,因为 HTTP 服务器尚未监听连接。
借助 Next.js 8,HTTP 将在编译开始之前监听连接,这意味着如果您在编译完成之前访问 https://127.0.0.1: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>
的更改
import Head from 'next/head';
export default function IndexPage() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
);
}
<Head>
组件甚至可以在不同的组件中多次使用,例如,您的布局组件可以设置一些默认的 head 标签。
但是,您可能想要使用不同的值覆盖默认的 head 标签,在旧版本的 Next.js 中,这将导致标签在输出中重复,因为无法对标签进行去重。
因此,现在可以为 <Head>
组件内的每个元素提供 key
属性,这将自动对具有相同 key
值的标签进行去重。
当在两个标签上设置 key="viewport"
时,仅渲染最后一个标签。
import Head from 'next/head';
export default function IndexPage() {
return (
<>
<Head>
<title>My page title</title>
<meta
name="viewport"
content="initial-scale=1.0, width=device-width"
key="viewport"
/>
</Head>
<Head>
<meta
name="viewport"
content="initial-scale=1.2, width=device-width"
key="viewport"
/>
</Head>
</>
);
}
安全改进
新的 crossOrigin
配置选项
在 Next.js 6 中,我们引入了向 pages/_document.js
中的 <Head>
和 <NextScript>
添加 crossOrigin
属性的选项,但这并未涵盖设置 cross-origin
的所有用例。
Next.js 有一个客户端路由器,可以动态注入 <script>
标签,这些标签在注入时缺少 cross-origin
属性。
为了确保所有 <script>
标签都设置了 cross-origin
,我们在 next.config.js
中引入了一个新的配置选项
module.exports = {
crossOrigin: 'anonymous',
};
引入此选项的另一个好处是,不再需要自定义 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 次 commit。
- 在 GitHub 上,该项目已获得超过 34,400 次 star。
- 自首次发布以来,已提交了超过 2600 个 pull request。
Next.js 社区拥有超过 4,570 名成员。 加入我们!