如何用 esbuild 替换 Create React App 中的 Webpack

原文链接:https://devtails.xyz/how-to-replace-webpack-in-create-react-app-with-esbuild[1]

作者:Adam[2]

正文从这开始~

今年是2022年,你所有搞web开发的朋友都告诉你要学习React。为了让事情变得简单,他们告诉你有一个神器叫做create-react-app[3]。你会看到,在三个命令行的帮助下,你可以拥有一个完整配置的React应用程序运行,并为此感到高兴。

代码语言:javascript
复制
npx create-react-app my-app
cd my-app
npm start

在大约一分钟的依赖包安装和几秒钟的npm启动后,你就可以开始了。

现在你拥有了一个基础的React应用程序,你添加了几个额外的组件和页面来建立你梦寐以求的React应用程序。到目前为止,一切都很顺利,你所做的更改神奇地展示在localhost上。

最后,是时候将这个应用程序部署到网络上,并分享你的创造。为了使事情变得简单,你只需运行npm run build,并添加一个命令将文件scp到你的服务器上。

这是你第一次运行npm run build,你发现运行该命令需要花费20秒。"这是我唯一一次部署",你告诉自己,并忽略了构建所需的时间。

你加载很炫酷的新网站,却发现上面有一个错别字。你快速的修改完并重新部署。在你的改动生效之前,又要花费漫长的20秒时间。

"嗯,也许我应该更新这里的padding。" "如果这是不同的颜色呢?" "我应该添加谷歌网站分析。" 各种各样的新想法涌入你的脑海。它们中的每一个都只需要更新一行代码。然而,要让这些代码被部署起来,却要花上20-30秒。

当你意识到在刚刚部署成功后,却有一个严重bug需要被修复时,问题变得糟糕起来。问题修复起来很简单,但是我们又要花费半分钟才能上线。

这不是一个编造的故事。这是我目前在Kaizen做的一个音乐应用程序的情况。

在其他项目中,我看到生产环境构建时间已经膨胀到超过一分钟。在一个较慢的构建机器上运行时,有时需要两倍的时间。

以前,我曾写过快速迭代的重要性,三行代码不应花费一整天[4]。这个原则同样适用于部署代码。在生产环境被充分验证之前,不能声称事情已经搞定。这个过程越慢,就必须等待更长的时间才能看到代码是否按预期工作。

这篇文章演示了如何用速度更快的esbuild打包器替换create-react-app中安装的webpack打包器。

安装esbuild

代码语言:javascript
复制
npm i -D esbuild

在package.json中更新构建脚本

代码语言:javascript
复制
// package.json
"scripts": {
    "start": "react-scripts start",
    "build": "esbuild src/index.js --bundle --outfile=public/js/app.js --loader:.js=jsx",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
},

重新运行构建

代码语言:javascript
复制
npm run build

在默认的create-react-app应用程序中,你应该会看到以下错误:

esbuild-errors.png

启用JS文件的JSX语法

前两个错误建议在构建命令中加入 --loader:.js=jsxesbuild对扩展名为jsx的文件默认会进行处理,但要处理扩展名为.js的文件则需要添加上述命令。

代码语言:javascript
复制
// package.json
"build": "esbuild src/index.js --bundle --outfile=build/js/app.js --loader:.js=jsx"

添加SVG Loader

默认的应用程序使用import语法来包含一个svg图像。esbuild默认不处理这种类型的文件。为了支持这些类型的文件,esbuild提供了插件支持。你可以在这里[5]找到社区esbuild插件的列表。此时,我们将使用esbuild-plugin-inline-image来内联我们的svg图片。额外的,该插件也可以处理未来有关img的需求。

代码语言:javascript
复制
npm i -D esbuild-plugin-inline-image

为了加载新的插件,我们需要改变我们的构建命令,来使用esbuild的JavaScript API。

代码语言:javascript
复制
// build.js
const esbuild = require("esbuild");
const inlineImage = require("esbuild-plugin-inline-image");

esbuild.build({
entryPoints: ["./src/index.js"],
outfile: "./public/js/app.js",
minify: true,
bundle: true,
loader: {
".js": "jsx",
},
plugins: [inlineImage()],
}).catch(() => process.exit(1));

代码语言:javascript
复制
// package.json
"build": "node build.js"

更改完之后,当运行npm run build 时,将会看到构建成功。

esbuild-success.png

在我的电脑上,这个构建命令现在大概需要60毫秒。比起6秒的webpack构建,快了整整100倍。但是还没结束,我们仍需要真正的看到并运行这些改动。

更新index.html

Create React App创建了一个public文件夹,里面预置了几个文件。包含在其中的index.html更像是一个模板,在运行react-scripts build时,会被处理并输出到build文件夹。

在我们新的esbuild构建中,index.html不需要成为模板。删除对%PUBLIC_URL%的引用,并添加一个script标签,指向我们新构建的app.jsapp.css包。

代码语言:javascript
复制
// public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>React App</title>
<script src="/assets/app.js" async defer></script>
<link rel="stylesheet" href="/assets/app.css"/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

你可能想把public/js添加到你的.gitignore中,因为你可能不想在生产构建的时候进行检查。

添加serve.js来自动重新构建

代码语言:javascript
复制
// serve.js
const esbuild = require("esbuild");
const inlineImage = require("esbuild-plugin-inline-image");

esbuild
.serve(
{
servedir: "public",
port: 8000,
},
{
entryPoints: ["./src/index.js"],
outfile: "./public/js/app.js",
bundle: true,
loader: {
".js": "jsx",
},
plugins: [inlineImage()],
}
)
.catch(() => process.exit());

替换npm start

代码语言:javascript
复制
// package.json
"start": "node serve.js"

运行npm start会在8000端口启动一个本地开发服务器,这样你就可以通过http://localhost:8000[6]进行访问。有了esbuild,你将看到应用程序会如期运行,而且初始构建和后续构建都快如闪电。

总结

只需仅仅几步,我们就将一个6秒的构建转换为60毫秒的构建。有一些地方还可以再调整一下,但这应该给你留下了一个良好的开端,也就是如何将基于webpackReact构建转换为esbuild。如前所述,我将在Kaizen的前端代码中进一步探索这种转换,并将写下我在一个更大的项目中遇到的任何问题。

参考资料

[1]

https://devtails.xyz/how-to-replace-webpack-in-create-react-app-with-esbuild: https://devtails.xyz/how-to-replace-webpack-in-create-react-app-with-esbuild

[2]

Adam: https://devtails.xyz/about/

[3]

create-react-app: https://github.com/facebook/create-react-app

[4]

三行代码不应花费一整天: https://devtails.xyz/3-lines-of-code-shouldnt-take-all-day

[5]

这里: https://github.com/esbuild/community-plugins

[6]

http://localhost:8000: http://localhost:8000