跳到内容
返回博客

星期一,2019 年 2 月 11 日

Next.js 8

发布者

今天,我们很自豪地推出生产就绪的 Next.js 8,其特点包括

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

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

我们感谢我们的社区和所有对我们的成功寄予厚望的人。自从我们的上一篇博文发布以来,我们已经看到像 AT&TStarbucksTwitch 等公司使用 Next.js 重新发布其面向公众的网站和应用程序。

Serverless Next.js

Next.js serverless 目标从页面输出 Serverless 函数

Serverless 部署通过将您的应用程序拆分成更小的部分(也称为 lambdas)来显着提高可靠性和可扩展性。对于 Next.js 而言,pages 目录中的每个页面都变成了一个 serverless lambda。

Serverless 有 许多优点。引用的链接讨论了 Express 上下文中的一些优点,但这些原则普遍适用:serverless 允许分布式故障点、无限的可扩展性,并且采用“按使用量付费”模式,因此非常经济实惠。

要在 Next.js 中启用 serverless 模式,请在 next.config.js 中添加 serverless 构建 target

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 Serverless 函数的签名类似于 Node.js HTTP 服务器回调

type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;

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

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

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://127.0.0.1:3000'));

摘要

  • 用于实现 serverless 部署的底层 API
  • pages 目录中的每个页面都变成一个 serverless 函数 (lambda)
  • 创建尽可能小的 serverless 函数(50 KB 基础 zip 大小)
  • 针对函数的快速 冷启动 进行了优化
  • serverless 函数没有 0 个依赖项(它们包含在函数包中)
  • 使用 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.16KB23% 更小

我们减小尺寸的主要方法是

  • 页面初始化内联脚本已删除
  • 不再在每次页面加载时都包含 /_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 文件会按需编译,之后页面会被渲染。

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

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://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 元素去重

构建应用程序时,一个常见的需求是更新页面的 <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 次 commit。
  • 在 GitHub 上,该项目已被 star 超过 34,400 次
  • 自首次发布以来,已提交超过 2600 个 pull request

Next.js 社区拥有超过 4,570 名成员加入我们!