前端工程化基石:构建工具,构建工具家族图谱🛠️

1,932 阅读9分钟

前端工程化基石:构建工具,构建工具家族图谱🛠️

前言

“学不动了, 真学不动了!”

“造轮子, 前端就是天天造轮子!”

“别更新了, 我要跟不上了!”

.....

为什么市面上已经有这么多构建工具了,有 esbuildwebpackrollup 等等等,还有源源不断的构建工具出现,重复的造轮子真的有必要吗,我们前端除了造轮子,没有其他事情了吗?在这篇文章开始之前,我们先讨论一个话题 -- 为什么构建工具层出不穷?这是不是无意义的「内卷」。 在推特上,有这么一篇帖子,讨论了重复造轮子的意义,得出了一个结论为了洞察而重新发明,为了影响力而重用,我认同这个观点。

x.png

原文链接

市面上出现了太多的构建工具,大家可能会在各种构建工具中难以抉择,今天我们就来统一的对这些构建工具进行统一分类。

构建工具

什么是构建工具,它负责将开发者编写的模块化代码( JS/TS/CSS/图片 等)及其依赖关系,经过编译、转换、优化、合并等处理,最终输出为浏览器可直接运行的高效静态资源。在早期中,由于项目的体型较小,因此我们手动处理依赖关系,但随着技术的更新迭代,项目的体型也越来越大,因此我们需要自动化的构建工具,帮我们去完成一系列的处理。

类别

在这里我们讨论两类构建工具,第一类是底层构建工具,例如 webpackrollupesbuild等,第二类是基于这些底层构建的高层封装工具,帮助我们以低配置,甚至零配置的方式进行打包,例如 tsuptsdown

底层构建工具

Webpack

  • 开发语言:JS
  • 优点
    • 历史积累,生态系统丰富
    • 强大的模块处理
    • 丰富的插件系统
  • 缺点
    • 配置复杂,调试成本高
    • 大型项目性能瓶颈明显
    • 冷启动慢
  • 适用于大型复杂 SPA 项目,微前端等

Rollup

  • 开发语言:JS
  • 优点
    • 原生支持 Tree Shaking
    • 插件系统功能丰富
    • 产物体积小
    • ES Modules 打包
  • 缺点
    • 不支持 HMR
    • 对某些类型资源类型,或 cjs 模块,需要第三方插件实现
  • 不适用于项目应用打包,适用于库模式打包

Esbuild

  • 开发语言:Go
  • 优点
    • 编译速度极快
    • 多线程编译
    • 零配置开箱即用
  • 缺点
    • 功能单一
    • 插件生态弱
  • 不适用于复杂应用

Rspack

  • 开发语言:Rust
  • 优点
    • Webpack 生态无缝迁移
    • Rust 驱动,冷启动快
    • 字节跳动开源,文档友好
    • 内置 Lightning CSS 加速
  • 缺点
    • 生态较新,插件覆盖不足
    • 文档成熟度待提升
  • 适用于 Webpack 技术债迁移,目前较为成熟的 Rust 构建方案

Turbopack

  • 开发语言:Rust
  • 优点:
    • Webpack 团队推出新版开发工具
    • 深度集成 Next.js
  • 缺点
    • 插件系统未成熟
  • 比较不推荐的打包工具,推出后不温不火,曾公布性能基准测试,声称比 Vite 快10倍,遭到 yyx 回怼

Rolldown

  • 开发语言: Rust
  • 优点
    • Rust 开发,构建速度快
    • 基于 Oxc 工具集合构建
    • 未来将作为 Vite 的底层构建工具
    • 兼容 Rollup API,兼容 Rollup 生态
  • 缺点
    • 目前仅发布 1.0 beta 版本,项目仍在开发中
    • 文档不完善

Rolldown 作为构建工具领域的明日之星,正受到开发者社区的万众瞩目。它即将成为 Vite 新一代的底层构建引擎,取代现有的 Esbuild 和 Rollup,而是通过性能跃升补强 Vite 的生产构建能力。

这一变革的期待值之所以高涨,源于 Vite 近年来在《JavaScript 现状调查报告》中的统治级表现:在构建工具满意度、采用率等核心榜单中持续霸榜首位,已成为前端生态最受欢迎的构建解决方案。而 Rolldown 作为 Vite 团队钦定的未来基座,自然承载着开发者对更快、更稳、更强大的构建体验的终极期待。

高层封装工具

Vite

底层框架:Esbuild / Rollup。在开发环境中使用 Esbuild,在生产环境中使用 Rollup;在开发阶段依靠 Esbuild 的特性,拥有极快的冷启动速度,而生产环境中则需要较长的打包时间;由于开发和生产环境中使用了两种不同的构建工具,因此可能出现构建产物不一致的结果,导致出现无法预测的错误;由于 Vite 依靠的是浏览器原生的 ESM 特性,在开发时构建大型项目,会出现加载过多的请求情况,导致白屏时间过长。

这也是目前 Vite 最受诟病的三大因素,因此 Vite 团队致力于开发 Rolldown 来解决这三个因素,官网推出基于 Rolldown 驱动的 Vite 目前以名为 rolldown-vite 的独立包提供,大家目前可以使用这个包体验由 Rolldown 驱动的效果。

Rsbuild

底层架构:Rspack。与框架无关,支持 ReactVueSolidSveltePreact 等框架的构建,对标 Vite,致力于提升前端的开发体验。

Rslib

底层架构:Rspack。专为 JavaScript/TypeScript 库开发设计,可用来打包工具库,组件库等。

unbuild

底层框架:Rollupunbuild 是专门为打包 NuxtNitroUnJS 库而创建的,由于 unbuild 的设计最初是有特定的打包对象,因此官方并未生成专门的文档也并未进行宣传,仅有 Readme.md 说明文档,这也是大多数人并未听说过该构建工具的原因。

obuild

底层框架:Rolldown。与 unbuild 为同一个作者,也是用来代替 unbuild 的一个工具,因此与 unbuild 类似有特定的打包对象,也并未进行宣传。

tsup

底层框架:Esbuild。构建 TypeScript 库的最简单、最快捷的构建工具,也是目前库仓库打包最流行的工具之一。

tsdown

底层框架:Rolldowntsdown 是由 Rolldown 团队推出的全新库构建工具,目前正处于积极开发阶段。其核心定位为 基于 Rust 工具链的 TypeScript 库构建解决方案,设计上深度参考 tsup 的功能范式,支持一键式迁移现有 tsup 项目。

作为 Rolldown 生态的官方成员,它与 Vue 框架存在紧密的技术协同—— 可利用 unplugin-vue 插件,对 Vue 组件库的编译与打包。在功能定位上,tsdownobuild 高度趋同,均致力于通过 Rust 驱动实现库构建的极速体验。

关于 rolldown-vite

尤大在五月三十日刚发布了一篇博客,详细的介绍了 rolldown-vite,有兴趣的小伙伴可以阅读一下。

总结:

  • rolldown-vite 已集成了 rolldown 作为底层构建工具,构建速度显著加快。
  • 但目前仍处于测试阶段,插件仍存在部分兼容性问题,旨在用户体验,以及发现问题,解决问题。
  • 不建议在生产环境使用,可能会存在无法预知的错误。

以下是我自己的测试结果:

vite

vite.png

rolldown-vite

rolldown-vite.png

总的来说,rolldown-vite 的构建速度远快于 vite,但可能由于硬件设施的问题,以及插件的兼容性问题 (部分插件仍需要借助 esbuild,在后续中将 transformWithWsbuild 迁移至 transformWithOxc 应该还会快上不少。) 感观上觉得 rolldown-vite 还是不够快,差距并不明显。

tsdown

现在,让我们以 tsdown 作为构建工具,通过实际动手操作来深入探索它的功能特性。

项目准备

初始化项目,安装依赖

npm init -y
npm i -D tsdown

文件结构

├── dist/               # 构建后自动生成
├── node_modules/       # npm 安装后自动生成
├── src/
│   ├── index.ts        # 项目入口文件
│   └── utils.ts        # 工具函数
├── package.json
└── tsdown.config.ts    # 配置文件

tsdown 配置

// index.ts
export { add, foo } from './utils'
// utils.ts
export const foo = Object.defineProperties(
  {
    bar: 'bar',
  },
  {
    baz: {
      value: 'baz',
      enumerable: true,
    },
  },
)

export function add(a: number, b: number) {
  return a + b
}
// tsdown.config.ts
import { defineConfig } from 'tsdown'

export default defineConfig({
  // 文件入口,可以指定单一入口(字符串),也可以指定多个入口(数组)
  entry: ['./src/index.ts'],
  // 输出目录,默认为 dist
  outDir: './dist',
  // 构建目标,会将代码中的语法进行降级
  // 例如当你指定为 es5 时,代码中的箭头函数会被降级为普通函数
  target: 'es6',
  // 构建格式,esm | cjs | iife,默认为 esm,可以指定一个(字符串),也可以指定多个(数组)
  format: 'esm',
  // 清理,每次构建之前清理输出目录(默认为 dist)
  clean: false,
  // 声明文件,项目中必须安装 typescript
  dts: true,
  // 运行平台,node | browser | neutral
  platform: 'neutral',
  // 监听模式,检测文件内容发生变化,自动重新打包
  watch: false,
  // 源码映射
  sourcemap: true,
  // 树摇,默认开启,没有使用到的函数会被删除,不会打进最终产物中
  treeshake: true,
  // 拆包,默认关闭,开启后,会拆分出多个文件,每个文件对应一个入口文件
  unbundle: false,
  // 压缩代码,减少代码体积
  minify: true,
  // 指定外部依赖,并不会将 external 的包打进最终产物中
  external: ['react'],
  // 强制将 noExternal 的包打进最终产物中
  noExternal: ['vue'],
})
{
  "scripts": {
    "build": "tsdown",
    "dev": "tsdown --watch"
  }
}

启动命令后,构建完成后,dist 目录下为最终构建产物,用户就能将该产物进行服务器部署,或者 npm 包发布。

总结

选择构建工具的核心在于契合项目需求。 大型复杂项目可考虑底层工具,以获得丰富的配置选项和功能;小型简单项目则适合高层封装工具,它们开箱即用,能显著降低配置成本。

大家也不妨尝鲜 Rolldowntsdown,它们未来有望成为前端圈的明星项目。欢迎体验并反馈问题,一起帮助项目优化,共建更健壮的工具生态!🚀

构建工具如星辰,各有其独特轨迹。每款新工具诞生,皆是对前人智慧的淬炼——萃取精华,摒弃沉疴。若技术苍穹仅存孤星,我们将失去选择最适合自身星座的权利,这何尝不是文明进程中的遗憾?

此刻,你还会认为重复造轮是种谬误吗?当每个轮毂都雕刻着创新的年轮,承载着不同的方向......