2019年2月11日,星期一
Next.js 8
发布者我们今天很自豪地推出生产就绪的 Next.js 8,其特性包括
- 无服务器 Next.js
- 大幅减少构建时内存使用
- 构建时环境配置
- 预取性能改进
- 更小的初始 HTML 大小
- 改进的按需加载条目
- 开发环境中更快的端口监听
- 更快的静态导出
- Head 元素去重
- 新的 crossOrigin 配置选项
- 移除内联 JavaScript
- API 身份验证示例
与往常一样,我们一直努力确保所有这些优势都**完全向后兼容**。对于大多数 Next.js 应用程序,您只需运行
npm i next@latest react@latest react-dom@latest
我们感谢我们的社区和所有相信我们成功的人。自从我们上次发布博客文章以来,我们已经看到像AT&T、星巴克和Twitch等公司使用 Next.js 重新启动其面向公众的网站和应用程序。
无服务器 Next.js
无服务器部署通过将您的应用程序拆分为更小的部分(也称为**lambda**)极大地提高了可靠性和可扩展性。在 Next.js 的情况下,pages
目录中的每个页面都成为一个无服务器 lambda。
无服务器有很多好处。引用的链接在 Express 的上下文中讨论了其中一些,但这些原理普遍适用:无服务器允许分布式故障点、无限可扩展性,并且在“按使用付费”模型下非常经济实惠。
要在 Next.js 中启用**无服务器模式**,请在 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 无服务器函数的签名类似于 Node.js HTTP 服务器回调
type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;
- http.IncomingMessage
- http.ServerResponse
void
指的是函数没有返回值,等同于 JavaScript 中的undefined
。调用该函数将结束请求。
由于托管平台的函数签名不同,Next.js 为无服务器部署提供了低级 API。通常,您需要使用兼容性层包装 Next.js 无服务器构建的输出。
例如,如果平台支持 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'));
**摘要**
- 用于实现无服务器部署的低级 API
pages
目录中的每个页面都成为一个无服务器函数(lambda)- 创建尽可能小的无服务器函数(**50 KB** 基本压缩包大小)
- 针对函数的快速冷启动进行了优化
- 无服务器函数没有依赖项(它们包含在函数捆绑包中)
- 使用 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
页面在发生错误时按需加载。
这意味着默认情况下需要加载、解析和执行的代码更少。
开发体验改进
Next.js 的主要目标之一是提供最佳的生产性能和尽可能好的开发体验。此版本包含许多基于用户反馈的细微改进。
改进的按需条目
默认情况下,Next.js 会自动仅编译正在积极开发的页面。每次运行next dev
时,Next.js 不会编译 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合作将 ping 转换为 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 核 CPU 的 MacBook 进行的测试,通过利用所有核心来预渲染页面,导出速度从大约每秒 25 个页面提高到每秒 75 个页面。
Next.js 将自动检测 CPU 内核数量并相应地分配页面,无需任何代码更改。
特别感谢Benjamin Kniffler实现了此功能。
头部元素去重
构建应用程序时,一个常见需求是更新页面的<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>
组件甚至可以多次用于不同的组件中,例如,您的布局组件可以设置一些默认的头部标签。
但是,您可能希望使用不同的值覆盖默认的头部标签,在旧版本的 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 次提交。
- 在 GitHub 上,该项目获得了超过34,400 次星标。
- 自首次发布以来,已提交超过2600 个拉取请求。
Next.js 社区拥有超过4,570 名成员。 加入我们!