直入正题,自从 umi mfsu 和 vite unbundle 打响 FE 高效开发第一枪,淘系 rax 新增 swc 功能、swc 作者加入 vercel 麾下,nextjs 新版本默认 swc 打包,再到阿里 ice2 新增 swc + esbuild 链路支持,swc 和 esbuild 逐渐入侵 FE 主工具流,暗示了一统天下的预兆。
Rust 和 Go 将成为未来 JavaScript 基础建设的主流语言。
本文将就 swc 与 esbuild 如何打通落地到 webpack 项目中为抓手来进行蓝图描绘,在阅读前,希望你已经具备了足够的基础知识和了解度,过于细致的内容将留给各位同学自行收束。
由于 esbuild 的定位是打包器,而不是真正的转译 polyfill 器,故我们的链路是 swc 先代替 babel 打包和 polyfill,再走 esbuild 压缩代码。
到这里为止,假如你是使用的 vue-cli 或 cra 的脚手架非配置透明项目,基本就可以说拜拜了(不排除 craco 的神仙高手),我们只针对 webpack 透明项目介绍使用规则。
首先打通 swc 为我们做 ts 的转译,当然我们是对标 React 项目。
pnpm add -D swc-loader @swc/core @swc/helpers
项目内新建 .swcrc.js
即 swc 配置文件,该文件和官方定义的 .swcrc
配置文件名称并不同,其原因是我们需要给不同环境分流,从而细粒度手动加载 config ,一个默认配置文件满足不了我们的野望。
// .swcrc.js
const path = require('path')
module.exports = (isDev = false) => {
// swc polyfill 策略,会复用 babel 链路,但效率比 babel 低
const polyfillConfig = isDev ? {} : {
env: {
mode: 'usage', // or entry
coreJs: 3,
path: path.resolve(__dirname),
},
}
return {
module: {
type: 'es6',
ignoreDynamic: true,
},
// polyfill
...polyfillConfig,
jsc: {
parser: {
syntax: 'typescript',
dynamicImport: true,
decorators: true,
tsx: true,
},
loose: true,
target: 'es2015',
externalHelpers: true,
transform: {
legacyDecorator: true,
decoratorMetadata: true,
react: {
runtime: 'automatic', // or classic
throwIfNamespace: true,
useBuiltins: true,
development: isDev,
},
},
},
}
}
这里着重要说的是,swc 的配置选项都很语义化,了解
es6 - es13 规范和 babel 生态的同学们相信一眼就很熟悉了,关于其具体说明可见官方文档:
本质上官方文档也书写的极其简单,因为这些配置项大多来自 es 规范和 babel 生态圈,如果你真的不懂,请照猫画虎使用即可,此处将不再浪费口舌介绍基础知识。
配置 webpack.config.js
代替 babel-loader
的部分:
// webpack.config.js
const jsReg = /\.(js|jsx|ts|tsx)$/
const swcLoader = 'swc-loader'
const swcConfig = require('./.swcrc.js')
const nodeModulesReg = /node_modules/
module: {
rules: [{
test: jsReg,
loader: swcLoader,
options: swcConfig(isDev),
exclude: nodeModulesReg,
}]
},
关于 exclude
要着重注意配置上,polyfill 侵染到 css 是要吃苦头的,详见:
《 mini-css-extract-plugin TypeError: $ is not a function 问题解决 》
当然如果你有特殊的定制逻辑,按自己的高级正则配置即可。
到现在为止,swc 链路就完整打通了,下面我们打通 esbuild 链路。
pnpm add -D esbuild esbuild-loader
我们使用 esbuild-loader
自带的 mini 插件,借助为压缩而生打通 webpack 的插件 ESBuildMinifyPlugin
完成我们的野望。
// webpack.config.js
const esbuild = require('esbuild')
const { ESBuildMinifyPlugin } = require('esbuild-loader')
minimizer: [
new ESBuildMinifyPlugin({
target: 'es2015',
legalComments: 'none', // 去除注释
css: true, // 压缩 css
implementation: esbuild, // 自定义 esbuild instance 实现
})
]
相信有知识积累的同学一眼就知道了,直接把 Terser 和 CssMini 给替换了,其实就是 TerserPlugin
和 CssMinimizerPlugin
( css-minimizer-webpack-plugin
)。
当然,css mini 还要联动 mini-css-extract-plugin
的 Loader ,整体是一环套一环,具体可见 esbuild-loader
的 文档说明 。
到此为止,我们的 esbuild 链路也打通了。
根据实战经验,项目越大享受的收益越大,至少有 2x 打包时间减半以上的收益。
目前 swc 和 esbuild 都处于疯狂迭代中,使用过程中即时 sync 最新的 @swc/core
和 esbuild
实现本体才是王道。