基于本文回答

播面 播面

文解图解播客视频,多维讲透八股文
0
评论

什么是 Webpack 的 Hash、Chunkhash 和 Contenthash?

在 Webpack 中,hashchunkhashcontenthash 是用来给输出的文件(bundle)添加动态哈希值的变量。

它们的主要作用是实现强缓存(Long-term Caching)缓存刷(Cache Busting):当文件内容未改变时,文件名保持不变,浏览器继续使用本地缓存;当文件内容改变时,文件名随之改变,浏览器强制下载最新的文件。

这三者的区别在于计算哈希值时所基于的“作用域/粒度”不同


1. hash(在 Webpack 5 中推荐写作 fullhash

  • 作用域:整个项目(每次编译 Compilation)。
  • 计算机制:基于所有构建文件的内容计算出来的。只要项目中有任何一个文件发生了修改,重新构建后,所有输出文件的 hash 值都会统一改变
  • 缺点:缓存命中率极低。即使你只修改了一行 CSS,打包出来的 JS 文件的文件名也会发生变化,导致浏览器无法利用之前的 JS 缓存。
  • 适用场景:通常只在开发环境(Development)中使用,或者用于不需要精细化缓存控制的场景。

注意:Webpack 5 已将 [hash] 重命名为 [fullhash](但 [hash] 依然兼容)。


2. chunkhash

  • 作用域:根据 Webpack 的 Chunk(代码块) 来计算。
  • 计算机制:每个 Entry(入口文件)在 Webpack 中都会被构建为一个或多个 Chunk。chunkhash 是基于该 Chunk 本身及其依赖的文件计算的。只要该 Chunk 内部的代码不改变,其 chunkhash 就不变,其他独立的 Chunk 改变不会影响它。
  • 优势:实现了入口文件级别的缓存隔离(例如:修改了 app.js,不会影响独立打包的 vendor.js(第三方库)的哈希值)。
  • 缺陷:如果在一个 Chunk 中同时引入了 JS 和 CSS(通过 MiniCssExtractPlugin 抽离 CSS),当你只修改了 JS 时,因为它们属于同一个 Chunk,抽离出来的 CSS 文件的 chunkhash 也会跟着改变,导致 CSS 缓存失效。

3. contenthash

  • 作用域:基于单个文件本身的内容
  • 计算机制:只要文件自身的内容不发生改变,其 contenthash 就绝对不会变。
  • 优势:最高粒度的缓存控制。完美的解决了 chunkhash 的问题。例如:在同一个 Chunk 中,如果你只修改了 JS 代码,没有修改 CSS,那么打包后 JS 的 contenthash 会变,而抽离出的 CSS 的 contenthash 保持不变,CSS 文件依然可以完美命中浏览器缓存。
  • 适用场景生产环境(Production)的最佳实践。

总结对比表

哈希类型 影响范围 (Scope) 触发变更的条件 推荐使用场景
hash / fullhash 整个项目 只要项目中任何文件改变 开发环境
chunkhash 同一个 Chunk (入口及依赖) 同一 Chunk 内的代码发生改变 生产环境的多入口/代码分割 JS
contenthash 仅当前文件本身 仅当前文件的内容改变 生产环境的 CSS 及所有静态资源

最佳实践配置示例 (Webpack 5 生产环境)

在实际的生产环境(Production)配置中,统一推荐优先使用 contenthash

javascript
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    // JS 输出:使用 contenthash
    filename: 'js/[name].[contenthash:8].js',
    chunkFilename: 'js/[name].[contenthash:8].chunk.js',
    clean: true, // 清理旧文件
  },
  plugins: [
    // 抽离出的 CSS 文件:使用 contenthash
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        type: 'asset/resource',
        generator: {
          // 图片等静态资源:使用 contenthash
          filename: 'images/[name].[contenthash:8][ext]',
        },
      },
    ],
  },
};

(注::8 表示只截取哈希值的前 8 位,可以让文件名更短。)