4. 核心概念-Output:output.path与output.filename配置、占位符的使用
好,咱们接着聊 Webpack 的核心概念。前面讲完了入口(Entry),打包的原材料有了,那成品往哪放?怎么命名?这就是 Output 要解决的问题。
我个人觉得,Output 配置是新手最容易忽略、但坑最多的地方。你想想看,项目构建完,一堆文件散落在 dist 目录里,名字要么是 main.js,要么是 bundle.js,浏览器缓存策略根本没法做。嗯,今天咱们就把这块彻底捋清楚。
4.1 output.path 与 output.filename
先说两个最基础的属性:path 和 filename。
- output.path:指定打包后文件的输出目录。必须是绝对路径。
- output.filename:指定每个输出文件的名称。
看个最简单的例子:
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
这样配置后,所有打包产物都会扔到项目根目录下的 dist 文件夹里,主文件叫 bundle.js。
注意:path 必须用绝对路径。我见过不少新手直接用 './dist',结果打包完文件不知道跑哪去了。用 path.resolve 或 path.join 处理一下,准没错。
4.2 占位符的使用
如果所有输出文件都叫 bundle.js,那多入口打包怎么办?缓存更新怎么处理?
这时候就需要占位符了。Webpack 提供了一组内置的占位符,可以动态生成文件名。常用的有四个:
| 占位符 | 含义 | 典型场景 |
|---|---|---|
[name] |
模块名称,对应 entry 的 key | 多入口打包时区分文件 |
[hash] |
本次构建的全局唯一哈希 | 所有文件共享同一个 hash |
[chunkhash] |
每个 chunk 独立的哈希 | 按需加载、代码分割时使用 |
[contenthash] |
文件内容本身的哈希 | CSS 文件、图片等静态资源 |
4.3 [name] 占位符
[name] 是最直观的占位符。它会把 entry 配置中的 key 直接替换进来。
// 多入口配置
module.exports = {
entry: {
index: './src/index.js',
about: './src/about.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
打包后你会得到 index.js 和 about.js 两个文件。简单明了。
小技巧:我习惯在开发环境用 [name].js,这样调试时能一眼看出哪个文件对应哪个入口。生产环境再换成带 hash 的版本。
4.4 [hash]、[chunkhash]、[contenthash] 的区别
这三个 hash 相关的占位符,是面试常考题,也是实际项目中最容易踩坑的地方。我一个个说。
[hash]
[hash] 是构建级别的。一次构建生成一个唯一的 hash 值,所有输出文件都共享它。
output: {
filename: '[name].[hash].js'
}
比如你改了 index.js 里的一个变量,重新构建后,index.js 和 about.js 的 hash 都会变。为什么?因为整个构建过程的 hash 变了。
避坑指南:我曾经在一个老项目里看到有人用 [hash] 做缓存策略。结果每次改一行代码,所有文件 hash 都变了,用户得重新下载全部资源。这其实违背了缓存更新的初衷。后来我全改成了 [chunkhash] 或 [contenthash]。
[chunkhash]
[chunkhash] 是 chunk 级别的。每个 chunk 有自己的 hash,只有这个 chunk 的内容变了,它的 hash 才会变。
output: {
filename: '[name].[chunkhash].js'
}
还是上面那个例子,你只改了 index.js,那只有 index.[chunkhash].js 的 hash 会变,about.[chunkhash].js 保持不变。浏览器可以继续使用缓存中的 about.js,只重新下载 index.js。
为什么会这样?因为 Webpack 在构建时,会根据每个 chunk 的内容计算独立的 hash。内容不变,hash 就不变。
[contenthash]
[contenthash] 是更细粒度的。它基于文件内容本身计算 hash。哪怕你只是改了一个空格,hash 都会变。
// 配合 MiniCssExtractPlugin 使用
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
output: {
filename: '[name].[contenthash].js'
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
]
};
我个人建议,生产环境中 JS 文件用 [chunkhash],CSS 文件用 [contenthash]。因为 CSS 文件可能被多个 chunk 引用,用 [contenthash] 能更精确地控制缓存失效。
核心总结:
[hash]:一次构建一个值,所有文件共享。不推荐生产环境使用。[chunkhash]:每个 chunk 独立。适合 JS 文件。[contenthash]:每个文件独立。适合 CSS、图片等静态资源。
4.5 clean: true 清理旧文件
每次构建都会生成新文件,但旧文件不会自动删除。时间长了,dist 目录里会堆积大量无用的历史文件。
Webpack 5 提供了一个非常方便的配置:clean: true。
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash].js',
clean: true // 构建前清理 dist 目录
}
};
设置 clean: true 后,每次构建都会先清空 output.path 目录,再写入新文件。相当于每次都是「干净」的构建。
小提示:在 Webpack 4 时代,我们需要借助 clean-webpack-plugin 这个第三方插件来实现同样的功能。Webpack 5 直接内置了,省了一个依赖。如果你还在用 Webpack 4,可以考虑升级了。
嗯,到这里 Output 的核心配置就讲完了。总结一下:path 定目录,filename 定名字,占位符让名字动态化,clean: true 让目录保持整洁。下一节咱们聊聊 Loader,那才是 Webpack 真正强大的地方。