#毫无营养,只是第一次整理的初稿,请阅读参考文章,而且废话挺多哈哈。
一,为什么我整理这么一篇文章?
前端时间整理了包管理器之间的对比,带给我的进步很大。这次打算整理一下打包器之间的对比,因为前段时间在面试题里看到一些构建工具的内容,打算对比一下不同的构建工具。
其实对比这些相同的技术解决方案很有用,去看他们的差别,大家为什么要再造一个轮子,新轮子的优点是什么,以及实现的原理是什么,说不定以后开发能用的上。
二,构建工具是用来干什么的?
构建工具简单的说就是自动化工具和转换工具,可以理解为帮助我们自动做一些操作,简化开发。
常见的操作:
- 转换操作(将我们使用的高级工具或写法转换为浏览器认识的写法):ES6/CoffeeScript->ES5、Jade->html、Less/Sass/Stylus->css
- 打包操作(将写在不同文件里的代码(称之为模块)重新整理到一个文件里)
构建工具的功能很多,而且很多工具只侧重部分功能(很多工具只实现一个功能,但是也属于构建工具的行列),这里通过功能的方式讲解一下。
常见的耳熟能详的构建工具:Webpack、Vite、Parcel、esbuild、swc、Rollup、Rome、Turbopack 等等。
1,将 ES6/CoffeeScript 转换为 ES5
背景:
我们的前端代码是依赖于浏览器环境执行的,不同版本的浏览器支持的代码版本不同。然而前端 JS 版本是不断迭代的,2015 年发布了 ES6 版本,由于其实用性让众多开发者都转向使用 ES6 编码,然后还有很多人使用低版本的浏览器且不兼容 ES6(其实只用 1.25%的设备浏览器没有完全支持,很少了,参考网址)。为了兼容这些老设备,就出现了将 ES6 转换为 ES5 的转码器。
实现的工具:Babel、Traceur、Webpack 等一众重型构建工具
历史:
babel 是 2014 年发布的老牌的转码器(它自称是编译器,但是要理解其真正含义,就是将 ES6->ES5),由社区进行维护;而 Traceur 是由谷歌在 2013 年发布的工具,旨在让开发者更早使用未来的 ES6 版本,但是由于 Babel 广泛的社区支持以及 Babel 后来出现的插件系统的火爆,Traceur 于 2018 年宣布进入维护模式。
2,打包 JS 模块(最核心的功能)
背景:
打包的概念是随着模块化的出现而出现的,随着我们前端要写的代码量越来越大,模块化变得越来越有必要,大家希望把同一功能的代码写在一个单独的文件里并且可以方便的引用其他模块的代码,但是当时的浏览器默认是不支持这种模块化操作的(ES6 开始支持),所以就出现了很多工具,它们的作用是把我们的这种高级写法(写在不同文件里的写法)转换为浏览器支持的写法(单文件的写法),这就是打包。
实现的工具:Browserify、RequireJS、Webpack 等一众重型构建工具
历史:
首先要提到的是两种模块化规范(模块化的前提就是大家都按照同一种写法来编写模块,否则不同人写法不一样还怎么使用模块):CommonJS、AMD(asynchronous module definition) 关于模块化的对比我的另一篇文章有说明。
一开始的模块化出现在 nodejs 服务端,nodejs 默认支持 CommonJS 规范的模块化,浏览器端开发人员很自然地想要客户端模块,但是 CommonJS 是同步加载的方式,在服务器端没有太大问题(因为所有模块都存在本地硬盘,等待时间就是硬盘读取时间),但是在浏览器端存在很大问题(浏览器的模块都存在服务器端,等待时间取决于网速快慢,浏览器可能处于假死状态),因此浏览器端使用的是 AMD 规范(即异步加载)。这也就诞生了我们的这些打包工具。
三,常见构建工具解析
本来打算通过功能将不同的工具进行对比,后来发现自己根本把握不住它们之间的功能,所以只能逐个工具的阅读资料,然后总结要点以及优缺点,最后总结一下他们之间的对比。
构建工具对比网站(不全):https://bundlers.tooling.report/
1,Webpack
背景:
webpack 出现于 2012 年,当时 web 界面开发变得更加复杂,需要处理不同类型文件和依赖关系。Tobias Koppers 开发了 webpack 来处理这个问题,后来 webpack 逐渐流行并引入了一些新特性:Tree Shaking(摇树优化)、Code Splittting(代码分割)、mode(模式)。
2021 年前的神!
核心原理:
把每个 JS 模块的代码包在一个函数里,将这些模块形成的函数放到一个 bundle 文件里,并加入一个辅助函数(用来缓存模块并且计算返回模块导出)。运行时只需要从入口模块开始计算模块导出即可获取并缓存全部模块。
工作流程:
- 读取 webpack 配置项
- 注册相关 hooks
- 以入口脚本起始,遍历解析依赖
- 遍历过程中,对不同类型文件使用不同的 loader 编译文件
- 基于事件执行机制,执行插件任务
体系架构:
功能 | 作用 |
---|---|
代码分割(code splitting) | 对静态资源进行分割,以实现最合理的按需加载策略,对性能影响较大 |
Hashing(对打包资源进行版本信息映射) | 对不同模块的依赖关系进行分析,并根据依赖关系,最合理地利用缓存机制 |
Output Module Formats(输出包形式) | 满足不同用户需求,输出 ESM/CommonJS 等 |
Transformations(转义) | ES6->ES5/压缩 JS 代码/无用代码删除(DCE) |
Non-JavaScript Resources | 除 JS 外资源处理(CSS、图片、svg 等) |
生态:
- loader 机制:用于支持 JS 外的资源类型,比如 CSS、svg 等
- 插件机制(以及 hooks):用于支持对 loader 预处理结果进行优化处理
2,Vite
背景:
尤雨溪在 2020 年开始写的一个项目,旨在充分利用现代浏览器的特性(ES6),减少开发者在开发过程中的等待时间和体验。
核心思路:
开发环境 | 思路 | 优势 |
---|---|---|
开发环境 | 启用一个开发服务器(koa 服务器),服务端按需编译模块(使用 esbuild 打包),浏览器端基于 ES imports 解析模块 | 跳过打包过程,启动速度快,而且热更新速度极快,且不会随着模块增多而变慢 |
生产环境 | Rollup 打包 | rollup 打包文件体积更小 |
Vite server 所有逻辑都依赖中间件实现,这些中间件拦截请求之后,完成如下内容:
- 处理 ESM 语法,比如将 import 第三方依赖路径转换为浏览器可识别的依赖路径
- 对.ts、.vue 文件进行即时编译
- 对 Sass/less 的需要预编译的模块进行编译
- 和浏览器建立 Socket 连接,实现 HMR
3,Parcel
4,Esbuild
5,Gulp
6,swc
7,Rollup
#参考资料
新一代构建工具(1):对比 rollup/parcel/esbuild—esbuild 脱颖而出-腾讯云开发者社区-腾讯云 (tencent.com)
Facebook 出品的前端工具链凉了!-51CTO.COM
[ECMAScript 6_百度百科 (baidu.com)](https://baike.baidu.com/item/ECMAScript 6/22641264)
ES6 以上版本代码要不要转码成 ES5 ? - 掘金 (juejin.cn)
“ES6” | Can I use… Support tables for HTML5, CSS3, etc
javascript - require 和 ES6 import 的区别 - 个人文章 - SegmentFault 思否
CommonJS、requirejs、ES6 的对比 - 简书 (jianshu.com)
Javascript 模块化编程(三):require.js 的用法 - 阮一峰的网络日志 (ruanyifeng.com)
了解 Browserify - Darren Ji - 博客园 (cnblogs.com)