前端 Bundler,即前端打包工具,用于处理和优化前端资源(JavaScript、CSS、HTML、图片、字体等)的工具。它将这些资源组合并捆绑成一个或多个文件,以减少加载时间和提高应用性能
为什么需要 bundler
随着现代前端开发的复杂度不断增加,Bundler 作为构建过程的重要组成部分,帮助开发者管理复杂的依赖关系、进行代码拆分、实现模块化开发,并优化构建输出,已经成为开发流程中不可或缺的一部分
- 模块化开发:支持现代 JavaScript 模块系统,提高代码可维护性。
- 减少 HTTP 请求:将多个文件合并打包,减少请求数量,提高加载速度。
- 代码转译与兼容性:将现代 JavaScript 转译成兼容的旧版本。
- 优化与压缩:自动优化和压缩代码,减小文件体积。
- 静态资源处理:处理和优化 CSS、图片、字体等静态资源。
- 提升开发体验:提供热模块替换等功能,提高开发效率。
- 代码分割与懒加载:按需加载代码,提高执行性能。
- 自动化与任务管理:简化和自动化构建任务,提升开发过程效率。
Webpack
Webpack 是目前最流行的前端打包工具,功能强大,插件生态丰富,适合处理复杂的前端项目。
- 功能全面、生态丰富、高度可定制,几乎可以满足所有需求
- 配置繁琐复杂,学习成本高
- 性能相对于 Go、Rust 编写的 bundler 较慢
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js', // 入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录
filename: 'bundle.js', // 输出文件名
clean: true // 清理输出目录
},
module: {
rules: [
{
test: /.js$/, // 匹配.js文件(使用正则表达式)
exclude: /node_modules/, // 排除node_modules目录
use: {
loader: 'babel-loader', // 使用babel-loader
}
},
{
test: /.css$/, // 匹配.css文件
use: ['style-loader', 'css-loader'] // 依次使用style-loader和css-loader
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html', // 指定模板文件
filename: 'index.html', // 输出文件名
})
],
devServer: {
contentBase: path.join(__dirname, 'public'), // 指定静态文件目录
compress: true, // 启用gzip压缩
port: 9000, // 端口号
open: true, // 自动打开浏览器
hot: true // 启用热模块替换
},
resolve: {
extensions: ['.js', '.jsx'] // 自动解析文件扩展名
},
mode: 'development' // 设置模式为开发模式
};
如果应用规模不大,或者暂时对构建性能的要求并不高,Webpack 是最保险的选择
Vite
严格来讲 Vite 并不能仅仅是 bundler,Vite 既是一个前端开发服务器和任务运行器,也是一个 bundler,只不过在不同阶段,它的功能侧重点有所不同
- 开发阶段:主要对模块进行即时编译和 HMR,而不进行传统的打包,因此这个阶段可以认为它不是传统的 bundler。
- 生产构建阶段:使用 Rollup 进行完整的打包和优化工作,因此在这个阶段,它确实可以被认为是一个 bundler。
这种双重策略使得 Vite 能够在提供极快开发体验的同时,也能生成高效的生产代码,从而更好地适应现代前端开发的需求。
mport { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
input: 'src/main.js',
output: {
entryFileNames: `assets/[name].js`,
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`
},
}
}
});
如果使用 Vue 那可以直接应用 Vite,使用 React 等如果没有历史包袱,Vite 也是一个不错的选择,如果是老项目建议沿用 Webpack 或替换为 Rspack 等和 Webpack 生态兼容的选择
ESBuild
ESBuild 是一个新的打包工具,因其极高的编译速度而迅速受到关注和青睐,使用 Go 语言编写。
- 高速构建:编译速度非常快,是 Webpack 的数倍。适合开发环境和快速构建。
- 简单易用:配置简洁,适合快速上手。
- 生态系统较小:插件和加载器数量不如 Webpack 丰富,某些复杂需求可能无法实现。
- 功能覆盖不完全:目前不支持所有 Webpack 的高级特性,如复杂的代码分割策略。
ESBuild 取代 Webpack 独立使用的话生态和代码风格=功能的缺失可能会使大问题,但可以作为 Webpack 的一个插件使用,以获得 Webpack 的生态系统和配置灵活性的同时,利用 ESBuild 的高效编译能力来加速构建过程
module.exports = {
entry: './src/index.tsx', // 入口文件
output: {
filename: 'bundle.js', // 打包后的文件名
path: path.resolve(__dirname, 'dist') // 输出路径
},
module: {
rules: [
{
test: /.tsx?$/, // 匹配 TypeScript 和 TSX 文件
loader: 'esbuild-loader',
options: {
loader: 'tsx', // 适配 TypeScript 和 React 的 JSX
target: 'es2015' // 指定编译目标为 ES2015
}
},
{
test: /.css$/, // 匹配 CSS 文件
use: ['style-loader', 'css-loader'] // 使用 style-loader 和 css-loader 加载 CSS
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js'] // 解析文件扩展名
},
devtool: 'source-map' // 启用 source map 以便调试
};
Rspack
我们创建 Rspack 的原因,是为了解决在字节跳动维护构建工具时遇到的各种性能问题。由于字节跳动内部存在许多巨石应用,它们都具有复杂的构建配置,生产环境的构建需要耗费十几分钟,甚至超过半小时;开发环境的耗时也超过十几分钟。
我们在 webpack 上尝试了多种方法来优化这些巨石应用,但是效果甚微。我们意识到在 webpack 上的优化已经难以为继,必须要从底层改造,才能适应我们的需求。
Rspack 是字节跳动技术团队为了解决构建性能使用 Rust 开发的 bundler
- 使用 Rust 编写,并且将大量的 loader 内置,带来了显著的性能提升和配置简化
- 配置基于 webpack 设计实现,开发者可以非常轻松地将项目从 webpack 迁移至 rspack
- 虽然 Rspack 内置了一些常见的加载器,但其生态系统的插件和加载器数量不如 Webpack 丰富
- 作为一个新兴的构建工具,社区支持和文档资源相对较少
- 与现有工具链和流行框架的集成度不如 Webpack
Rspack 相对 Turbopack 还是很有潜力,但目前在成长期,尤其是作为国人发布的产品,海外的参与度在前期可能有限,考虑到社区支持和文档资源,建议观望
Respack 2024.08.28 发布了 v1.0,新项目可以尝试一下了
const path = require('path');
module.exports = {
entry: './src/index.tsx',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
module: {
rules: [
{
test: /.(ts|tsx|js)$/,
loader: 'rspack-loader',
options: {
loader: 'tsx',
target: 'es2015'
}
},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
},
devtool: 'source-map'
};
Turbopack
Turbopack 是 Webpack 的作者使用 Rust 语言开发一个前端模块化的工具,目标是取代 Webpack,目前 Turbopack 仍然处于 AIpha 阶段,离正式运用到生产环境还有不少时间。
- 快确实是快,但主要 提升来自于 SWC
- Turbopack 提供了一些基础插件和扩展,能够支持常见的文件处理和优化任务,但仍然与 Webpack 和其他成熟工具的插件生态系统有差距
- 作为新兴工具,Turbopack 的社区相对来说还在发展初期,社区规模和活跃度与 Webpack 等工具相比还不大
const path = require('path');
module.exports = {
entry: './src/index.tsx', // 入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录
filename: 'bundle.js', // 输出文件名
},
resolve: {
extensions: ['.ts', '.tsx', '.js'], // 自动解析扩展名
alias: {
'@': path.resolve(__dirname, 'src'), // 别名配置
},
},
module: {
rules: [
{
test: /.tsx?$/, // 匹配 .ts 或 .tsx 文件
use: 'ts-loader', // 使用 ts-loader 处理
exclude: /node_modules/, // 排除 node_modules 目录
},
{
test: /.css$/, // 匹配 .css 文件
use: ['style-loader', 'css-loader'], // 依次使用 style-loader 和 css-loader
}
],
},
devServer: {
contentBase: path.join(__dirname, 'public'), // 指定内容目录
compress: true, // 启用 gzip 压缩
port: 3000, // 端口号
hot: true, // 启用 HMR(热模块替换)
open: true, // 自动打开浏览器
},
mode: 'development', // 设置开发模式
};
Turbopack 被设计为 Next.js 的一个未来核心构建工具,以替代 Webpack 和其他构建工具。这种紧密集成在 Next.js 项目中有一定的优势,其它项目不建议使用
Rollup
Rollup 从一开始就设计为打包 JavaScript 库,因此在构建库文件时有很多优化措施。例如,输出更干净的代码结构、更少的开销(boilerplate)等,顺便说一句 Rollup 是第一个广泛应用和推广 Tree Shaking 的 JavaScript 模块打包工具
Rollup 原生支持多种模块格式(如 ES6、CommonJS、UMD、AMD 等),这使得它在发布兼容性库时非常方便。图表四兄弟 D3.js、Three.js、Chart.js、Highcharts 都使用 rollup 进行打包,Echarts、LodashES、Preact、Svelte、dayjs、date-fns 等也在使用 rollup,当然 vite 目前底层还在使用 Rollup 在生产阶段构建(使用 Rust 写的 rolldown 已经开源了,未来可能替换掉 Rollup)
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import postcss from 'rollup-plugin-postcss';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js', // 入口文件
output: {
file: 'dist/bundle.js', // 输出文件
format: 'iife', // 立即调用函数表达式(IIFE)格式适合浏览器环境
sourcemap: true // 生成sourcemap文件以便调试
},
plugins: [
resolve(), // 处理模块路径
commonjs(), // 转换CommonJS模块为ES6
babel({
exclude: 'node_modules/**', // 排除node_modules目录
babelHelpers: 'bundled'
}),
postcss({
extract: true // 将CSS提取到单独的文件
}),
terser() // 压缩输出文件
]
};
Rollup 其实没有太多和 Webpack 对比的必要,插件的生态的丰富度和配置灵活性肯定不及 Webpack,但 Rollup 更适合于开发和打包 JavaScript 库,尤其是希望生成干净、体积小的可重用模块时
SWC
虽然我们对 SWC 的理解主要是作为代码转译工具使用,但 SWC/swcpack 也有 bundler 的能力
SWC can be used for both compilation and bundling. For compilation, it takes JavaScript / TypeScript files using modern JavaScript features and outputs valid code that is supported by all major browsers.
但不幸的是 SWC 作者主要精力在 Turbopack 上
This feature is still under construction. Also, the main author of SWC works for Turbopack(opens in a new tab) by Vercel, so this feature is not something that will be actively developed.
建议目前不要使用,反而希望 SWC 在代码转译生态上发发力