常见构建工具使用与原理浅析

发布于:2025-07-06 ⋅ 阅读:(15) ⋅ 点赞:(0)

1. 初版构建工具

1.1. Grunt

Grunt 是前端第一个正式的构建工具,它基于 Node.js 开发。Grunt 同样是基于插件实现功能拓展增强,但对于像Webpack上很多能力,如HMR、Scope Hoisting等都是不支持的,可以作为学习Webpack前的了解。

Grunt 更像是一种自动化的配置工具集,就如官方所说,Grunt 是 The JavaScript Task Runner,每个 Grunt 任务通常必须创建中间文件将结果传递给其他任务。

所能实现的功能包括:检查每个 JS 文件语法、合并两个 JS 文件、将合并后的 JS 文件压缩、将 SCSS 文件编译、将Less文件转换为CSS文件等等。

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        concat: {
            options: {
                separator: ';',
            },
            dist: {
                src: ['src/**/*.js'],
                dest: 'dist/<%= pkg.name %>.js'
            }
        },
        uglify: {
            options: {
                banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
            },
            dist: {
                files: {
                    'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
                }
            }
        },
        quit: {
            files: ['test/**/*.html']
        },
        jshint: {
            files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
            options: {
                // options here to override JSHint defaults
                globals: {
                    jQuery: true,
                    console: true,
                    module: true,
                    document: true
                }
            }
        },
        watch: {
            files: ['<%= jshint.files %>'],
            tasks: ['jshint', 'quit']
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-quit');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-concat');

    grunt.registerTask('test', ['jshint', 'quit']);
    grunt.registerTask('default', ['jshint', 'quit', 'concat', 'uglify']);
};

1.2. Gulp

Gulp跟Grunt类似,都是基于task驱动执行的,可以完成javascript/coffee/sass/less/html/image/css 等文件的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成。Gulp的优点在于Gulp更倾向于写代码的方式,但相关的插件资源不如Grunt。

同样的,现代的构建工具基本不使用Gulp了,有兴趣看官网自行学习,这里不详细介绍。

const { series, parallel } = require('gulp');

function clean(cb) {
  // body omitted
  cb();
}

function css(cb) {
  // body omitted
  cb();
}

function javascript(cb) {
  // body omitted
  cb();
}

exports.build = series(clean, parallel(css, javascript));

2. 基于Webpack改进的构建工具

2.1. Rollup

Rollup 和 Webpack 都通过解析 JavaScript 的依赖树,将代码打包成适合浏览器或 Node.js 执行的指定版本 JavaScript。然而,Rollup 更轻量化,生成的代码不会像 Webpack 那样包含大量的内部结构,而是尽可能保持代码的原始状态。配置方面,Rollup 相对简单,但由于缺乏 devServer 和 HMR热模块替换功能,因此通常用于 JavaScript 库的开发,而非应用开发。

Rollup 主要负责将代码转码为目标 JavaScript,并且可以根据需要生成支持 UMD、CommonJS 和 ES 模块格式的代码。Vue、React、Angular 等框架的源码打包工具中都能看到 Rollup 的应用痕迹。

2.1.1. 使用方式

1. 浏览器环境使用的应用程序

(1). 无需考虑浏览器兼容问题

  • 开发者编写ES模块代码 => Rollup 通过入口文件递归识别 ESM 模块 => 最终生成一个或多个 bundle.js => 浏览器可以直接通过 <script type="module"> 引入并运行。

(2). 需考虑浏览器兼容问题

  • 这种情况下会复杂一些,需要额外使用 polyfill 库,或者与 Webpack 结合使用,以确保兼容性。

2. 打包成 npm 包

(1). ESM 模块打包

  • 开发者编写 ESM 代码 => Rollup 通过入口文件递归识别 ESM 模块 => 可以配置输出多种模块格式(ESM、CJS、UMD、AMD) => 最终打包成一个或多个 bundle.js。

  • 如果需要生成 CJS(CommonJS)格式的模块,开发者可以使用 @rollup/plugin-commonjs 插件。

(2). Rollup 更适合打包 JS 库

  • 由于 Rollup 生成的库支持 ESM 的 tree shaking(摇树优化),这有助于将库的体积最小化。因此,许多像 React、Vue 等库的源码都是用 Rollup 打包的。

3. Webpack 与 Rollup 打包的差异

  • 历史背景:Webpack 诞生于 ESM 标准出台之前,当时浏览器只能通过 script 标签加载模块,且没有作用域。为了实现模块的作用域隔离,Webpack 使用了 IIFE(立即执行函数表达式),这也是为什么 Webpack 打包后的代码结构看起来比较复杂,模块之间需要通过函数来隔离作用域。

  • CJS 兼容性:Webpack 需要在浏览器中模拟 CommonJS(CJS)的 require 和 module.exports 方法,因此注入了大量代码来实现这一功能,这也是 Webpack 打包后代码混乱的原因之一。

  • 浏览器不支持 CJS 的原因:CJS 是同步的,在 Node.js 环境下运行速度快,因为无需等待网络响应。 浏览器是客户端,需要等待服务器响应,因此不能同步执行,否则会影响用户体验。 Webpack 为了兼容早期发布到 npm 的 CJS 包,保留了 IIFE 结构和代码注入,导致打包后的代码结构复杂且混乱。

  • Rollup 的优势:Rollup 诞生于 ESM 标准出台之后,专为 ESM 设计,没有历史包袱,因此能够生成精简且没有额外注入的代码。

2.1.2. 使用总结

rollup在构建JavaScript方面比Webpack有更大的优势:

  • 构建速度明显快于Webpack。

  • 生成的代码量很小。

  • 配置方式其实非常简单。

2.2. Parcel

与 Webpack 相比,Parcel 采用 assets 方式组织文件,可以直接构建任何类型的文件,而 Webpack 则必须以 JS 为入口组织其他文件,这在使用体验上是一种提升。

Parcel 的速度优势来自于多核处理和文件系统缓存技术,这使得二次构建更快。其缓存机制使用 C++ 编写,效率更高,类似于 Webpack 的 dll 插件。此外,虽然 Parcel 本身是零配置的,但针对 HTML、JS 和 CSS 文件的处理仍需配置 .posthtmlrc、.babelrc 和 .postcssrc 文件。因此,Parcel 更适合小型、简单的项目,对于定制化需求较高的项目,建议使用 Webpack,因为其社区资源更为丰富。

2.2.1. 主要特点

Parcel 的主要特点包括: