hash、chunkhash 和 contenthash

583 阅读3分钟

前言

本篇文章主要介绍以下内容: 

  • 为什么需要 hash ? 
  • hash 的类型 
  • 每种 hash 类型的用法

为什么需要 hash ?

在 Web 应用程序上,我们会使用浏览器缓存来加快其页面响应速度,增强用户体验。对于其中不常变化的资源,通常会设置一个很长的过期时间,如:

Cache-Control: max-age=31536000

这样浏览器之后请求相同的资源就会命中缓存。但是,如果我们不更新文件路径,那么浏览器仍旧会为用户提供缓存的旧资源。换句话说,用户将看不到更新的功能。更甚至,如果后端废弃了某个接口,前端会直接报错。

因此为了解决这个问题,我们通常会为每个文件添加后缀,例如版本。如下:

app.js?version=1
vendor.css?version=1
main.css?version=1

如今,我们一般会使用 webpack 来打包我们的项目。对于生成后缀,webpack 更加的容易。通过下面基本的 webpack 配置就可以实现上述效果:

const path = require('path');

module.exports = {
  entry: {
    vendor: './src/vendor.js',
    main: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[hash].js'
  }
};

终端运行 webpack 命令会出现以下结果:

通过上图可以发现每个文件都有了hash后缀。

hash 的类型

webpack 提供了如下三种 hash 类型: 

  1. [hash] 
  2. [chunkhash]
  3. [contenthash] 

下面会分别介绍这些不同类型的 hash 是什么?以及在什么情况下使用它们。

hash

hash 对应于构建。每个块的 hash 值都相同。如果一个文件改变,其余所有文件的 hash值都会改变。

我们修改 index.js 后重新构建,如下所示:

hash 不能作为文件的后缀,因为它不可能命中缓存。每次项目更新后构建,hash 都会改变,浏览器会重新请求资源。

chunkhash

chunkhash 基于 webpack 入口的。根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的值,即定义的每个入口都有自己的 hash 值。

如果入口发生任何变化,则仅相应的 hash 值会发生变化。所以可以使用 chunkhash 作为文件后缀。 

要使用 chunkhash 而不是 hash, 只需将“[name].[hash].js”改为“[name].[chunkhash].js”。修改后的webpack.config.js:

const path = require('path');

module.exports = {
  entry: {
    vendor: './src/vendor.js',
    main: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  }
};

重新构建后的结果:

我们继续修改 index.js,保持 vendor.js 不变。构建后,如下所示:

对比两张图片,验证了我们上面的结论。只有 inde.js 的 hash 值发生了改变,vendor.js 的 hash 值保持不变。

contenthash

contenthash是在 ExtractTextWebpackPlugin 中创建的 hash 的特定类型,它是根据提取的内容而不是完整的块内容来计算的。 

Note: ExtractTextWebpackPlugin 从 bundle 中提取文本(CSS)到单独的文件,关于 ExtractTextWebpackPlugin 它的更多介绍点击这里

首先我们安装插件:

cnpm i css-loader extract-text-webpack-plugin --save-dev

然后新建 index.css, 并在 index.js 中引用。 

修改 webpack.config.js 如下:

const path = require('path');

module.exports = {
  entry: {
    vendor: './src/vendor.js',
    main: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
    	filename: '[name].[chunkhash].css'
    }),
  ]
};

对于 css, 如果在 ExtractTextPlugin 中使用 [name].[chunkhash].css,那么 css 和 js 将得到相同的 hash值。这时如果我们改变 js,那么 css 的 hash 值也会相应改变。

如果要想在只有 css 改变时,css 的 hash 值才会改变,此时就需要使用 

[name].[contenthash].css。构建结果大家可以动手实践验证一下。 

一般只在生产环境中使用hash / chunkhash / contenthash,在开发环境中不必配置,因为它会增加编译时间。

参考链接

  1. www.webpackjs.com/configurati…

  2. webpack.js.org/guides/cach…

  3. medium.com/@sahilkkraz…

如有错误,请指正!!!