今天我们很高兴地推出 Next.js 9.2,主要特性包括:
- 全局样式表的原生 CSS 支持:应用现在可以直接导入
.css
文件作为全局样式表。 - 组件级样式的原生 CSS 模块支持:通过
.module.css
命名约定,可以在应用中任何位置导入和使用局部作用域的 CSS。 - 改进的代码分割策略:Google Chrome 团队深度优化了 Next.js 的代码分割策略,显著减小了客户端包体积。此外,他们还最大化利用了 HTTP/2 来提升页面加载速度,同时不影响 HTTP/1.1 的性能表现。
- 全捕获动态路由:Next.js 动态路由现在支持全捕获路由,可满足多种新用例,例如由 CMS 驱动的网站。
所有这些改进都是非破坏性的,完全向后兼容。要更新只需运行:
全局样式表的原生 CSS 支持
Next.js 5 通过名为 next-css
的自定义插件引入了 CSS 导入支持,该插件扩展了 Next.js 的行为。
随着时间的推移,我们不断收到来自企业和 Next.js 用户的一致反馈,指出他们经常需要在应用中添加 next-css
。
此外,next-css
在导入 CSS 时存在一些缺失的限制。例如,你可以在项目的每个文件中导入 CSS 文件,但导入的 CSS 文件将对整个应用全局生效。
为了提升开发者体验并解决这些不一致问题,我们开始着手为 Next.js 默认添加 CSS 导入支持。
我们很高兴地宣布,Next.js 现在原生支持将样式表导入你的应用。
要开始在你的应用中使用 CSS 导入,请在 pages/_app.js
中导入 CSS 文件。
例如,假设你的项目根目录下有一个名为 styles.css
的样式表:
如果尚未创建 pages/_app.js
文件,请先创建。
然后,导入 styles.css
文件:
由于样式表本质上是全局的,它们必须被导入到 自定义 <App>
组件 中。这对于避免全局样式的类名冲突和顺序问题很有必要。
在开发环境中,以这种方式表达样式表可以让你在编辑样式时自动更新页面上的样式。
在生产环境中,所有 CSS 文件将自动合并为一个经过压缩的 .css
文件。这个 CSS 文件将通过 <link>
标签加载,并自动注入到 Next.js 生成的默认 HTML 标记中。
这一新特性完全向后兼容。如果你正在使用 @zeit/next-css
或其他 CSS 相关插件,该特性将被禁用以避免冲突。
如果你当前正在使用 @zeit/next-css
,我们建议从 next.config.js
和 package.json
中移除该插件,从而在升级后使用内置的 CSS 支持。
组件级样式的原生 CSS 模块支持
Next.js 现在通过 [name].module.css
文件命名约定支持 CSS 模块。
与 Next.js 5 中通过 next-css
提供的支持不同,全局 CSS 和 CSS 模块现在可以 共存 —— next-css
要求应用中所有 .css
文件要么作为全局样式处理,要么作为局部样式处理,但不能同时处理。
CSS 模块通过自动创建唯一的类名来实现 CSS 的局部作用域。这允许你在不同的文件中使用相同的 CSS 类名,而不用担心冲突。
这种行为使得 CSS 模块成为包含组件级 CSS 的理想方式。CSS 模块文件 可以在应用的任何位置导入。
例如,考虑 components/
文件夹中的一个可复用 Button
组件:
首先,创建 components/Button.module.css
,内容如下:
然后,创建 components/Button.js
,导入并使用上述 CSS 文件:
CSS 模块是一个 可选 特性,仅对具有 .module.css
扩展名的文件启用。常规的 <link>
样式表 和 全局 CSS 文件 仍然受支持。
在生产环境中,所有 CSS 模块文件会自动合并为 多个经过压缩和代码分割的 .css
文件。这些 .css
文件代表了应用中的热执行路径,确保为应用的每个页面加载最少数量的 CSS 以实现绘制。
与上述相同,这一新特性完全向后兼容。如果你正在使用 @zeit/next-css
或其他 CSS 相关插件,该特性将被禁用以避免冲突。
如果你当前正在使用 @zeit/next-css
,我们建议从 next.config.js
和 package.json
中移除该插件,从而使用内置的 CSS 支持。
改进的代码分割策略
Next.js 9.2 之前的版本有一组固定的 JavaScript 包需要加载以使页面具有交互性:
- 页面的 JavaScript 文件
- 包含公共 JavaScript 的文件
- Next.js 客户端运行时包
- Webpack 客户端运行时包
- 动态导入(通过
next/dynamic
添加,使用时)
为了使页面具有交互性,所有这些包都必须加载,因为它们相互依赖以在浏览器中启动 React。
由于所有这些包都是应用具有交互性所必需的,因此尽可能优化它们非常重要。在实践中,这意味着不过度下载应用中其他部分的代码。
出于这个原因,Next.js 使用了一个 commons
包,其中包含页面之间的公共 JavaScript。旧的包分割策略生成 commons
的计算基于一个使用率启发式算法。如果一个模块在超过 50% 的页面中使用,它将被标记为公共模块。否则,它会被打包到页面的 JavaScript 文件中。
然而,应用可能包含许多不同类型的页面。例如,营销页面、博客和仪表盘。如果营销页面的数量比其他类型的页面多得多,公共模块的计算结果将主要针对营销页面进行优化。
我们的目标是在一个应用中优化所有类型的页面。
Alex Castle 提出了一种新的分块方法(创建独立的 JavaScript 文件),允许在涉及多种页面类型时,通过多个文件进行优化的公共分块。
今天,我们很高兴地宣布,这种新的分块行为在 Next.js 9.2 中默认启用。我们要向 Google Chrome 团队 和 Alex Castle 表示深深的感谢,感谢他们贡献这一变更。这一变更反映了数周的研究、实验室测试、实际测试和实施的累积努力。
新的分块实现利用了 HTTP/2 来交付更多数量的小体积分块。
在新的启发式算法下,分块会为以下内容创建:
- 每个页面的最小分块。
- 包含 React、ReactDOM、React 的调度器等内容的框架分块。
- 任何超过 160kb(压缩前)的
node_module
依赖项的库分块。 - 用于所有页面共享代码的公共分块。
- 尽可能多的共享分块(被 2 个或更多页面使用),以优化整体应用大小和初始加载速度。
- Next.js 的客户端运行时。
- Webpack 运行时。
让我们看看这在实际应用中的意义:
早期采用者行业合作伙伴 Barnebys® 的整体应用大小减少了 23%。
此外,他们最大的 JS 包减少了 30% —— 从 605kB 降至 425kB —— 无需任何代码更改。
另一个行业合作伙伴 SumUp® 的最大 JS 包减少了 70% —— 从 395kB 降至 122kB —— 无需任何代码更改。
最大的 JavaScript 包
之前 | 之后 | 变化 | |
---|---|---|---|
Barnebys | 605kB | 425kB | 减少 30% |
SumUp | 395kB | 122kB | 减少 70% |
新的分块行为不仅减少了整体和初始加载大小,还减少了后续的客户端导航。Barnebys® 在六 (6) 次页面导航后加载的 JavaScript 量减少了 87%:
多次客户端跳转加载的 JavaScript
之前 | 之后 | 变化 | |
---|---|---|---|
Barnebys | 136kB | 18kB | 减少 87% |
这一新行为完全向后兼容。升级到最新版本的 Next.js 即可利用这一性能改进。
全捕获动态路由
随着 Next.js 9 的发布,我们引入了 动态路由段,目标是在不需要自定义服务器的情况下简化 Next.js 中的动态段。这一特性已被 Next.js 用户广泛采用。
但仍有一些情况是动态路由段特性未涵盖的。
其中之一是全捕获路由。例如,将通配符如 /post/**
作为页面路由。这对于具有由内容源(如 CMS)定义的嵌套结构特别有用。
你现在可以使用 [...name]
语法创建全捕获动态路由。
例如,pages/post/[...slug].js
将匹配 /post/a
、/post/a/b
、/post/a/b/c
等路径。
slug
将在路由查询对象中作为单独路径部分的数组提供。因此,对于路径 /post/foo/bar
,查询对象将是 { slug: ['foo', 'bar'] }
。
社区
我们非常高兴看到 Next.js 的采用持续增长:
- 我们有超过 880 位独立贡献者。
- 在 GitHub 上,该项目已获得超过 44,000 次星标。
- 示例目录 包含超过 220 个示例。
Next.js 社区现在有超过 13,800 名成员。加入我们!
我们感谢我们的社区以及所有外部反馈和贡献,这些帮助塑造了这一版本。