08月13, 2024

脚手架内置esbuild踩坑记录

随着项目越来越大后,咱们脚手架使用 babel 进行代码编译感觉越来越慢。为了提升编译速度,尝试了一下使用 esbuild 后发现速度提升明显,于是决定将 esbuild 正式集成到脚手架中。

主要内容

  • 为什么要使用 esbuild
  • 如何集成
  • 遇到的问题及解决方案

为什么要使用 esbuild ?

  • esbuild 基于 go 实现,构建速度非常快(官网声称比 webpack5 快 10-100 倍)
  • 目前 esbuild 生态已相对成熟,基本满足替换目前使用的 babel 的条件
  • 项目越来越大后,使用 babel 确实感觉比较慢,并且经过对比测试后,使用 esbuild 在编译时间确实有提升

如何集成?

直接使用 esbuild-loader 代替目前的 babel-loader 即可。
ola-cli@3.7.x 开始同时支持 babel 和 esbuild,通过 .olarc.js 配置 esbuild: true 开启 esbuild 编译

需要注意的是:esbuild 目前只能产出 es2015 代码,所以不兼容 IE 浏览器,若有兼容需求请慎用

遇到的问题

1. 原有的 autoCssModule 功能会失效

失效原因

autoCSSModules 功能主要是通过 js 代码中引用 css 文件的方式来自动判断是否启用 cssModule 功能,比如 import './index.css' 这种写法就是普通 css 引用,import styles from './index.css' 就是以 cssModule 的方式引用。

当编译工具发现以 cssModule 方式引用时,会自动将代码改写成 import styles from './index.css?modules',这样在 webpack 的 css-loader 配置中可以匹配到 ?modules 参数并开启 cssModule 功能。
自动改写代码原来是基于 babel 的插件实现,esbuild-loader 不支持自定义插件,于是只能在 esbuild-loader 编译完成后来处理这部分改动。

如何解决从

webpack 的 loader 入手,在 esbuild-loader 后增加一个 loader 来处理 esbuild 编译完成后的代码。

使用 es-module-lexer 对代码进行分析,提取出需要开启 cssModule 的 css 引用方法。es-module-lexer 会分析出代码具体在文件中的位置。

使用 magic-string 将对应位置引用的 css 文件加上 ?modules 后缀。magic-string 是一个文本内容维护工具,支持对文本内容进行各种修改,最终生成修改后的结果并支持输出 source-map 的一个工具。

2. 原有的 transformImports 功能会失效

失效原因

原有功能是使用了 babel 的一个插件 babel-plugin-transform-imports。这个插件的主要作用是将 es6 模块的整体引入改写为部分引入,减少了打包后的代码依赖项。例如通过如下配置:

1
2
3
4
5
transformImports: {
'@jd/jmtd': {
transform: (importName) => `@jd/jmtd/es/${importName}`,
},
}

可以将代码里 import { Button } from '@jd/jmtd' 改写成 import Button from '@jd/jmtd/es/Button'。两者的区别就是,前者会引入 '@jd/jmtd/index.js',导致整个组件库的代码都会被引入进来,而后者只会去引用 Button 组件。

同 csModule 一样,由于 esbuild-loader 不支持自定义插件,无法在 esbuild 编译对代码进行改写,所以 transformImports 功能会失效。

如何解决

和 cssModule 的处理方法类似,通过 webpack 的 loader 来处理 esbuild 编译过后的代码。同样使用 es-module-lexermagic-string,根据 .olarc.js 配置文件中的 transformImports 配置内容,对代码文件内引用的模块进行匹配,符合条件的模块引用代码会被重写为逐条引入的写法。

3. sourceMap 定位飘移

当使用 webpack loader 对 esbuild 编译后内容进行修改过后,会导致 esbuild 产生的 sourceMap 和源码内容定位不准确的问题。原因就是在进行 transfromImports 修改过后,文件内容的行数可能会发生变化。

要解决这个问题,主要思路就是读取 esbuild 产生的 sourceMap 内容,并且将要重写的那部分代码的映射关系删除掉,重新添加新的映射关系。

这里需要用到一个工具叫 source-map。它可以读取已有的 sourceMap 内容并且支持对它进行再次修改,最后再生成一个最新的 sourceMap 内容。

最后

对 transform-imports 支持已经抽离成了一个 npm 包,源码可以查看 transform-imports-loader

本文链接:https://www.chenliqiang.cn/post/webpack-scaffold-use-esbuild.html

-- EOF --