跳到内容
返回博客

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.config.js 中设置 webpack 属性来扩展 Next.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 Modules

我们最受期待的功能之一是导入 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

阅读更多关于 Next.JS 中CSS 加载器用法的文章,或参考我们已为您创建的一些包

加载器
CSSnext-css
LESSnext-less
SASSnext-sass

我们的目标是赋能社区开发和发展一个实用简单的扩展生态系统。为此,我们正在开放next-plugins monorepo,供 Next.js 社区维护。欢迎所有拉取请求!

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 Altlibs 和模块重载

随着时间的推移,许多 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 模块或创建您自己的模块!

生产中的可选外部 Sourcemap

现在 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> 组件连接在一起。我们将此功能称为区域

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

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 Gateway)连接在一起。

更快的生产构建时间

我们认为开发者体验和用户体验是相辅相成的。变更编写、测试和部署的效率越高,新功能添加、bug 修复和整体用户体验提升的速度就越快。

因此,我们仍然专注于持续改进系统最基本构建块的性能。

借助 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 表明存在一个新的代码分割入口点。

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

在 Next.js 5.0 之前,动态包会收到如下所示的 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 工程团队的贡献,他们的真知灼见、代码和测试促成了此功能的设计。

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