Back返回博客

Next.js 5:通用 Webpack、CSS 导入、插件与多区域部署

Next.js 5 专注于提升大型应用的可扩展性、组合能力与性能表现

我们非常高兴向全球开发者推出 Next.js 5.0 版本,该版本已立即发布至 npm。升级方式如下:

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

除了升级 Next.js 外,还需同步升级其 peer 依赖项 reactreact-dom

Next.js 是用于构建通用服务端渲染(或静态预渲染)React.js 应用的开发工具集。只需执行 next 命令即可轻松启动任意规模应用的开发。(了解更多

每个新版本发布时,我们都承诺保持向后兼容性,提供平滑的升级路径,并仅在必要时进行 API 变更。Next.js 5.0 也不例外。

但在底层架构上,Next.js 经历了重大变革以实现更强大的用例和扩展性。我们首先让 Next.js 的服务端与客户端代码共享统一的 Webpack 构建流程。

通用 Webpack 与 Next 插件

Next.js 整合了 Webpack、Babel 和 Uglify 等强大工具,并为终端用户提供极简接口:开发用 next,生产构建用 next build,启动服务用 next start,静态导出则用 next export

我们早期的重要决策之一就是为这些工具的配置提供高度灵活的扩展点。我们不仅要追求易用性,更要实现按需扩展的自由度。

例如,您可以通过在 next.config.js 中配置 webpack 属性来扩展 Next.js 的 webpack 配置

由于 Webpack 在开发和生产环境有不同行为,我们当时将其设计为可装饰默认配置的函数

next.config.js
module.exports = {
  webpack(config, { dev }) {
    // 修改配置
    return config;
  },
};

可选 next.config.js 配置示例

但此前 Webpack 仅处理客户端(浏览器)代码包,无法利用这套强大工具链处理服务端渲染。

我们很高兴地宣布,经过大规模代码重构后,现已实现 Webpack 的通用化支持

对开发者而言,唯一变化是上述装饰函数会新增 isServer 参数。但新机制意味着您现在可以使用丰富的 Webpack loader 生态系统。

CSS、LESS、SASS、SCSS 与 CSS 模块

最受期待的功能之一是支持 CSS 文件导入及 Webpack loader 能力:

import './index.css';
 
export default function Index() {
  return (
    <div>
      <p>我爱 CSS!</p>
    </div>
  );
}

页面组件示例(pages/index.js),通过通用 Webpack 实现 CSS 导入

使用时需自行安装所需 loader 作为 peer 依赖:

终端
npm i --save css-loader style-loader postcss-loader

Next.js 允许自由选择所需 loader 并按需升级版本

然后在配置中扩展 loader 设置。于 next.config.js 添加:

next.config.js
module.exports = {
  webpack(config, options) {
    const { dev, isServer } = options;
    const extractCSSPlugin = new ExtractTextPlugin({
      filename: 'static/style.css',
      disable: dev,
    });
    config.module.rules.push({
      test: /\\.css$/,
      use: cssLoaderConfig(extractCSSPlugin, {
        cssModules,
        dev,
        isServer,
      }),
    });
    return config;
  },
};

直接扩展 webpack 配置可提供极高的灵活性和控制力

虽然我们推荐使用组件级样式方案(如内置的 styled-jsx babel 插件),但 CSS loader 在复用现有 CSS 代码库和简化旧项目迁移方面具有显著优势。

我们不再默认启用所有可能的特性和 loader,而是引入 Next.js 插件——这些简单函数可装饰您的配置。无需手动扩展配置,只需:

const withCss = require('next-css');
module.exports = withCss({
  /* 可选额外配置 */
});

只需引入 next-css 即可启用 .css 文件导入

了解更多关于 CSS Loader 的使用方法,或参考我们已提供的插件包:

Loader插件包
CSSnext-css
LESSnext-less
SASSnext-sass

我们的目标是让社区能够发展和维护实用的扩展生态。为此,我们开放了 next-plugins 单体仓库,欢迎提交 PR!

TypeScript 支持

TypeScript 是 JavaScript 生态中增长最快的技术之一,甚至已获得 Babel 7 官方支持。这意味着只需自定义 .babelrc 即可在 Next.js 中天然支持。

同时得益于通用 Webpack 支持,您现在就能获得完整的 TypeScript 支持!

可如下扩展 webpack 配置:

next.config.js
module.exports = {
  webpack(config, options) {
    const { dir, defaultLoaders } = options;
    config.resolve.extensions.push('.ts', '.tsx');
    config.module.rules.push({
      test: /\\.+(ts|tsx)$/,
      include: [dir],
      exclude: /node_modules/,
      use: [
        defaultLoaders.babel,
        { loader: 'ts-loader', options: { transpileOnly: true } },
      ],
    });
    return config;
  },
};

只需启用 ts-loader 即可

与 CSS loader 和预处理器类似,TypeScript 也是需求最高的功能之一。为简化项目集成,我们提供了 next-typescript 插件,只需在配置文件中引入:

next.config.js
const withTs = require('next-typescript');
module.exports = withTs({
  /* 额外配置 */
});

插件可轻松组合:它们本质上是函数

更好的 React 替代库与模块重载支持

近年来出现了许多 React 的替代实现,其中值得关注的有 preactnervjsinferno

还有一些库专注于替换 DOM 渲染器,如 react-dom-lite,它通过轻微牺牲浏览器兼容性来实现更小的 React 构建包。

通用 Webpack 支持让将这些库作为替代方案集成变得更加简单。与其他插件类似,使用 preact 只需:

终端
npm i @zeit/next-preact preact preact-compat

安装 preact 插件及其必要 peer 依赖

const withPreact = require('@zeit/next-preact');
module.exports = withPreact();

适配 preact 的新版 next.config.js 配置

查看简洁的 @zeit/next-preact 模块或创建您自己的实现!

生产环境可选外部源映射 (Optional External Sourcemaps in Production)

现在 Next.js 同时使用 webpack 处理客户端和服务器端代码,只需对配置稍作调整即可在生产构建中启用源映射 (source maps)。

开发环境下源映射会自动启用,因此我们针对生产环境进行如下配置:

next.config.js
module.exports = {
  webpack(config, { dev }) {
    if (!dev) {
      config.devtool = 'source-map';
    }
    return config;
  },
};

我们只需在非开发环境下对 devtool 选项进行不同配置

多应用区域 (Zones)

Next.js 从诞生之初就有一个明确目标:恢复并保持 Web 的简洁性。

服务端渲染 (SSR)、简单通用的数据获取方式,以及基于文件系统结构的声明式页面,都是我们基于这一理念引入的特性。

Web 服务和网站的一个常被忽视的特性是它们天然具备的可组合性和可扩展性。

例如,mydomain.com/settingsmydomain.com/ 可以是两个完全独立的应用,各自独立部署、独立扩展,甚至运行同一软件的不同版本。

只需通过简单配置后端路由层或面向公网的负载均衡器,就能将它们"粘合"成统一的用户体验。我们很高兴现在能够支持将由 Next.js 构建的多个应用组合起来,并通过常规的 <Link> 组件相互连接。我们将此特性称为多应用区域 (Zones)。

以部署在 Vercel 上的这两个独立 Next.js 应用为例:

两个页面体验无缝衔接,但实际属于不同应用

在重构文档系统时,我们希望让社区贡献尽可能简单。

我们决定将文档"微型网站"拆分到独立仓库。每当有拉取请求提交变更提案时,系统会自动隔离部署:

每当 PR 内有变更时,我们的机器人会自动部署

最终我们得到两个区域,通过路径别名功能整合到父域名 https://vercel.com 下。配置示例如下:

{
  "rules": [
    { "pathname": "/docs", "dest": "our-docs.vercel.app" },
    { "pathname": "/api", "dest": "our-docs.vercel.app" },
    { "dest": "my-main-website.vercel.app" }
  ]
}

这些简单规则让您能组合微服务和多应用区域

最后只需执行 now alias 命令:

Terminal
now alias -r rules.json my-domain.com

我们的使命是让部署尽可能通用和开放。为辅助本地开发,我们最近开源了 micro-proxy 工具,它使用与上述相同的配置格式。

您同样可以通过 Nginx、HAProxy 或 API Gateway 等方案实现区域整合。

更快的生产构建速度 (Faster Production Build Times)

我们认为开发者体验与用户体验密不可分。变更的编写、测试和部署效率越高,新功能上线越快,错误修复越及时,整体用户体验就越好。

因此,我们持续优化系统基础构件的性能表现。

在 Next.js 5.0 中,我们重新审视了 next build 命令——这是部署到生产环境或静态导出前运行的命令。

很高兴地宣布,对于包含数千组件的 vercel.com React 应用,Next.js 5.0 带来了显著提升,生产构建时间缩短了 23.6%:

主应用生产构建现在减少了 38 秒

动态导入的缓存优化 (Improved Caching for Dynamic Imports)

使用动态 import() 时,WebPack 会识别这是一个新的代码分割入口点。

构建时会为对应的模块子树生成特定包。

在 Next.js 5.0 之前,动态包的 URL 格式如下:

/_next/1517592683901/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6.js

现在,我们将动态导入转换为基于子树内容的哈希地址:

/_next/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6-b7874680a9e21fb6eb89.js

这意味着跨部署时,用户无需重复下载已使用过的代码。

片段支持 (Fragments)

Next.js 构建了顶级 <Document> 组件随每个页面进行服务端渲染。重载此组件可完全控制标记,实现诸多高级用例

初始标记部分包含 Next.js 需要在客户端评估的脚本列表。自定义 _document 如下所示:

pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document';
export default class extends Document {
  render() {
    return (
      <html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

Document 允许自定义页面的完整服务端渲染输出

此前我们被迫用 <div> 包裹脚本。

Next.js 5.0 开始支持 Fragment,带来更轻量的页面,并完全控制页面样式,无需多余标记。

更精准的错误提示 (More Accurate Errors)

Node.js 不支持源映射,服务器端错误会伴随指向编译后代码的堆栈跟踪。

Next 5 改进了服务端源映射支持。服务端渲染错误现在能准确定位到函数和行号。

错误现在显示正确的行号、文件和函数名

结语 (Conclusion)

通用 Webpack 加固了 Next.js 基础,使其更具前瞻性。总体上不再有插件或加载器适用性的人为区分。

秉承零配置理念,我们推出 Next 插件社区仓库,提供自动扩展 Next.js 功能的方案,无需调整具体配置。

由此我们支持全谱系 CSS 解决方案、TypeScript 等编译到 JS 的语言,以及 Nerve 等 React 替代方案,只需添加模块并在 next.config.js 中显式包含即可。简洁而不失透明。

多应用区域支持互连不同代码库甚至不同服务器的 Next.js 应用。这是我们团队可扩展性改进的重要里程碑。

Next.js 因此成为多团队维护大型应用的优秀选择。团队可并行部署改进,减少错误影响面,提升迭代速度,甚至尝试除核心功能外的不同技术,如状态管理或数据获取的多种方案

我们要特别感谢 Deep Varma 和 Trulia 工程团队贡献的关键见解、代码和测试。

一如既往,没有众多开源贡献者和优秀社区的支持,这个版本不可能实现。