自从 Next.js 进入 5.0 版本之后就引入了全新的插件系统,修改 webpack 配置的方式变得十分简单。但是我觉得这套插件系统实际上非常鸡肋,因为可配置项太少,想要做些自定义的话就会变得比较麻烦。

首先我们在 Next.js 项目下创建一个名为 next.config.js 的文件

touch next.config.js

为了处理 TypeScript 文件以及 Ant Design 的 less 样式,我们引入两个 Next.js 插件(当然别忘了 peer dependency):

npm install --save @zeit/next-typescript @zeit/next-less typescript less

按照 Next.js 文档所述,很轻松就可以把这两个插件应用上去。只需在 Next.js 项目下创建一个名为 next.config.js 的文件:

// next.config.js

const withLess = require('@zeit/next-less')
const withTypescript = require('@zeit/next-typescript')

module.exports = withTypescript(withLess())

根据 @zeit/next-less@zeit/next-typescript 的文档所述,我们需要做一些修改。为了正确导出 React Component,我们需要将 tsconfig.json 中的 module 改成 es2015。同时,为了导入处理后的 css,我们需要创建 pages/_document.tsx 文件并添加:

// pages/_document.tsx

import Document, { Head, Main, NextScript } from "next/document";

export default class extends Document {
  public render() {
    return (
      <html>
        <Head>
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.0.0/antd.min.css" />
          <link rel="stylesheet" href="/_next/static/style.css" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

虽然这样就已经可以使用了,但现在这种程度是没有办法给 Ant Design 使用按需加载的。也就是说,你只能以这种方式写代码,同时还需要从外部导入 Ant Design 的 css 文件:

import Button from "antd/lib/button"

而不是这样:

import { Button } from "antd"

为了使得代码更美观,我们可以使用一个 TypeScript 插件实现按需加载:

npm install --save ts-import-plugin

那么但是在 @zeit/next-typescript 中,ts-loader 已经被封装起来了,那怎么才能应用这个插件呢?幸亏 Next.js 给我们留了一个接口,从而可以用一个我认为不是那么美观的手段 hack 进去。当然,你也可以选择 fork @zeit/next-typescript,然后使用自己的版本。

这里有个地方要注意的是,webpack 的服务端配置中不能够引入这个插件,只能在客户端配置中引入,因此要做一个判断。

// next.config.js

const withLess = require('@zeit/next-less')
const withTypescript = require('@zeit/next-typescript')
const tsImportPluginFactory = require('ts-import-plugin')

module.exports = withTypescript(withLess({
  webpack: (config, options) => {
    const { dev, isServer } = options

    config.module.rules.forEach(rule => {
      if (rule.use instanceof Array) {
        rule.use.forEach(use => {
          if (use.loader === 'ts-loader' && !isServer) {

            // Import on demand for Ant Design
            use.options = Object.assign({}, use.options, {
              getCustomTransformers: () => ({
                before: [ tsImportPluginFactory({
                  style: true
                }) ]
              })
            })

          }
        })
      }
    })

    return config
  }
}))

这时我们就可以将 pages/_document.tsx 中引入 Ant Design 的 css 一行删除掉了:

// pages/_document.tsx

import Document, { Head, Main, NextScript } from "next/document";

export default class extends Document {
  public render() {
    return (
      <html>
        <Head>
          <link rel="stylesheet" href="/_next/static/style.css" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

最后,

npm run dev

这样就完成了。

其它的一些自定义功能,比如说 Ant Design 不允许启用 CSS Modules,而自己的项目里面的其它文件却想使用 CSS Modules 这个功能,也可以通过在 webpack 函数里面遍历 config.module.rules 并修改的方式实现,实际上还是相当灵活的。