选择 JavaScript 构建工具:配置还是不配置

2025-06-07

选择 JavaScript 构建工具:配置还是不配置

构建工具已成为现代 Web 应用程序工作流程中必不可少的组成部分。我之前曾介绍过构建工具的基本功能,展示了构建工具如何帮助编写脚本、实现自动化并降低复杂性。考虑到这些因素,我将深入探讨一些较为流行的构建工具,以及它们如何应用于您的项目。

这篇文章并非详尽的列表,而是旨在提供一些建议,帮助您开始研究不同的构建工具如何改进开发工作流程。阅读这篇关于构建工具的比较文章,您将获得一些知识,以便更好地比较这些工具如何满足您的特定需求。

我们又在比较什么?

本指南仅介绍能够执行项目模块打包的构建工具,即将动态内容拼接到模块中,并将打包到静态资源文件中。此过程可以通过脚本、自动化和压缩功能来增强。并非所有构建工具都生来平等,也并非所有构建工具都能完成上述所有功能。有些工具需要更多配置,而有些则是即插即用的解决方案,可以满足您的大部分开箱即用需求。

您可能会(或逐渐形成)对构建工具功能的偏好。因此,本文不会选择某款构建工具来统领所有工具,而是会介绍每种工具的优势、陷阱以及理想的项目配置。

工具 优势 陷阱 理想项目
Browserify 简单 开发仅由社区插件驱动 小型项目或原型
Webpack 灵活的配置和代码拆分 配置可读性 中大型 Web 项目
Rollup.js 可共享库的捆绑 模块捆绑已内置于浏览器中 库或插件
Gulp 任务运行器 模块捆绑器的配套工具 项目需要额外的脚本
npm 内置于大多数 JavaScript 项目中 手动设置 适用于大多数项目
包裹 无需配置 可用文档较少 中大型 Web 项目
微束 占地面积小 可用文档较少 注重尺寸的库或插件

Browserify

优点:非常简单
缺点:项目开发速度已经大大放缓
理想项目:希望摆脱大量使用脚本标签并转而使用 CommonJS 要求的项目。

Browserify专注于简化入门,是 JavaScript 开发中模块打包的绝佳入门工具。Browserify 最初的设计初衷是帮助前端开发者在浏览器中像在服务端渲染Node应用一样使用CommonJS (require 语句) 。过去,Web 开发需要在页面上使用多个 script 标签来构建现代 Web 应用。Browserify 可以将所有 JavaScript 文件浏览器化为一个串联(组合和合并)的文件,以便轻松地将其作为单个 script 标签添加到页面上。

browserify-徽标

使用 Browserify 需要先安装 CLI。我建议使用命令行方式的 npm。

    npm install browserify
Enter fullscreen mode Exit fullscreen mode

安装后,您可以将应用程序的 JavaScript 入口点(很可能是 index.js)指向启动浏览器化过程的位置。

    browserify index.js > bundle.js
Enter fullscreen mode Exit fullscreen mode

结果是您的 JavaScript 的捆绑版本,可以包含在您的 index.html 中。

    <script src="bundle.js"></script>
Enter fullscreen mode Exit fullscreen mode

Browserify 的实现功能齐全,并专注于开箱即用的 JavaScript 改进。为了支持非 JavaScript 资源(例如 CSS 或图片)的打包,社区创建了一系列转换函数(所有函数都以 ify 结尾,真是巧妙之极)来为这些解决方案提供支持。我非常支持借助开源社区的力量来推进项目,但如果您正在尝试使用 Browserify,请注意:有些转换函数已经超过六个月没有更新了。话虽如此,您可以通过为项目解决方案提供插件来回馈社区,这其中还有很多其他方面。

与其他一些构建工具不同,Browserify 没有需要维护的标准配置文件。不过,您可以利用 Node.js 的package.json 文件来处理更高级的配置。Browserify 的工作是通过插件和 JavaScript 文件的内容推断出来的。对于不需要经常更新的项目来说,这可能是一件好事。但对于需要大量工具和优化的项目来说,缺少配置文件可能会成为一种负担,因为没有真实来源或地方来揭露构建工具的魔力。

查阅 Browserify 文档以及转换列表,了解它是否包含使你的开发工作流程顺畅所需的一切。你还可以使用本教程了解如何使用 Browserify 构建 React 应用,以了解其实际操作。如果你追求简洁,那么我会考虑在你的下一个项目中使用 Browserify。

Webpack

优势:项目支持积极,拥有大量开箱即用的功能
缺点:需要一些自定义配置才能正确使用
理想项目:希望随时掌握最新、最强大更新的项目。希望进行代码拆分的项目也应该考虑使用 webpack。

Webpack是一款基于四大核心概念的构建工具:入口、输出、插件和加载器。一旦理解了这些概念,就可以在项目中使用 Webpack 了。Webpack 在某些方面与 Browserify 类似,但通过插件社区提供了一些增强的功能。然而,Webpack 提供了更多开箱即用的功能,并计划持续添加更多功能,同时不断重新思考项目的设计。

webpack 徽标

我之前写过一篇从零开始使用Webpack 的指南,重点讲解了如何利用 Webpack CLI 构建 React 应用程序。Webpack 要求你创建一个单独的配置文件来支持你的 Webpack 构建工作。这个文件其实就是一个 JavaScript 对象,Webpack 使用它根据配置对象中的键和值在构建过程中启用和禁用某些功能。

    // example of a webpack.config.js

    module.exports = {
      entry:'./index.js',
      output: {
        filename: 'bundle.js'
      }
    }
Enter fullscreen mode Exit fullscreen mode

在配置中,您可以指定项目的入口点以及您希望放置打包文件的位置。这使得运行 Webpack 构建更加简单,因为您无需记住特定的命令,只需webpack创建构建即可。

    npm install webpack
    webpack
Enter fullscreen mode Exit fullscreen mode

Webpack 配置或许是添加新功能和工具来增强构建流程的便捷方式,但就像大多数“甜蜜”的东西一样,偶尔添加一些功能和工具可能会导致配置变得臃肿不堪,难以管理。一个看起来难以管理的配置,可能是因为项目开发团队担心在 Webpack 配置文件中添加过多内容会破坏构建,从而避免更改或更新 Webpack 配置。

React 团队通过将配置抽象到 create-react-app CLI 工具下方的隐藏脚本中,解决了这个 Webpack 问题。如果你查看这个隐藏的配置,会发现它包含一些你可能见过的布局最合理的配置注释。但是,它需要如此多的注释,这不禁让你怀疑,是否有更好的方法来实现如此精细的配置,而无需用大量的注释来支持每个决策。

    // excerpt from the creat-react-app's webpack config

    module.exports = {
      // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
      // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
      devtool: 'cheap-module-source-map',
      // These are the "entry points" to our application.
      // This means they will be the "root" imports that are included in JS bundle.
      // The first two entry points enable "hot" CSS and auto-refreshes for JS.
      entry: [
        // We ship a few polyfills by default:
        require.resolve('./polyfills'),
        // Include an alternative client for WebpackDevServer. A client's job is to
        // connect to WebpackDevServer by a socket and get notified about changes.
        // When you save a file, the client will either apply hot updates (in case
        // of CSS changes), or refresh the page (in case of JS changes). When you
        // make a syntax error, this client will display a syntax error overlay.
        // Note: instead of the default WebpackDevServer client, we use a custom one
        // to bring better experience for Create React App users. You can replace
        // the line below with these two lines if you prefer the stock client:
        // require.resolve('webpack-dev-server/client') + '?/',
        // require.resolve('webpack/hot/dev-server'),
        require.resolve('react-dev-utils/webpackHotDevClient'),
        // Finally, this is your app's code:
        paths.appIndexJs,
        // We include the app code last so that if there is a runtime error during
        // initialization, it doesn't blow up the WebpackDevServer client, and
        // changing JS code would still trigger a refresh.
      ],
      output: {
        // Add /* filename */ comments to generated require()s in the output.
        pathinfo: true,
        // This does not produce a real file. It's just the virtual path that is
        // served by WebpackDevServer in development. This is the JS bundle
        // containing code from all our entry points, and the Webpack runtime.
        filename: 'static/js/bundle.js',
        // There are also additional JS chunk files if you use code splitting.
        chunkFilename: 'static/js/[name].chunk.js',
        // This is the URL that app is served from. We use "/" in development.
        publicPath: publicPath,
        // Point sourcemap entries to original disk location (format as URL on Windows)
        devtoolModuleFilenameTemplate: info =>
          path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
      },

      // ... there is so much more to this
Enter fullscreen mode Exit fullscreen mode

Webpack 团队正在积极开发这个项目,并致力于解决配置方面的困惑。许多曾经需要 Webpack 插件才能实现的功能现在都包含在库本身中,包括 tree-shaking、uglifying,甚至 Web Assembly (WASM) 支持。完善的文档也使 Webpack 作为构建工具更加易于使用,并且自 Webpack 2(2016 年秋季)发布以来一直得到持续维护。

Webpack 不仅专注于模块打包,还内置了代码拆分功能。代码拆分是指在需要时,通过基于路由的单独页面打包,只加载所需内容的做法。这有可能提升页面加载速度和整体浏览体验。然而,代码拆分的学习曲线比较陡峭,我个人还没有完全掌握,但 Webpack 团队成员正在努力通过webpack.academy来拉平这条曲线。

社区构建的 Webpack 配置样板有很多,其中包括一个非常简洁的工具,名为Webpackbin。Webpackbin是一个用于构建和配置 Webpack 示例的沙盒。您可以从这里生成链接,这在研究 Webpack 配置时非常有用,因为作者通常会将他们的配置发布到沙盒中并提供 URL 来分享。

https://www.webpackbin.com/bins/-KeVMcQWGocAn5VJL6XL

Webpack 致力于成为内置组件(部分组件需单独购买)的构建工具。如今,Webpack 几乎可以解决您在构建 Web 应用程序时遇到的所有问题,但您可能仍需要仔细阅读手册(文档),才能使其按照您的喜好进行构建和运行。

汇总

优点:内置包管理功能
缺点:您需要确保您的项目已实现 ES6 语法
理想项目:希望在构建过程中使用稍微少一点的配置并已经使用最新 ESNext 功能(如 ES 模块)的项目

Rollup 是一个 JavaScript 模块打包器,可以将小段代码编译成更大、更复杂的代码。它使用新版 JavaScript ES6 模块系统(而非之前的 CommonJS 和 AMD 等特殊解决方案)来执行项目的打包(打包)。ES6 模块允许您自由无缝地组合您常用库中最有用的单个函数。

Rollup JS 徽标

您可以通过命令行开始使用 Rollup。只需指向 index.js 并为打包输出指定一个名称即可。

    npm install -D rollup
    rollup index.js --o bundle.js --f iife
Enter fullscreen mode Exit fullscreen mode

为了避免我们不断重复相同的命令,您可以选择添加一个 rollup.config.js 文件,类似于我们在 webpack 中看到的那样。同样的配置风险在 ght e 中也同样有效。

    // rollup.config.js
    export default {
      input: 'src/index.js',
      output: {
        file: 'bundle.js',
        format: 'cjs'
      }
    };
Enter fullscreen mode Exit fullscreen mode

Rollup 因其内置的包管理功能(而非 Web 应用程序的包管理功能)而受到越来越多的软件包和开源维护者的青睐。其中一些功能与通用模块定义(UMD) 的功能相似,使 Rollup 成为 JavaScript UMD 需求和 ES6 模块之间的桥梁。由于 ES6 是热门话题,因此 Rollup 无法在没有插件的情况下处理 CommonJS 依赖的文件。这仅限于尚未将 ES6 语法应用于其工作流程的旧项目。但是,如果您正在启动一个新项目,则不会有太多限制。

自 2017 年春季起,所有主流浏览器都原生支持 ES6 模块,这使得 Rollup 也有望获得新的竞争优势。Rollup 本身也支持 tree-shaking,能够从项目包中移除未使用的代码,你可以在rollup repl的示例中看到这一点。这对于需要页面优化帮助的项目来说非常有价值。

汇总站点

虽然 tree-shaking 看起来像一个小功能,但想想Momentjslodash这样的大型项目吧。Tree-shaking 提供了将库的所有部分从 bundle 中排除,只包含你正在使用的那部分的功能。

除了树之外,您还可以汇总更多内容,因此我鼓励您查阅汇总指南,以获取有关如何在下一个项目中利用 tree-shaking 和其他功能的更多信息。

Gulp

优点:用于连接其他构建工具之间的点的绝佳工具
缺点:如果您需要模块捆绑,那么这不是适合您的构建工具
理想项目:需要额外帮助将脚本功能纳入其构建过程的项目

Gulp是一款将脚本功能融入工作流程的工具。在列表中的所有工具中,我不会将 Gulp 用于模块打包,而是将其用作增强构建流程的工具。对于那些希望将脚本放入 JavaScript 中的开发人员来说,Gulp 是理想之选。Webpack 和 Browserify 所缺少的许多功能都可以通过 Gulp 命令链式调用来增强。Gulp 命令既可以用纯 JavaScript 手写,也可以利用 Gulp 庞大的插件社区来利用。

gulp 徽标

您可以将多个命令串联起来,使用 Gulp 为构建过程编写脚本。一些常见的操作可能是将图片发送到Cloudinary ,或者使用Algolia编译 JSON 文件来验证您的搜索。我坚信这正是 Gulp 的闪光点,并且它拥有大量社区构建的插件,可以处理从简单到复杂的脚本,例如在构建过程中处理 CVS 生成或图片处理等任务。

    // example gulp build
    gulp.task("build", function(callback) {
      runSequence(["css", "js"]);
    });

    gulp.task("css", () => (
      gulp.src("./src/css/*.css")
        .pipe(postcss([
          cssImport({from: "./src/css/main.css"}),
        ]))
        .pipe(gulp.dest("./dist"))
    ));

    gulp.task("js", (cb) => {
      //  using webpack ;)
      const myConfig = Object.assign({}, webpackConfig); 
      webpack(myConfig, (err, stats) => {
        if (err) throw new gutil.PluginError("webpack", err);
        gutil.log("[webpack]", stats.toString({
          colors: true,
          progress: true
        }));
        cb();
      });
    });
Enter fullscreen mode Exit fullscreen mode

Gulp 在构建过程中处理同步脚本和异步(使用插件)脚本时表现非常出色,但您很可能需要使用其他构建工具进行优化和转换。我倾向于使用 Gulp 进行快速脚本编写,但倾向于包含单独的打包器(如 Webpack 或 Browserify)来处理打包工作。这是我的个人偏好,也是我尝试保留 Gulp 作为模块打包之前的任务运行器来做它擅长的事情。Gulp 脚本可能会遇到 Webpack 配置所面临的相同问题,即配置过长且有大量注释,而这正是我要避免的。我建议您亲自尝试一下 Gulp,并浏览API 文档以获取有关如何使用它的示例。

npm

优点:不需要额外的依赖
缺点:脚本需要手动编写或收集
理想项目:小型项目和原型

提到 Gulp 脚本作为构建工具,我自然不能不提npm(Node 包管理器)。npm 通常被认为是一个包管理器,以及将 JavaScript 库托管在 CDN 上的中介。到目前为止,我提到的每个构建工具都利用 npm 通过命令行进行安装。npm 的功能远不止包管理。npm 的一项功能是运行脚本。

npm-scripts 功能可以完成许多现代构建工具的功能,同时减少软件包依赖和维护开销。仔细观察就会发现,我们讨论过的每个工具都包含一组纯 JavaScript 文件。这些文件可以通过 npm scripts 连接,并且无需依赖即可手动编写。

对于小型项目和原型,从 npm 开始就足够了,我建议您在评估构建工具之前先了解一下 npm。正如 npm 联合创始人 Laurie Voss 所解释的那样:“npm 的初衷是帮您摆脱繁琐的构建流程,而不是为了给您的构建过程增加繁琐的仪式感。您可以观看 Laurie 在之前 JSConf EU 上的演讲。

npm 项目的文档以及有关如何使用npm 作为构建工具的一般博客文章是学习更多信息的绝佳资源。

包裹

优点:无需配置
缺点:项目较新,参考文档较少
理想项目:希望快速上手的小型项目和原型

Parcel于 2017 年底问世,将所有繁琐的 JavaScript 配置点封装在一个小小的包中。Parcel 消除了构建工具的复杂性,并可与 JavaScript 领域最流行的插件(包括 Babel 转换)开箱即用。

包裹标志

与 Browserify 类似,Parcel 也无需配置文件,但也没有 Parcel 特有的插件。Parcel 依赖于 Babel 等现有的 JavaScript 生态系统项目来完成工作。Parcel 只是一个编排器。您还可以在 package.json 或 .babelrc 文件中包含 Babel 转换和插件,Parcel 会自动在构建过程中将其包含进去。无需额外配置,这是一个非常吸引人的特性。此外,您也无需再学习一个库来维护您的项目(也就是可怕的 JavaScript 疲劳)。

Parcel 的入门与其他类似,但您不需要为包提供输入和输出,而只需在脚本中提供入口。

    npm install parcel-bundler
    parcel build index.js
Enter fullscreen mode Exit fullscreen mode

其他功能可以在文档中找到,但剧透一下,它们要求你编写现代 JavaScript 才能实现。这个项目的底层其实没有什么神奇之处。你可以看看这个使用 Parcel 运行 React 应用程序的示例。正如之前提到的,这个项目还比较新,但前景看好。目前已经有一些很棒的文档,以及一个

微束

优点:零配置,占用空间极小
缺点:项目较新,可供参考的文档较少
理想项目:注重大小的项目,希望作为插件或附加组件与其他项目共享

如果您还没有听说过 Parcel,那么您很可能还没有听说过Microbundle,这是一款用于微型模块的零配置打包工具。Microbundle 由前面提到的Rollup项目提供支持,旨在通过移除配置步骤,将其模块打包提升到一个新的水平。与 Parcel 类似,它只使用 package.json 文件来打包您的项目。因此,请确保包含运行 JavaScript 和打包资源所需的所有必要依赖项。

    npm install microbundle
    microbundle
Enter fullscreen mode Exit fullscreen mode

如果没有提供入口文件,Microbundle 会默认你有一个 index.js 文件。如果没有提供输出文件,它还会创建一个 bundle 并压缩该文件。Microbundle 不仅会创建 bundle 版本,还会在 bundle 过程中提供一个 UMD 版本。

    // index.js
    function () {
      console.log("Hello World")
    }

    // microbundled into index.m.js
    function O(){console.log("FOOOOOOOOOO")}O();

    // microbundled into index.umd.js
    !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){console.log("FOOOOOOOOOO")});
Enter fullscreen mode Exit fullscreen mode

这对于嵌入到其他项目中的小型可共享项目非常有用。多亏了这篇文章,我现在才发现这个 Microbundle,但我发现它对Netlify Identity Widget很有用,Netlify Identity Widget 是一个旨在嵌入到大型项目中的项目,并且已经手动打包到 UMD 中了。

现在去建造一些东西

无论您的编程风格如何,总有一款适合您,而选择构建工具则取决于您想要的控制方式。我尝试提供从无配置到大量配置的渐进式方案。尽管选择众多,但所有构建工具都能与Netlify兼容,并且能够帮助您维护应用程序。因此,请尝试几种构建工具,看看哪一种适合您。

如果您非常喜欢这里未列出的工具,请发表评论并告诉我。

文章来源:https://dev.to/netlify/choosing-a-javascript-build-tool-to-config-or-not-config-2ia8
PREV
宣布 NgRx v16:与 Angular Signals、Functional Effects、Standalone Schematics 等集成!
NEXT
用神经网络玩井字游戏