跳到内容
返回博客

2019年2月11日,星期一

Next.js 8

发布者

我们今天很荣幸推出生产就绪的 Next.js 8,其特性包括:

一如既往,我们努力确保所有这些优势都完全向后兼容。对于大多数 Next.js 应用程序,您只需运行:

终端
npm i next@latest react@latest react-dom@latest

我们感谢我们的社区以及所有相信我们能成功的人。自上一篇博客文章以来,我们看到像 AT&T星巴克Twitch 等公司使用 Next.js 重新推出了其面向公众的网站和应用程序。

无服务器 Next.js

Next.js 无服务器目标从页面输出无服务器函数

无服务器部署通过将应用程序拆分为更小的部分(也称为 lambda 函数)来显著提高可靠性和可伸缩性。在 Next.js 的情况下,`pages` 目录中的每个页面都成为一个无服务器 lambda。

无服务器有许多好处。所引用的链接谈到了其中一些在 Express 环境下的好处,但其原则普遍适用:无服务器允许分布式故障点、无限可伸缩性,并且以“按需付费”模式实现难以置信的经济实惠。

要在 Next.js 中启用无服务器模式,请在 `next.config.js` 中添加 `serverless` 构建 目标

next.config.js
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;

Next.js 为无服务器部署提供低级 API,因为托管平台具有不同的函数签名。通常,您会希望使用兼容层包装 Next.js 无服务器构建的输出。

例如,如果平台支持 Node.js 的 http.Server

服务器.js
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://:3000'));

总结

  • 实现无服务器部署的低级 API
  • pages 目录中的每个页面都变成一个无服务器函数(lambda)
  • 创建尽可能小的无服务器函数(基本 zip 大小为 50 KB
  • 优化函数快速冷启动
  • 无服务器函数没有依赖项(它们包含在函数包中)
  • 使用 Node.js 的 http.IncomingMessagehttp.ServerResponse
  • 通过在 `next.config.js` 中设置 `target: 'serverless'` 来选择启用
  • server 目标仍然得到全面支持和维护
  • publicRuntimeConfigserverRuntimeConfigserverless 模式下不受支持。请改用构建时配置。

大幅减少构建时内存使用

我们为 webpack 做出了贡献,以改进 Next.js(以及 webpack 生态系统的其余部分!)的构建性能和资源利用。

这项工作使内存使用量提高了 16 倍,且性能没有下降

内存释放速度更快,进程在大量压力(许多页面)下不再崩溃。

我们很快将深入探讨如何实现此优化。请继续关注Next.js 博客

构建时环境配置

在审查 Next.js 应用程序时,我们经常观察到的一个重复出现的模式是添加 babel-plugin-transform-definewebpack.DefinePlugin 来为应用程序提供配置值。

Next.js 8 引入了 next.config.js 中名为 env 的新键,以向后兼容的方式提供相同的功能

next.config.js
module.exports = {
  env: {
    customKey: 'MyValue',
  },
};

这将允许您在代码中使用 process.env.customKey。例如

pages/index.js
export default function IndexPage() {
  return <h1>The value of customKey is: {process.env.customKey}</h1>;
}

process.env.customKey 将在构建时被替换为 'MyValue'

预取性能改进

Next.js 路由器允许您预取页面以加快导航速度

pages/index.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.08.0变化
文档大小(服务器渲染)1.50KB1.16KB缩小 23%

我们减少大小的主要方法是

  • 页面初始化内联脚本已移除
  • /_error 页面不再包含在每次页面加载中

/_error 的按需加载

每当生产中发生错误时,/_error 页面都会呈现以显示错误。

自 Next.js 首次发布以来,/_error 页面脚本标签一直是初始 HTML 的一部分,这意味着即使在没有运行时错误的情况下不会使用它,它也会被加载。

从 Next.js 8 开始,当发生错误时,/_error 页面将按需加载。

这意味着默认情况下需要加载、解析和执行的代码更少。

DX 改进

Next.js 的主要目标之一是提供最佳的生产性能和最佳的开发者体验。此版本包含许多基于用户反馈的细微改进。

改进的按需条目

开箱即用,Next.js 仅自动编译正在积极开发的页面。每次运行 next dev 时,Next.js 不会编译 pages 目录中的所有页面。相反,它会根据您访问页面的时间按需编译页面。

例如,当访问 https://:3000/my-page 时,pages/my-page.js 文件会按需编译,之后页面会渲染。

这确保了开发人员在启动开发服务器时不必等待所有页面编译完成,这在大型应用程序中可能需要相当长的时间。它保持内存使用率低,并且编译器快速,因为编译器在捆绑时不需要考虑所有页面。

The on-demand entries flow
按需条目流程

当一个页面在 25 秒内没有被访问时,它将从编译器的构建缓存中清除,以保持编译器快速并减少内存使用。

Next.js 跟踪页面访问的方式是使用轮询机制。每 5 秒,会发送一个“on-demand-entries-ping”以使 Next.js 开发服务器知道正在访问给定页面。

自此功能首次发布以来,ping 是使用 window.fetch 调用完成的,这意味着每次触发 ping 时,它都会显示在浏览器开发工具的 consolenetwork 选项卡中。

最受欢迎的功能之一是能够从浏览器开发人员工具中隐藏这些请求,因为这些请求可能会增加不必要的干扰。

我们很高兴地宣布,在 Next.js 8 中,基于 fetch 的 ping 已被基于 WebSockets 的方法取代,这意味着 ping 仍然发生,但只有在检查 WebSocket 连接时才可见。

特别感谢 JJ Kasper 在转换为 WebSockets 方面的合作。

开发中更快的端口监听

当启动 Next.js 开发服务器时,它必须运行一些初始编译才能处理请求。默认情况下,Next.js 会等到此编译步骤完成后才启动 HTTP 服务器,这意味着如果您运行 next dev 然后转到浏览器,有时可能会收到“此网站无法访问”的消息,因为 HTTP 服务器尚未监听连接。

在 Next.js 8 中,HTTP 服务器将在编译开始之前监听连接,这意味着如果您在编译完成之前访问 https://: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> 的更改

pages/index.js
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" 时,只渲染最后一个标签。

pages/index.js
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 中引入了一个新的配置选项

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 名成员加入我们!