跳至内容
返回博客

2018年2月5日,星期一

Next.js 5:通用Webpack、CSS导入、插件和区域

作者

我们非常高兴地向世界推出 Next.js 5.0。它已在 npm 上发布,立即生效。要升级,请运行

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

除了升级 Next.js 之外,我们还升级了对等依赖项 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.js webpack 配置,方法是在您的 next.config.js 中设置 webpack 属性。

由于 webpack 对生产和开发的执行方式不同,因此我们当时决定将其设为一个函数,用于修饰我们的默认 webpack 配置。

next.config.js
module.exports = {
  webpack(config, { dev }) {
    // modify it!
    return config;
  },
};

可选 next.config.js 文件示例

但是,Webpack 仅在客户端(浏览器)捆绑包上执行,您将错失使用此出色工具链进行服务器端渲染的可能性。

我们很高兴地宣布,我们已经对我们的代码库进行了广泛的重构,以使 Webpack 能够通用地工作

从您的角度来看,唯一变化的是上面装饰器函数会多传递一个 isServer 属性。但是,新的语义意味着现在您可以使用广泛的 Webpack 加载器生态系统。

CSS、LESS、SASS、SCSS 和 CSS 模块

我们最常被请求的功能之一是能够导入 CSS 文件并利用 Webpack 加载器。

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

使用 CSS 导入的页面示例(pages/index.js),这要归功于通用 Webpack。

要使此功能正常工作,您可以将所需的加载器作为对等依赖项引入。

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

Next.js,它让您可以自由选择所需的加载器,并随意将其升级到不同的版本。

然后扩展配置以配置您的加载器。在 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 加载器具有许多重要的优势,例如易于重用现有的 CSS 代码库,以及大大简化将旧代码库迁移到 Next.js 的过程。

我们没有默认启用所有可以想象的功能和加载器,而是引入了Next.js 插件,它们是修饰配置的简单函数。无需像上面那样手动扩展配置来设置加载器,您只需执行以下操作即可

const withCss = require('next-css');
module.exports = withCss({
  /* extra optional config */
});

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

阅读有关CSS 加载器与 Next.JS 一起使用的更多信息,或参考我们已经为您创建的一些软件包。

加载器软件包
CSSnext-css
LESSnext-less
SASSnext-sass

我们的目标是赋予社区开发和发展实用且简单的扩展生态系统的能力。为此,我们正在开放next-plugins单体仓库,供 Next.js 社区维护。欢迎所有 PR!

TypeScript 支持

JavaScript 生态系统中发展最快的技术之一是 TypeScript。它发展得如此之快,以至于它正在被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 加载器和预处理器一样,TypeScript 一直是最受请求的功能之一。为了像其他任何加载器一样轻松地将其集成到项目中,我们现在有一个next-typescript 插件,您可以将其包含在 next.config.js 文件中。

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

插件可以轻松组合:它们只是函数。

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

随着时间的推移,出现了许多 React 的直接替换实现。其中一些值得注意的是 [preact](https://preact.reactjs.ac.cn/)、nervjsinferno

其他库专注于替换 DOM 渲染器,例如 react-dom-lite,它旨在通过在浏览器兼容性方面做出一些小的权衡来创建更小的 React 构建。

通用 Webpack 支持使将这些库作为直接替换进行集成变得更加容易。与其他插件相同,使用 Next.js 和 preact 只需要执行以下操作。

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

我们安装 preact 插件和必要的对等依赖项。

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

我们新的 next.config.js 准备好了 preact。

查看非常简单的@zeit/next-preact 模块或创建您自己的模块!

生产环境中的可选外部源映射

现在 Next.js 使用 webpack 来处理客户端和服务器代码,在生产环境构建中启用源映射只需要对配置进行少量调整。

在开发环境中,源映射会自动启用,因此我们为生产环境配置了不同的设置。

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

当不在开发环境中时,我们只需对 devtool 选项进行不同的配置。

区域

从一开始,Next.js 的目标之一 就是恢复并保持 Web 的简单性。

服务器端渲染、一种简单且与数据无关的数据获取方法,以及基于文件系统结构的声明式页面,是我们根据这种思路引入的一些功能。

Web 服务和网站的一个经常被忽视的方面是它们天生就具有可组合性和可扩展性

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

要将它们“粘合在一起”,为最终用户提供统一的体验,只需要对后端路由层或负载均衡器进行一些简单的配置,将它们暴露给外部世界。我们很高兴现在能够组合使用 Next.js 构建的多个应用程序,并使用普通的 <Link> 组件将它们连接在一起。我们称此功能为区域

例如,考虑这两个独立的 Next.js 应用程序部署到Vercel

Both of our pages have a seamless experience, but they belong to separate apps
我们的这两个页面都具有无缝的用户体验,但它们属于不同的应用程序。

当我们改进我们的文档时,我们希望尽可能轻松地接受社区贡献。

我们决定将文档“迷你网站”拆分到其自己的存储库中。此外,每当提交拉取请求并提出更改时,我们都会自动将其隔离部署。

Every time a change happens inside a PR our bot automatically deploys it
每次 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 命令。

终端
now alias -r rules.json my-domain.com

我们的使命是使部署尽可能通用和开放。为了帮助进行本地开发,我们最近开源了 micro-proxy,这是一个使用上面看到的相同配置格式的工具。

您也可以使用其他解决方案(如 Nginx、HAProxy 或 API 网关)将区域连接在一起。

更快的生产环境构建时间

我们认为开发者体验和用户体验是相辅相成的。更改编写、测试和部署的效率越高,添加新功能、修复错误的速度就越快,整体用户体验就会得到改善。

因此,我们始终专注于不断改进系统最基本构建块的性能。

在 Next.js 5.0 中,我们有机会再次审视 next build,这是在部署到生产环境或将 Next.js 应用程序导出为静态站点之前运行的命令。

我们很高兴地报告,对于 vercel.com(一个由数千个组件组成的 React 应用程序),我们使用 Next.js 5.0 已经看到了非常显著的改进,**生产环境构建时间缩短了 23.6%**。

Our main application production build now takes 38 fewer seconds to complete
我们的主要应用程序生产环境构建现在只需 38 秒即可完成。

改进动态导入的缓存

每当你使用动态的 import() 时,这会告诉 Webpack 存在一个新的代码分割入口点。

在构建时,这意味着为模块的对应子树生成一个特定的 bundle。

在 Next.js 5.0 之前,动态 bundle 会接收一个类似于以下内容的 URL

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

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

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

这意味着在部署过程中,你的用户和客户无需重新下载他们已经使用过的代码。

片段

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 支持,这转化为更轻量级的页面和对页面样式的完全控制,而无需任何多余的标记。

更准确的错误

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

在 Next 5 中,我们改进了服务器端的源映射支持。服务器渲染时发生的错误现在会指向正确的函数和行号。

Errors now show the correct line, file and function name
错误现在显示正确的行号、文件和函数名称。

结论

通用 Webpack 巩固了 Next.js 的基础,并使其更具未来性。总的来说,不再存在关于哪些插件或加载程序适用于 Next.js 以及哪些不适用的这种人为的分离。

本着“零配置”的精神,我们很高兴推出 Next 插件,这是一个用于食谱的社区资源库,这些食谱可以自动扩展 Next.js 的功能,而无需调整特定的旋钮。

通过此功能,我们现在支持所有范围的 CSS 解决方案、编译为 js 的语言(如 TypeScript)以及 React 的替代方案(如 Nerve),只需引入一个额外的模块并在 next.config.js 中明确说明其包含即可。简单而不模糊。

区域允许互连不在同一存储库或甚至同一服务器上根植的 Next.js 应用程序。我们认为这是“团队可扩展性”类别改进方面的一个非常重要的里程碑。

因此,Next.js 成为由多个团队维护的大型应用程序的绝佳选择。他们现在可以并发部署改进,减少错误面,提高迭代速度,甚至可以尝试除了我们的核心之外的其他技术,例如 多种不同的方法 来进行状态管理或数据获取。

借此机会感谢 Deep Varma 和 Trulia 工程团队为贡献关键见解、代码和测试而做出的贡献,这些贡献促成了此功能的设计。

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