构建打包规范
产物目录、资源命名、压缩策略
- Standard:新项目优先使用 Vite,存量 Webpack 项目可逐步迁移。
- Standard:构建工具版本在
package.json中锁定,团队统一。
| 工具 | 适用场景 | 说明 |
|---|---|---|
| Vite | 新项目、中小型项目 | 构建快、配置简单、基于 Rollup |
| Webpack | 大型存量项目 | 生态成熟、配置灵活 |
| Rollup | 库/组件库打包 | Tree-shaking 优秀、产物干净 |
| esbuild | 极致性能场景 | Go 编写、速度极快 |
| Turbopack | 大型 monorepo | Webpack 替代品(实验性) |
| 语言 | 构建工具 |
|---|---|
| Java | Maven / Gradle |
| Python | Poetry / setuptools / pyinstaller |
| Go | 原生 go build |
| Node.js | tsc / esbuild / pkg |
- Requirement:
package.json中必须定义标准化的构建脚本:
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"build:dev": "vite build --mode development",
"build:staging": "vite build --mode staging",
"build:prod": "vite build --mode production",
"preview": "vite preview",
"lint": "eslint . --ext .ts,.tsx",
"type-check": "tsc --noEmit",
"test": "vitest"
}
}- Requirement:构建产物统一输出到
dist/目录,禁止输出到其他目录。
dist/├── index.html # 入口 HTML├── assets/ # 静态资源│ ├── js/ # JavaScript 文件│ ├── css/ # CSS 文件│ ├── images/ # 图片│ ├── fonts/ # 字体│ └── icons/ # 图标├── favicon.ico├── robots.txt└── manifest.json**Java(Maven):
target/├── user-service-1.0.0.jar # 可执行 Jar├── user-service-1.0.0.jar.original # 原始 Jar(Spring Boot 瘦身)├── classes/ # 编译后的 class 文件├── lib/ # 依赖 Jar└── maven-status/**Go:
build/├── user-service # 二进制文件├── user-service-darwin-arm64├── user-service-linux-amd64└── user-service-windows-amd64.exe**Python:
dist/├── user_service-1.0.0-py3-none-any.whl # Wheel 包└── user_service-1.0.0.tar.gz # 源码包- Requirement:构建前必须清理旧产物,禁止增量构建导致的残留文件。
- Standard:
dist/、target/、build/目录必须加入.gitignore。
# .gitignore
dist/
target/
build/
*.log- Requirement:JS/CSS 文件名必须包含内容 hash,格式为 `
. . 。
dist/assets/js/index.3a2b1c4d.jsdist/assets/css/main.9e8f7g6h.css- Standard:hash 长度 8 位,使用内容哈希(contenthash)。
- Standard:图片、字体同样带 hash,确保缓存可控。
dist/assets/images/logo.1a2b3c4d.pngdist/assets/fonts/inter.5e6f7g8h.woff2- Standard:入口 HTML 文件名保持
index.html,不添加 hash。
// vite.config.ts
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false, // 生产环境默认关闭,调试时按需开启
rollupOptions: {
output: {
// JS 文件命名
entryFileNames: 'assets/js/[name].[hash].js',
chunkFileNames: 'assets/js/[name].[hash].js',
// CSS 文件命名
assetFileNames: (assetInfo) => {
const name = assetInfo.name || '';
if (name.endsWith('.css')) {
return 'assets/css/[name].[hash][extname]';
}
if (/\.(png|jpe?g|svg|gif|webp)$/.test(name)) {
return 'assets/images/[name].[hash][extname]';
}
if (/\.(woff2?|ttf|otf)$/.test(name)) {
return 'assets/fonts/[name].[hash][extname]';
}
return 'assets/[name].[hash][extname]';
},
},
},
},
});- Requirement:生产构建必须开启代码压缩(minify)。
- Standard:使用
terser(Webpack)或esbuild(Vite)压缩。 - Standard:压缩时移除
console.log、debugger、注释。
// vite.config.ts
export default defineConfig({
build: {
minify: 'esbuild', // 或 'terser'
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
},
format: {
comments: false, // 移除注释
},
},
},
});- Requirement:生产构建必须压缩 CSS,移除空白与注释。
- Standard:使用
cssnano或esbuild内置 CSS 压缩。
- Requirement:必须开启代码分割,将第三方依赖与业务代码分离。
- Standard:按路由懒加载页面组件,减小首屏包体积。
- Standard:第三方依赖单独打包,便于缓存。
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'ui-vendor': ['antd', '@ant-design/icons'],
'utils-vendor': ['lodash-es', 'dayjs', 'axios'],
},
},
},
},
});- Requirement:必须开启 Tree Shaking,移除未使用的代码。
- Standard:确保所有依赖支持 ES Module 格式,避免全量引入。
// 正确:按需引入
import { debounce } from 'lodash-es';
// 错误:全量引入
import _ from 'lodash';- Standard:生产环境默认关闭 Source Map,避免源码泄露。
- Caution:如生产环境需要错误定位,可生成 Source Map 但上传到错误监控平台(Sentry),不随产物发布。
// vite.config.ts
export default defineConfig({
build: {
sourcemap: process.env.NODE_ENV !== 'production', // 生产关闭
},
});- Standard:构建时生成
.gz/.br预压缩文件,由 CDN 或 Nginx 直接返回。 - Standard:文本类资源(JS、CSS、HTML、SVG、字体)开启压缩。
- Caution:图片(PNG、JPG、WebP)已是压缩格式,无需再 gzip。
// vite.config.ts
import viteCompression from 'vite-plugin-compression';
export default defineConfig({
plugins: [
viteCompression({
algorithm: 'gzip',
threshold: 10240, // 大于 10KB 才压缩
ext: '.gz',
}),
viteCompression({
algorithm: 'brotliCompress',
threshold: 10240,
ext: '.br',
}),
],
});- Standard:使用
rollup-plugin-visualizer或webpack-bundle-analyzer分析包体积。 - Standard:单 chunk 体积建议 ≤ 200KB(gzip 后),超过需拆分。
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [visualizer({ open: true })],
});- Requirement:不同环境使用不同的构建配置,通过
--mode参数区分。 - Requirement:生产构建必须设置
NODE_ENV=production,确保依赖库进入生产模式(关闭开发警告)。
- Standard:构建时注入环境变量,区分开发/预发布/生产。
// vite.config.ts
export default defineConfig(({ mode }) => ({
define: {
'process.env.NODE_ENV': JSON.stringify(mode),
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
},
}));# 开发环境构建
npm run build:dev
# 预发布环境构建
npm run build:staging
# 生产环境构建
npm run build:prod-
Requirement:构建完成后必须校验产物完整性:
- 产物目录存在且非空。
- 入口
index.html存在。 - 所有引用的资源文件存在。
- 无
console.log/debugger残留(生产环境)。 - 包体积在阈值范围内。
-
Standard:CI 流水线中集成构建产物校验脚本。
#!/bin/bash
# scripts/verify-build.sh
set -e
DIST_DIR="dist"
# 检查产物目录
if [ ! -d "$DIST_DIR" ]; then
echo "❌ Build output directory not found: $DIST_DIR"
exit 1
fi
# 检查入口 HTML
if [ ! -f "$DIST_DIR/index.html" ]; then
echo "❌ index.html not found"
exit 1
fi
# 检查无 console.log 残留(生产环境)
if [ "$NODE_ENV" = "production" ]; then
if grep -r "console.log" "$DIST_DIR/assets" --include="*.js" | head -1; then
echo "❌ console.log found in production build"
exit 1
fi
fi
echo "✅ Build verification passed"**Java Spring Boot 构建:
mvn clean package -DskipTests -Pprod**Go 交叉编译:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/user-service ./cmd/server**Python 构建:
python -m build| 反模式 | 问题 | 建议 |
|---|---|---|
| 产物不带 hash 文件名 | 缓存无法自动失效 | 构建时自动加 contenthash |
| 生产环境保留 console.log | 信息泄露、性能损耗 | 构建时移除 |
| 生产环境开启 Source Map | 源码泄露 | 默认关闭,按需上传到监控平台 |
| 不开启代码分割 | 首屏包体积过大 | 按路由 + 第三方依赖拆分 |
| 全量引入依赖 | 包体积膨胀 | 按需引入 + Tree Shaking |
| 构建产物未清理旧文件 | 残留文件导致部署异常 | 构建前清理 dist |
| 不同环境共用同一构建产物 | 配置错乱 | 分环境构建 |
| 图片未压缩直接打包 | 包体积过大 | 构建时自动压缩 |
| 无包体积监控 | 体积膨胀无法感知 | 集成 bundle analyzer |