文件指纹是什么?怎么用?
问题解析
文件指纹是 Webpack 打包后输出文件名的后缀,用于解决浏览器缓存问题。通过给文件名添加唯一的哈希标识,可以在文件内容变化时强制浏览器重新加载资源。
核心概念
三种文件指纹类型
| 指纹类型 | 作用范围 | 特点 |
|---|---|---|
| Hash | 整个项目构建 | 任何文件修改都会导致所有 hash 变化 |
| Chunkhash | Webpack 打包的 chunk | 不同 entry 生成不同的 chunkhash |
| Contenthash | 文件内容 | 内容不变则 contenthash 不变,最精确 |
占位符说明
| 占位符 | 含义 |
|---|---|
[ext] |
文件后缀名 |
[name] |
文件名 |
[path] |
文件相对路径 |
[folder] |
文件所在文件夹 |
[contenthash] |
文件内容的 hash |
[hash] |
模块标识符的 hash |
[chunkhash] |
chunk 内容的 hash |
[emoji] |
随机 emoji 表情 |
详细解答
不同资源的指纹选择策略
// webpack.config.js
module.exports = {
output: {
// JS 使用 chunkhash:入口文件变化时,对应 chunk 的 hash 变化
filename: 'js/[name]_[chunkhash:8].js',
// 动态导入的 chunk 也使用 chunkhash
chunkFilename: 'js/[name]_[chunkhash:8].js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.(png|jpe?g|gif|svg)$/,
use: {
loader: 'file-loader',
options: {
// 图片使用 hash:通常图片内容独立,使用 hash 即可
name: 'img/[name]_[hash:8].[ext]'
}
}
}
]
},
plugins: [
// CSS 使用 contenthash:CSS 内容变化时才改变 hash
new MiniCssExtractPlugin({
filename: 'css/[name]_[contenthash:8].css'
})
]
};
三种 Hash 的详细对比
1. Hash(项目级)
// 不推荐:任何文件修改都会导致所有文件缓存失效
module.exports = {
output: {
filename: 'bundle_[hash:8].js' // 项目任意文件修改,hash 都会变
}
};
缺点:
- 无法利用浏览器缓存
- 每次构建所有文件名都变化
2. Chunkhash(Chunk 级)
// 推荐用于 JS 文件
module.exports = {
entry: {
main: './src/main.js',
vendor: './src/vendor.js'
},
output: {
filename: '[name]_[chunkhash:8].js'
}
};
优点:
- 不同入口文件独立计算 hash
- 修改 main.js 不会导致 vendor.js 缓存失效
3. Contenthash(内容级)
// 推荐用于 CSS 文件
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name]_[contenthash:8].css'
})
]
};
优点:
- 最精确的缓存控制
- 只有内容真正变化时才改变文件名
深入理解
为什么 CSS 要用 Contenthash 而不是 Chunkhash?
// 场景:使用 MiniCssExtractPlugin 提取 CSS
// 如果 CSS 使用 chunkhash,会出现问题:
// main.js 修改了,但 main.css 内容没变
// 使用 chunkhash:main.css 的 hash 也会变(因为属于同一个 chunk)
// 使用 contenthash:main.css 的 hash 不变(因为内容没变)
module.exports = {
plugins: [
new MiniCssExtractPlugin({
// 正确做法:CSS 使用 contenthash
filename: 'css/[name]_[contenthash:8].css'
})
]
};
Hash 长度控制
module.exports = {
output: {
// :8 表示取 hash 的前 8 位
// 长度越长,冲突概率越低,但文件名越长
filename: 'js/[name]_[chunkhash:8].js'
}
};
推荐长度:
- 开发环境:不需要 hash 或很短(如 4 位)
- 生产环境:8-12 位(平衡唯一性和文件名长度)
最佳实践
完整的文件指纹配置
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
app: './src/index.js',
vendor: './src/vendor.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
// JS 使用 chunkhash
filename: 'js/[name]_[chunkhash:8].js',
chunkFilename: 'js/[name]_[chunkhash:8].chunk.js',
// 资源文件路径
assetModuleFilename: 'assets/[name]_[hash:8][ext]'
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset/resource',
generator: {
// 图片使用 hash
filename: 'images/[name]_[hash:8][ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)$/,
type: 'asset/resource',
generator: {
// 字体使用 hash
filename: 'fonts/[name]_[hash:8][ext]'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
// CSS 使用 contenthash
filename: 'css/[name]_[contenthash:8].css',
chunkFilename: 'css/[name]_[contenthash:8].chunk.css'
})
]
};
缓存策略总结
资源类型 推荐指纹类型 原因
-------------------------------------------------
JS 入口文件 chunkhash 按入口独立缓存
JS 异步 chunk chunkhash 按 chunk 独立缓存
CSS 文件 contenthash 按内容精确缓存
图片/字体 hash 内容独立,hash 足够
面试要点
-
三种文件指纹的区别:Hash(项目级)、Chunkhash(Chunk 级)、Contenthash(内容级)
-
使用场景:
- JS 文件使用
chunkhash - CSS 文件使用
contenthash - 图片等资源使用
hash
- JS 文件使用
-
为什么 CSS 用 contenthash:避免 JS 修改导致 CSS 缓存失效,实现更精确的缓存控制
-
缓存优化原理:通过文件名变化强制浏览器重新加载,内容不变则利用缓存
-
注意事项:
- 提取 CSS 时务必使用 contenthash
- 合理控制 hash 长度(推荐 8 位)
- 配合 HTML 插件自动更新引用