使用esbuild等工具可以使构建速度更快。然而,如果你已经投资了webpack,但仍然想利用更快的构建,有一个方法。
在本教程中,我们将向你展示如何使用esbuild和webpack以及esbuild-loader。

网络开发的世界在不断发展
对那些患有JavaScript疲劳症的人表示歉意,Web开发的世界又在演变了。长期以来,通过某种基于Node.js的构建工具(如webpack或rollup.js)来运行你的JavaScript和TypeScript是一种普遍做法。这些工具都是用相同的语言编写的,即JavaScript或TypeScript。
博客上的新孩子是esbuild、Vite和swc等工具。这些工具与它们的前辈之间的显著区别是,新派工具是用Go和Rust等语言编写的。Go和Rust的性能远比JavaScript好。这就意味着构建速度大大加快。
这些新工具是变革性的,可能代表了网络构建工具的未来。从长远来看,像esbuild、Vite和朋友们很可能会取代目前的标准构建工具--webpacks、rollups等等。
然而,这只是长期的。有很多项目已经在他们目前的构建工具上投入了大量的资金--主要是webpack。迁移到一个新的构建工具不是一个小任务。新项目可能会从Vite开始,但现有的项目不太可能被移植。webpack如此受欢迎是有原因的;它确实把很多事情做得非常好。它在大型项目中经受住了考验,它很成熟,而且它能处理广泛的使用情况。
那么,如果你的团队想拥有更快的构建,但又没有时间进行大规模的迁移,你能做什么吗?是的,有一个可以探索的中间地带。
有一个相对较新的项目叫esbuild-loader。由hiroki osame开发,esbuild-loader是一个建立在esbuild之上的webpack加载器。它允许用户用自己来替换ts-loader 或babel-loader ,这极大地提高了构建速度。
为了充分披露,我是ts-loader的主要维护者,这是一个流行的TypeScript加载器,通常与webpack一起使用。然而,我强烈地感觉到,这里最重要的是开发人员的生产力。作为基于Node.js的项目,ts-loader 和babel-loader 将永远无法与esbuild-loader 进行同样的竞争。作为一门语言,Go真的,呃,很好!
虽然esbuild不一定适用于所有的用例,但它可以适用于大多数的任务。因此,esbuild-loader 代表了一个中间地带--也是在不告别webpack的情况下获得esbuild所提供的更高构建速度的早期方法。本攻略将探讨在你的webpack设置中使用esbuild-loader 。
将一个现有的项目迁移到esbuild
将一个使用babel-loader 或ts-loader 的项目迁移到esbuild-loader 是非常简单的。首先,安装该依赖项。
npm i -D esbuild-loader
如果你目前使用的是babel-loader ,请对你的webpack.config.js 做如下修改。
module.exports = {
module: {
rules: [
- {
- test: /\.js$/,
- use: 'babel-loader',
- },
+ {
+ test: /\.js$/,
+ loader: 'esbuild-loader',
+ options: {
+ loader: 'jsx', // Remove this if you're not using JSX
+ target: 'es2015' // Syntax to compile to (see options below for possible values)
+ }
+ },
...
],
},
}
或者,如果你正在使用ts-loader ,请对你的webpack.config.js 做如下修改。
module.exports = {
module: {
rules: [
- {
- test: /\.tsx?$/,
- use: 'ts-loader'
- },
+ {
+ test: /\.tsx?$/,
+ loader: 'esbuild-loader',
+ options: {
+ loader: 'tsx', // Or 'ts' if you don't need tsx
+ target: 'es2015'
+ }
+ },
...
]
},
}
创建一个基线应用程序
让我们看看esbuild-loader 在实践中是如何工作的。我们将使用Create React App来创建一个新的React应用程序。
npx create-react-app my-app --template typescript
这将在my-app 目录中使用TypeScript搭建出一个新的React应用程序。值得一提的是,Create React App在幕后使用了babel-loader 。
CRA还使用了Fork TS Checker Webpack Plugin来提供TypeScript类型检查。这非常有用,因为esbuild只是进行转译,并不是为了提供类型检查支持而设计的。所以很幸运的是,我们仍然有这个插件在那里。否则,我们将失去类型检查。
现在你明白了转移到esbuild的好处,我们首先需要一个基线来了解使用babel-loader 的性能是怎样的。我们将运行time npm run build 来执行我们简单应用的构建。

我们的完整构建,TypeScript类型检查,转译,最小化等等,都需要22.08秒。现在的问题是,如果我们将esbuild放入其中,会发生什么?
介绍esbuild-loader
定制Create React App构建的一种方法是运行npm run eject ,然后定制CRA所输出的代码。这样做是可以的,但这意味着你无法跟踪CRA的发展。另一种方法是使用Create React App Configuration Override(CRACO)这样的工具,它允许你在当地调整配置。CRACO将自己描述为 "一个简单易懂的配置层,用于create-react-app"。
让我们把esbuild-loader 和CRACO作为依赖项加入。
npm install @craco/craco esbuild-loader --save-dev
然后,我们将把我们的package.json 中的各种scripts 换成使用CRACO 。
"start": "craco start",
"build": "craco build",
"test": "craco test",
我们的应用程序现在使用CRACO,但我们还没有配置它。因此,我们将在我们项目的根部添加一个craco.config.js 文件。这就是我们把babel-loader 换成esbuild-loader 的地方。
const { addAfterLoader, removeLoaders, loaderByName, getLoaders, throwUnexpectedConfigError } = require('@craco/craco');
const { ESBuildMinifyPlugin } = require('esbuild-loader');
const throwError = (message) =>
throwUnexpectedConfigError({
packageName: 'craco',
githubRepo: 'gsoft-inc/craco',
message,
githubIssueQuery: 'webpack',
});
module.exports = {
webpack: {
configure: (webpackConfig, { paths }) => {
const { hasFoundAny, matches } = getLoaders(webpackConfig, loaderByName('babel-loader'));
if (!hasFoundAny) throwError('failed to find babel-loader');
console.log('removing babel-loader');
const { hasRemovedAny, removedCount } = removeLoaders(webpackConfig, loaderByName('babel-loader'));
if (!hasRemovedAny) throwError('no babel-loader to remove');
if (removedCount !== 2) throwError('had expected to remove 2 babel loader instances');
console.log('adding esbuild-loader');
const tsLoader = {
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('esbuild-loader'),
options: {
loader: 'tsx',
target: 'es2015'
},
};
const { isAdded: tsLoaderIsAdded } = addAfterLoader(webpackConfig, loaderByName('url-loader'), tsLoader);
if (!tsLoaderIsAdded) throwError('failed to add esbuild-loader');
console.log('added esbuild-loader');
console.log('adding non-application JS babel-loader back');
const { isAdded: babelLoaderIsAdded } = addAfterLoader(
webpackConfig,
loaderByName('esbuild-loader'),
matches[1].loader // babel-loader
);
if (!babelLoaderIsAdded) throwError('failed to add back babel-loader for non-application JS');
console.log('added non-application JS babel-loader back');
console.log('replacing TerserPlugin with ESBuildMinifyPlugin');
webpackConfig.optimization.minimizer = [
new ESBuildMinifyPlugin({
target: 'es2015'
})
];
return webpackConfig;
},
},
};
那么,这里发生了什么?脚本会在默认的Create React App配置中寻找babel-loader 的使用。会有两个:一个是TypeScript/JavaScript应用代码(我们要替换这个),一个是非应用的JavaScript代码。现在还不太清楚有哪些非应用的JavaScript代码,所以我们会把它留在原地;它可能很重要。我们真正关心的代码是应用程序的代码。
你不能使用CRACO 删除单一的加载器,因此,我们将删除这两个加载器,并重新添加非应用的JavaScriptbabel-loader 。我们还将添加esbuild-loader ,并设置{ loader: 'tsx', target: 'es2015' } 选项,以确保我们能够处理JJSX/TSX。
最后,我们也将换成使用Terser对esbuild的JavaScript进行粉碎处理。
巨大的性能改进
我们的迁移已经完成。下次构建时,我们将使用esbuild-loader ,在没有弹出的情况下运行Create React App。再一次,我们将运行time npm run build ,执行我们的简单应用的构建,并确定它需要多长时间。

我们的完整构建,TypeScript类型检查,转译,最小化等等,都需要13.85秒。通过迁移到esbuild-loader ,我们的整体编译时间减少了大约三分之一。这是一个巨大的进步!
随着你的代码库的扩展和你的应用程序的增长,编译时间会急剧上升。有了esbuild-loader ,你应该在构建时间上获得持续的好处。
The postwebpack or esbuild:首先出现在LogRocket博客上。