跳至内容
返回博客

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

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

由于托管平台的函数签名不同,Next.js 为无服务器部署提供了低级 API。通常,您需要使用兼容性层包装 Next.js 无服务器构建的输出。

例如,如果平台支持 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'));

**摘要**

  • 用于实现无服务器部署的低级 API
  • pages 目录中的每个页面都成为一个无服务器函数(lambda)
  • 创建尽可能小的无服务器函数(**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.16KB23% 更小

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

  • 页面初始化内联脚本已删除
  • /_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文件会按需编译,然后渲染页面。

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

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合作将 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>的更改。

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

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