写点什么

webpack 或 esbuild:为什么不是两者兼而有之?

用户头像
DisonTangor
关注
发布于: 刚刚
webpack 或 esbuild:为什么不是两者兼而有之?

【翻译于 LogRocket 网站上 John Reilly 所写的webpack or esbuild: Why not both?】使用像esbuild的工具可以更快地进行构建。但是,如果您正投资于webpack,但是仍想利用更快的构建,那么有一种方法。


在本教程中,我们将向您展示如何将 esbuild 与 webpack 与esbuild-loader一起使用。



Web 开发的世界正在发展

向那些遭受 JavaScript 疲劳的人道歉,Web 开发的世界再次发展。长期以来,通过某种基于 Node.js 的构建工具(如 webpack 或 rollup.js)运行 JavaScript 和 TypeScript 一直是常见做法。这些工具是用它们编译成的相同语言编写的——即 JavaScript 或 TypeScript。


博客上的新成员是esbuildViteswc等工具。它们与它们的前辈之间的显着区别在于,新式工具是用 Go 和 Rust 等语言编写的。Go 和 Rust 的性能比 JavaScript 好得多。这转化为显着更快的构建


这些新工具具有变革性,可能代表了 Web 构建工具的未来。从长远来看,esbuild、Vite和朋友之类的工具很可能会取代当前的标准构建工具——webpacks、rollups 等。


然而,这是长期的。有很多项目已经在他们当前的构建工具上投入了大量资金——主要是 webpack。迁移到新的构建工具并非易事。新项目可能会从 Vite 开始,但现有项目不太可能被移植。webpack 如此受欢迎是有原因的;它确实可以很好地完成很多事情。它经过大型项目的实战测试,非常成熟,并且可以处理广泛的用例。


因此,如果您的团队想要更快的构建但没有时间进行大规模迁移,您有什么可以做的吗?是的,有一个中间地带需要探索。


有一个相对较新的项目,名为esbuild-loader。esbuild-loader 由hiroki osame开发,是一个建立在 esbuild 之上的 webpack 加载器。它允许用户通过它交换ts-loader babel-loader,这大大提高了构建速度。


在这里声明一个兴趣以进行全面披露,我是ts-loader的主要维护者,这是一个流行的 TypeScript 加载器,通常与 webpack 一起使用。但是,我强烈认为这里重要的是开发人员的生产力。作为 Node.js 为基础的项目,ts-loaderbabel-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)+         }+       },
复制代码

创建基线应用程序

让我们在练习中看看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 插件来提供 TypeScript 类型检查。这非常有用,因为 esbuild 只是进行转译而不是设计来提供类型检查支持。所以幸运的是我们仍然有那个插件。否则,我们将失去类型检查。


现在您理解了迁移 esbuild 的优势,我们先需要一个基线来理解用babel-loader会有怎样的表现。我们运行time npm run build去执行一个我们简单 app 的构建。



我们的完整构建、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
复制代码


接着我们将交换我们各种scripts在我们的package.json以便使用CRACO


"start": "craco start","build": "craco build","test": "craco test",
复制代码


我们的应用使用 CRACO,但是我们还未配置他。所以我们正添加一个craco.config.js的文件到我们项目的根目录上。这是我们为esbuild-loader而换出babel-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删除单个加载程序,因此,我们将删除两者并重新添加非应用程序 JavaScript babel-loader。我们还将添加esbuild-loader{ loader: 'tsx', target: 'es2015' }选项设置,以确保我们能处理 JSX / TSX。


最后,我们也将使用Terser替换为 esbuild 的 JavaScript 缩小。

巨大的性能提升

我们的迁移已完成。下次我们构建时,我们将在没有弹出的情况下用esbuild-loader运行 Create React App 。再一次,我们将运行time npm run build以执行我们的简单应用程序的构建并确定需要多长时间:



我们的完整构建、TypeScript 类型检查、转译、缩小等全部耗时 13.85 秒。通过迁移到esbuild-loader,我们将整体编译时间减少了大约三分之一。这是一个巨大的进步!


随着代码库的扩展和应用程序的增长,编译时间可能会激增。使用esbuild-loader,您应该从构建时间中获得持续的好处。


发布于: 刚刚阅读数: 2
用户头像

DisonTangor

关注

怀揣一个武侠梦的男孩 2020.07.29 加入

还未添加个人简介

评论

发布
暂无评论
webpack 或 esbuild:为什么不是两者兼而有之?