课程目标
-
掌握前端自动化构建体系搭建思路;
-
了解常见构建工具生态;
-
掌握常见构建相关生态;
-
掌握前端自动化构建实现方案;
-
掌握自动化构建流程与原理;
知识要点
现代构建工具
Rollup
Rollup 是一个专注于 JavaScript 类库打包 的模块打包器,它尤其擅长处理 ES6 模块,并能通过高效的 Tree-shaking 来优化输出代。
Rollup和 Webpack 的主要区别
Rollup 更专注于 ES Modules 的打包,利用 ESM 的特性可以构建出结构更扁平、性能更出众的类库。它通常能生成更小巧、更高效的 bundle。配置相对简单 ,但因为没有devServer和HMR,所以一般rollup用于类库编写(JS库开发),而非业务开发。
Webpack 功能非常全面,致力于前端工程化的完整解决方案。它具备强大的代码分割、静态资源处理、热模块替换 (HMR) 等高级功能,更适合构建复杂的应用程序;
针对一个相同的demo,webpack和rollup打包出的体积相差极大:
webpack诞生于ESM标准出来前,CommonJs出来后,当时的浏览器只能通过script标签加载模块; script标签加载代码是没有作用域的,只能在代码内 用iife的方式 实现作用域效果。

Esbuild
Esbuild实践
javascript
// esbuild.config.js
import esbuild from 'esbuild'
const config = {
entryPoints: ['src/index.tsx'], // 支持 TS/TSX/JS/JSX
bundle: true, // 必须为 true 才能打包依赖
outdir: 'dist',
platform: 'browser', // 'node', 'neutral'
format: 'esm', // 'cjs', 'iife'
sourcemap: true, // 开发环境用 'linked',生产环境用 true 或 'external'
minify: true, // 生产环境开启
target: ['es2020'], // 根据你的目标浏览器设置
}
// 区分开发和生产环境
if (process.env.NODE_ENV === 'production') {
await esbuild.build(config)
} else {
const ctx = await esbuild.context(config)
// await ctx.watch() // 监听文件变化
await ctx.serve({ servedir: 'dist', port: 3000 }) // 提供服务并热重载(非 HMR)
}
构建的技术选型与方案
-
初始化项目
-
依赖盘点与安装(在架构时,分析你们的项目前期需要那些库及依赖,安装它们)
-
运行你确定的一些工程化脚本
-
start build
-
test、lint、type-check
-
基于 git 钩子做一些事情 commit pre-commit
-
-
打包构建
-
(团队建设 团队提效)规范的确立:ts、eslint、stylelint、spellcheck(拼写检查),git flow 规范
-
git flow 规范
=> 提效 =》减少 bug 提高代码质量
=》 cli 脚手架 统一脚手架 遵循规范
构建优化
输出分析
我们需要对输出结果做分析,以决定下一步的优化方向。
最直接的分析方法就是去阅读 Webpack 输出的代码,但由于 Webpack 输出的代码可读性非常差而且文件非常大,这会让你非常头疼。 为了更简单直观的分析输出结果,社区中出现了许多可视化的分析工具。这些工具以图形的方式把结果更加直观的展示出来,让你快速看到问题所在。
javascript
"buildJson": "webpack --profile --json > stats.json",
官方的可视化分析工具
打开 Webpack Analyse 链接的网页后,你就会看到一个弹窗提示你上传 JSON 文件,也就是需要上传上面讲到的 stats.json 文件,如图:
webpack-bundle-analyzer
它能方便的让你知道:
-
打包出的文件中都包含了什么;
-
每个文件的尺寸在总体中的占比,一眼看出哪些文件尺寸大;
-
模块之间的包含关系;
-
每个文件的 Gzip 后的大小;
Esbuild-loader
esbuild-loader 是一个基于 esbuild 的 webpack 加载器,可以显著提升构建速度。
javascript
// webpack.config.js
const { ESBuildMinifyPlugin } = require('esbuild-loader');
module.exports = {
mode: 'development',
module: {
rules: [
// JavaScript/TypeScript
{
test: /\.(js|jsx|ts|tsx)$/,
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
jsx: 'automatic' // 或 'preserve', 'transform'
}
},
// CSS
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'esbuild-loader',
options: {
loader: 'css',
minify: true
}
}
]
}
]
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx']
}
};
使用swc-loader优化
SWC虽然名义上是编译器,但它实际上是compiler + bundler,只不过目前的bundler功能还有待完善提高;
SWC 是用 Rust 语言编写的,其性能优势非常明显。它利用了 Rust 语言的高效性和底层优化机制,在对代码进行转换时速度极快。例如,在将 ES6 + 代码转换为 ES5 兼容代码的常见任务中,SWC 通常能够比 Babel 更快地完成转换操作,尤其是在处理大型项目的大量代码时,这种性能差异更加显著。
SWC
SWC 使用 .swcrc 文件进行配置,支持 JSON 格式:
javascript
{
"jsc": {
"parser": {},
"transform": {},
"target": "es5",
"loose": false,
"externalHelpers": false
},
"env": {
"targets": {},
"mode": "usage",
"coreJs": 3
},
"module": {
"type": "commonjs"
},
"minify": false
}
Vs Babel

Vite构建优化
Vite 在开发环境下凭借其基于 ESM 的 No-Bundle 机制已经非常快速,但在生产构建时,我们仍然需要关
核心构建配置优化
javascript
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
// 1. 指定输出目录,默认为 'dist'
outDir: 'dist',
// 2. 代码分割策略
rollupOptions: {
output: {
// 手动分块,避免过大的 chunk
manualChunks(id) {
if (id.includes('node_modules')) {
// 将 node_modules 中的依赖分块
if (id.includes('lodash')) {
return 'lodash';
}
if (id.includes('axios')) {
return 'axios';
}
// 将 React 相关库打包在一起
if (id.includes('react')) {
return 'react-vendor';
}
// 默认将所有其他 npm 包打包到 vendor 块中
return 'vendor';
}
},
// 用于命名代码拆分时创建的 chunk
chunkFileNames: 'js/[name]-[hash].js',
// 用于输出静态资源的命名
assetFileNames: '[ext]/[name]-[hash].[ext]'
}
},
// 3. 资源大小限制警告(单位:kb)
chunkSizeWarningLimit: 1000,
// 4. 生产环境 sourcemap(可选,根据需求选择)
// 'hidden-source-map' 只生成 sourcemap 但不包含引用,适合错误上报
sourcemap: process.env.NODE_ENV === 'production' ? 'hidden-source-map' : false,
// 5. 最小化配置
minify: 'esbuild', // 或 'terser',esbuild 更快
terserOptions: { // 当使用 terser 时的配置
compress: {
drop_console: true, // 移除 console
drop_debugger: true // 移除 debugger
}
}
}
})
路由级代码分割
使用动态 import() 实现路由懒加载,这是最有效的优化手段之一。
javascript
// router.js
const Home = () => import('./views/Home.vue')
const About = () => import('./views/About.vue')
const HeavyComponent = () => import('./components/HeavyComponent.vue')
对于非首屏需要的重型组件,使用懒加载。
javascript
<template>
<div>
<button @click="loadHeavyComponent">加载重型组件</button>
<Suspense>
<HeavyComponent v-if="showHeavy" />
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showHeavy = ref(false)
const HeavyComponent = ref(null)
const loadHeavyComponent = async () => {
HeavyComponent.value = await import('./components/HeavyComponent.vue')
showHeavy.value = true
}
</script>
