写给:准备把简历里的“会搭项目脚手架”变成真本事的你。
目标:把你从“能跑起来”带到“说得清、配得对、踩坑少、面试能聊赢”。
0. 先说“工程化”到底在管啥?
一句话:把“写页面”升级成“造产品” 。工程化提供一整套“后方保障”,让多人协作、可维护、可扩展、可上线。
它通常解决这些问题:
- 开发服务器:本地起个 Web Server(Vite 默认端口
5173),支持路由、代理、CORS、HMR。 - 语言与语法转译:
TS/TSX → JSX/JS、JSX → JS、Stylus/Sass/Less → CSS、PostCSS 自动补前缀。 - 模块化与依赖管理:
import/export、别名、按需加载、代码分割。 - 打包与优化:压缩、Tree-shaking、缓存、分包、图片/字体/静态资源处理。
- 质量保障:ESLint/Prettier、TypeScript 类型检查、单测/端测。
- 环境与部署:多环境注入(
dev/test/prod)、CI/CD、一键发布。 - 本地 API:有时配个 Node/Express 小服务做 mock 或代理。
极简对比(“最原始” vs “工程化”):
原始:index.html + <script> + 一堆全局变量 → 能跑,但维护成本爆炸
工程化:模块化 + 打包 + 开发服 + 质量工具 + 部署流程 → 团队能打仗
1. ES Modules 速通(为啥 Vite 这么快)
-
ESM(ES6 模块化) :
import / export,编译时静态分析,工具能提前看出依赖,才能 Tree-shaking。 -
在浏览器里直接用:
<script type="module" src="/src/main.jsx"></script> -
旧浏览器(如 IE 11)不原生支持 ESM,默认不兼容。
👉 现代项目一般放弃兼容;如确需兼容,可用降级方案(但成本高,建议评估业务必要性)。
2. Vite:为“开发体验”疯狂打 CALL
一句话:开发阶段不打包,按需编译 + 原生 ESM,开箱即用、HMR 飞起;生产阶段交给 Rollup 做极致优化。
2.1 为什么“快”
- No-bundle 开发:不预先打大包;浏览器请求哪个模块,Vite 就转换哪个。
- 依赖预构建:用
esbuild(Go 写的,很快)把第三方包预处理成 ESM,避免多次解析。 - HMR 毫秒级:改一个文件,只推一个模块,不牵连全项目。
2.2 生产构建
- 调用 Rollup:代码分割、Tree-shaking、Scope Hoisting、压缩。
- 既快又稳,产物干净。
2.3 模块链理解(你最好能“画”出来)
main.jsx
└─> App.jsx
├─> App.css
├─> components/*
├─> api/*
└─> store/*
Vite 启动时不全编译这棵树;你打开页面请求到谁,才临时转换谁。
2.4 关键文件示例
index.html
<!doctype html>
<html>
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"></head>
<body>
<div id="root"></div>
<!-- 关键:ESM 入口 -->
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
vite.config.ts(React 项目示例)
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' // 或 react-swc
import path from 'node:path'
export default defineConfig({
plugins: [react()],
server: { port: 5173, open: true, proxy: { '/api': 'http://localhost:3000' } },
resolve: { alias: { '@': path.resolve(__dirname, 'src') } },
css: { modules: { localsConvention: 'camelCase' } },
build: { sourcemap: false, outDir: 'dist', chunkSizeWarningLimit: 1024 }
})
兼容性补充:默认不支持 IE 11 这类老浏览器;确实需要可评估 legacy 插件方案,但不建议为“历史负担”牺牲主体验。
3. Webpack:生态深、可定制、业务验证久
一句话:打包器里的“老法师”。功能全、插件多、可定制强,大型项目深度定制仍常用。
3.1 核心概念
- Entry / Output:从入口出发,打包到输出。
- Loader:把“非 JS”变“JS 模块”(如
babel-loader、css-loader)。 - Plugin:增强能力(如
HtmlWebpackPlugin、MiniCssExtractPlugin)。 - devServer + HMR:开发体验不差,但大项目启动与热更速度往往比 Vite 慢。
3.2 最小可用配置(React + JSX)
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.jsx',
output: {
filename: 'js/[name].[contenthash:8].js',
path: path.resolve(__dirname, 'dist'),
clean: true
},
module: {
rules: [
{ test: /.(js|jsx)$/, exclude: /node_modules/,
use: { loader: 'babel-loader',
options: { presets: ['@babel/preset-env','@babel/preset-react'] } } },
{ test: /.css$/, use: ['style-loader','css-loader'] },
{ test: /.(png|jpe?g|gif|svg)$/i, type: 'asset', parser: { dataUrlCondition: { maxSize: 8 * 1024 } } }
]
},
resolve: { extensions: ['.js','.jsx'] },
plugins: [ new HtmlWebpackPlugin({ template: './public/index.html' }) ],
devtool: 'cheap-module-source-map',
devServer: { port: 8080, hot: true, open: true, historyApiFallback: true }
}
Babel 可选独立配置(.babelrc)
{ "presets": ["@babel/preset-env", "@babel/preset-react"] }
4. Webpack vs Vite:怎么选?
| 维度 | Vite | Webpack |
|---|---|---|
| 开发启动/HMR | 近乎秒开,毫秒级 HMR(ESM 按需编译) | 大项目相对慢,HMR 会重构依赖 |
| 生产打包 | Rollup 产物干净、分包友好 | 优化策略成熟,生态广 |
| 兼容性 | 默认走现代浏览器 | 通过 Babel/Polyfill/Loader 适配旧环境更灵活 |
| 生态与定制 | 近年很旺、简单高效 | 老牌、插件海量、任你魔改 |
| 学习曲线 | 低,开箱即用 | 相对高,配置项多 |
| 适用场景 | 新项目、追求开发体验、小中型团队 | 复杂历史项目、深度定制、企业长线治理 |
经验结论:
- 新项目、React/Vue SPA:先选 Vite,除非你必须兼容很老的浏览器或需要极深的构建定制。
- 老项目或重度定制平台:Webpack 仍然能打。
5. Tree-shaking / CommonJS / JSX:面试与实战要点
5.1 Tree-shaking 关键点
-
依赖 ESM 的编译时静态分析;CommonJS 的
require难以摇树。 -
最好使用具名导出,少用大对象默认导出。
-
声明副作用:
{ "sideEffects": false } -
第三方库选 ESM 版本(如
lodash-es)。
5.2 CommonJS vs ESM 超简表
| 点 | ESM | CommonJS |
|---|---|---|
| 加载 | 编译时静态 | 运行时动态 |
| 导入 | 引用绑定(会更新) | 值拷贝(有缓存) |
| 摇树 | ✅ | ❌ |
| 代表 | 浏览器、现代打包 | Node 传统生态 |
5.3 JSX 在两家里的“打开方式”
- Vite (React) :
@vitejs/plugin-react(或react-swc),即插即用,内置 Fast Refresh。 - Webpack:
babel-loader + @babel/preset-react,test: /.(js|jsx)$/,resolve.extensions补.jsx。
6. 开发服务器与“后方保障”
6.1 Vite Dev Server(默认 5173)
- 静态文件 + 模块转换 + HMR。
server.proxy可转发/api到http://localhost:3000,免 CORS 烦恼。
6.2 Express 迷你后端(有时你会用到)
// server.js
const express = require('express')
const app = express()
app.use(express.json())
app.get('/api/health', (_, res) => res.json({ ok: true }))
app.listen(3000, () => console.log('API on 3000'))
配合前端代理即可“前后端联调不跨域”。
7. 实操:两分钟脚手架
7.1 Vite(React)
npm create vite@latest my-app -- --template react
cd my-app
npm i
npm run dev # http://localhost:5173
npm run build # 生产包
npm run preview # 本地预览生产包
7.2 Webpack(从零手搓最小版)
mkdir wp-app && cd wp-app
npm init -y
npm i -D webpack webpack-cli webpack-dev-server \
html-webpack-plugin babel-loader @babel/core @babel/preset-env @babel/preset-react \
style-loader css-loader
# 按上面的 webpack.config.js / .babelrc 填好
npx webpack serve
8. 常见坑位清单(真·保命)
- Vite HMR 不生效:多数是缓存或依赖预构建问题,删
node_modules/.vite或调整optimizeDeps。 - 跨域:别硬怼浏览器,用代理(Vite
server.proxy/ Webpack devServerproxy)。 - Tree-shaking 没起作用:你用了
require或默认导出,或包是 CJS,或sideEffects没配。 - JSX 报错:忘了装 React 相关 preset/插件,或没在
resolve.extensions加.jsx。 - 旧浏览器报错:默认产物太“现代”,需要 Babel 降级与 Polyfill(谨慎评估必要性)。
9. 面试 30 秒高能速答卡
工程化:把开发、打包、优化、质量、部署一套打通。
Vite:开发不打包,ESM 按需编译 + esbuild 预构建,HMR 毫秒级;生产用 Rollup。
Webpack:打包器老大哥,生态强、可定制深;大项目和历史包袱里很能打。
ESM/Tree-shaking:ESM 静态分析支撑摇树;CJS 动态加载不利优化。
JSX 配置:Vite 用@vitejs/plugin-react;Webpack 用babel-loader + @babel/preset-react。
10. 结语
- 想要快、想要省心、新项目优先上 Vite。
- 需要深度定制、复杂场景治理、历史项目承接,Webpack 还是很稳。
- 别忘了:工程化不是工具崇拜,而是“以终为始”的团队协作与交付能力。