elpis里程碑二总结——webpack工程化模块
Webpack 多页面应用高级配置:从基础到开发/生产环境拆分
一、整体架构概览
三个配置文件通过 webpack-merge 有机组合,形成 “公共 + 环境专属” 的经典模式:
| 文件 | 职责 |
|---|---|
webpack.base.js | 定义入口生成逻辑、通用 loader、resolve 别名、基础插件及代码分割策略 |
webpack.dev.js | 注入 HMR 客户端、设置开发模式、Source Map、开发环境 output |
webpack.prod.js | 启用生产优化(多线程、压缩、CSS 提取、清理目录等) |
这种分层既避免了重复配置,又保证了各环境的独立调优。
二、webpack.base.js:构建的基石
2.1 动态多入口与 HtmlWebpackPlugin
- 使用
glob扫描app/pages/**/entry.*.js,自动提取入口名称(如entry.page1)。 - 同步生成
HtmlWebpackPlugin列表,每个插件将模板entry.tpl渲染为对应的.tpl文件(供后端模板使用),并通过chunks只注入当前入口的 JS。
代码要点:
javascript
const entryList = path.resolve(process.cwd(), "./app/pages/**/entry.*.js");
glob.sync(entryList).forEach((file) => {
const entryName = path.basename(file, ".js");
pageEntries[entryName] = file;
HtmlWebpackPluginList.push(
new HtmlWebpackPlugin({
template: path.resolve(process.cwd(), 'app/view/entry.tpl'),
filename: path.resolve(process.cwd(), 'app/public/dist', `${entryName}.tpl`),
chunks: [entryName],
})
);
});
2.2 Loader 配置一览
| 文件类型 | Loader 链 | 关键参数 |
|---|---|---|
.vue | vue-loader | 需配合 VueLoaderPlugin |
.js | babel-loader | include: path.resolve('app/pages') |
.scss | style-loader + css-loader + sass-loader | 开发环境内嵌 style |
.less | style-loader + css-loader + less-loader | - |
.css | style-loader + css-loader | - |
| 图片(png等) | url-loader | limit: 300,小于 300B 转 base64 |
| 字体(eot等) | file-loader | - |
2.3 Resolve 别名与 extensions
javascript
resolve: {
extensions: ['.js', '.vue', 'less', 'css'],//尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀。
alias: {
'$pages': path.resolve(process.cwd(), 'app/pages'),
'$common': path.resolve(process.cwd(), 'app/pages/common'),
'$widgets': path.resolve(process.cwd(), 'app/pages/widgets'),
'$store': path.resolve(process.cwd(), 'app/pages/store'),
}
}
2.4 通用插件
VueLoaderPlugin:处理.vue文件的必须插件。webpack.ProvidePlugin:自动加载Vue、axios、_到全局。webpack.DefinePlugin:定义 Vue 运行时常量(如__VUE_OPTIONS_API__)。- 动态生成的
HtmlWebpackPluginList(需展开至 plugins 数组)。
2.5 代码分割策略(optimization.splitChunks)
javascript
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: { test: /[\/]node_modules[\/]/, priority: 20, name: 'vendors' },
common: { minChunks: 2, priority: 10, name: 'common', minSize: 1 }
}
},
runtimeChunk: true
- vendors:第三方依赖(变化极少) → 强缓存。
- common:被 2 个以上入口引用的业务公共模块 → 中等缓存。
- runtimeChunk:单独抽离 Webpack 运行时 → 避免因业务变更导致 vendor hash 变化。
⚠️ 注意:
minSize: 1会导致极小的模块也被分割,生产环境建议调至20KB左右(可通过环境变量区分)。
三、webpack.dev.js:开发体验至上
3.1 动态注入 HMR 客户端
javascript
Object.keys(baseConfig.entry).forEach(v => {
if (v !== 'vendor') {
baseConfig.entry[v] = [
baseConfig.entry[v],
`webpack-hot-middleware/client?path=http://${HOST}:${PORT}/${HMR_PATH}&timeout=${TIMEOUT}&reload=true`
];
}
});
- 仅对业务入口注入(排除
vendor),避免第三方库被热替换影响。 - 通过
webpack-hot-middleware实现与开发服务器的 WebSocket 通信。
3.2 开发专属配置项
| 配置项 | 值 | 作用 |
|---|---|---|
mode | 'development' | 启用开发优化(如 process.env.NODE_ENV 注入) |
devtool | 'eval-cheap-module-source-map' | 快速重建,源码定位到行(不包含列),仅用于开发 |
output.filename | js/[name]_[chunkhash:8].bundle.js | 使用 chunkhash 便于缓存(开发环境虽不强制,但保持一致性) |
output.path | app/public/dist/dev/ | 输出到 dev 子目录,与生产环境隔离 |
output.publicPath | http://127.0.0.1:9002/public/dist/dev/ | 绝对路径,确保通过 dev server 访问静态资源 |
plugins | new webpack.HotModuleReplacementPlugin() | 启用模块热替换(HMR) |
3.3 配合 Dev Server 的注意事项
- 实际开发中,需要启动一个 Express/Koa 服务器,挂载
webpack-dev-middleware和webpack-hot-middleware。 publicPath必须与中间件提供的资源路径严格一致,否则会出现 404。
四、webpack.prod.js:性能与缓存为王
4.1 输出配置(生产专用)
javascript
output: {
path: path.join(process.cwd(), "./app/public/dist/prod"),
filename: "js/[name]_[chunkhash:8].bundle.js",
publicPath: "/dist/prod",
crossOriginLoading: "anonymous",
}
- 输出至
prod子目录,避免与开发文件混淆。 crossOriginLoading: "anonymous"为动态加载的 chunk 添加crossorigin属性,便于 CDN 错误监控。
4.2 多线程编译:HappyPack
javascript
new HappyPack({
id: 'js',
loaders: [`babel-loader?${JSON.stringify({ presets: ["@babel/preset-env"], plugins: ['@babel/plugin-transform-runtime'] })}`]
}),
new HappyPack({
id: 'css',
loaders: [{ path: 'css-loader', options: { importLoaders: 1 } }]
})
- 利用
os.cpus().length开启多线程,加速 JS/CSS 的转译过程。 - 在
module.rules中通过'happypack/loader?id=js'引用对应的 HappyPack 实例。
⚠️ 演进建议:新时代项目可改用
thread-loader,HappyPack 已停止维护,但逻辑依然值得学习。
4.3 CSS 处理链路
MiniCssExtractPlugin.loader替代style-loader,将 CSS 抽离为独立文件。CssMinimizerPlugin在 optimization 阶段压缩 CSS,与 JS 压缩并行。- 最终产物:
.css文件带有[contenthash:8]指纹,便于长期缓存。
4.4 清理与压缩
-
CleanWebpackPlugin:每次打包前删除app/public/dist整个目录,避免旧文件残留。 -
TerserWebpackPlugin:parallel: true启用多进程压缩。cache: true缓存压缩中间结果。drop_console: true移除所有console.*语句,保证生产环境日志清洁。
4.5 安全与跨域
HtmlWebpackInjectAttributesPlugin 为所有注入的 <script> 和 <link> 标签统一添加 crossorigin="anonymous",提升 CDN 资源错误捕获能力。
4.6 关闭性能警告
javascript
performance: { hints: false }
生产包体积可能较大(尤其包含大图片时),避免无意义的警告干扰 CI 日志。
五、开发 vs 生产:关键差异速查表
| 对比维度 | 开发环境 (dev) | 生产环境 (prod) |
|---|---|---|
| mode | development | production |
| source map | eval-cheap-module-source-map | 通常关闭(或 hidden-source-map) |
| 代码压缩 | 不压缩 | TerserWebpackPlugin + CssMinimizerPlugin |
| console 语句 | 保留 | drop_console: true 删除 |
| CSS 处理 | style-loader 内联 | MiniCssExtractPlugin 提取为独立文件 |
| 多线程编译 | 未使用(节省启动时间) | HappyPack 加速(或 thread-loader) |
| HMR | 注入 webpack-hot-middleware/client | 无 |
| 输出路径 | app/public/dist/dev | app/public/dist/prod |
| publicPath | 绝对路径(含协议+端口) | 相对路径 /dist/prod |
| 清理输出目录 | 否(通常手动或依赖 dev server) | CleanWebpackPlugin 自动清理 |
| 性能提示 | 默认 | hints: false |
六、最佳实践与常见问题
6.1 构建脚本示例(package.json)
json
"scripts": {
"build:dev":"node --max_old_space_size=4096 ./app/webpack/dev.js",
"build:prod":"node ./app/webpack/prod.js"
}
| 命令 | 内存参数 | 原因 |
|---|---|---|
build:dev | --max_old_space_size=4096 | 开发构建需要生成 Source Map、保留未压缩代码、支持 HMR,内存开销极大 |
build:prod | 无(默认约 1.5GB) | 生产构建通过代码压缩、多进程拆分、缓存等策略,内存需求相对较低 |
6.2 路径与端口配置化
建议将 HOST、PORT 等环境相关参数提取到 .env 文件或 config/index.js,避免硬编码。
6.3 HappyPack 的现代替代
javascript
// 使用 thread-loader 替代 HappyPack
module: {
rules: [{
test: /.js$/,
use: ['thread-loader', 'babel-loader'],
include: [path.resolve('app/pages')]
}]
}
6.4 开发环境为何不开启多线程?
因为开发阶段强调快速启动和增量重建,多线程的线程池创建和通信反而会降低速度。仅在生产构建时启用。
6.5 缓存策略进阶
- 配合
WebpackManifestPlugin生成资源映射表,供后端模板动态注入带有 hash 的资源 URL。 - 对 vendors 和 common chunk 设置
optimization.moduleIds: 'deterministic',避免添加新依赖时影响其他 chunk 的 hash。
七、总结
通过 webpack.base.js、webpack.dev.js、webpack.prod.js 三个文件的精细化拆分,我们实现了一套可维护、高性能、环境友好的 Webpack 多页面构建方案。
- 基础层:统一入口扫描、Loader 链、别名、公共插件、代码分割策略。
- 开发层:热更新、可读的 Source Map、绝对路径 publicPath,提升编码效率。
- 生产层:多线程编译、CSS/JS 极致压缩、内容哈希缓存、安全策略,保障线上性能。