Toc
  1. 文件指纹
  2. compilation 和 compiler
  3. hash/chunkhash/contenthash
    1. hash
    2. chunkhash
    3. contenthash
Toc
0 results found
FBB
我不知道的文件指纹
2020/04/03 前端 Webpack

淡黄的长裙,蓬松的头发
牵着我的手去看那新写出博客

在目前常用的 webpack 中,有这样的一段代码

module.exports = {
  entry: {
    index: "./src/index.js",
    demo: "./src/demo.js"
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[hash].js"
  }
};

作为小白,着实不是很懂其中[hash]这个变量的作用是啥,会对我们在项目打包的过程中,提供什么样子的帮助。
在经过一系列的科学上网查询资料之后,对这个东西有了新的认识。

文件指纹

文件中的 hash,其实是文件指纹的一种,是前端资源实现增量更新最常用的方案。
那什么又是文件指纹呢?

  • 打包之后输出文件名的后缀
  • 通常用于版本管理

compilation 和 compiler

在我们正式了解几种常见的文件指纹之前,我们需要对 webpack 打包会生成的 compilation 和 compiler 对象有一个了解。(官方文档)

  • compiler
    compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
  • compilation
    compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

对于两者,简单来说,compiler 对象代表的是不变的 webpack 环境,是针对 webpack 的;而 compilation 对象针对的是随时可变的项目文件,只要文件有改动,compilation 就会被重新创建。

hash/chunkhash/contenthash

hash

在文档中找到了几个处关于 hash 的定义:

  • Using the unique hash generated for every build;
  • [hash] in this parameter will be replaced with an hash of the compilation
  • The hash of the module identifier
    翻译来说就是,随着每一次构建会创建 compilation,然后 hash 值就会被更新

当我们构建一次,看到 dist 目录如下图:


当我们对 demo.js 随意进行修改,会发现 dist 目录变为下图:

虽然我们只是对 demo 文件就行了一次修改,但是两个文件的文件名都发生了修改,这并不是我们想要的效果,所以有了 chunkhash 的出现。

chunkhash

刚我们说到,修改文件的时候,chunkhash 能够做到不同时修改文件名。这就和他的相关定义有关系了。

  • Using hashes based on each chunks’ content
    基于每个 chunk 的内容而生成的 hash。

那在 webpack 中,chunk 又是什么呢?简单来说,我们可以将它理解成为模块。所以说对于不同的模块来说,每一次构建生成的 hash 值都是不一样的。

当我们把上述的代码改为如下:

module.exports = {
  entry: {
    index: "./src/index.js",
    demo: "./src/demo.js"
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[chunkhash].js"
  }
};

再进行一次构建,我们的 dist 目录如下图


和 hash 进行一样的操作,我们仅修改 demo 文件,dist 目录如下图

只有 demo 的文件名发生了改变,index 的文件名还是和之前是一样的。

当然在我们的页面中,除了若干的 JS 资源,也有很多的 CSS 资源,也会存在某个页面同时引用 JS 和 CSS 资源。我们使用 mini-css-extract-plugin 提取 css 文件的时候同样使用 chunkhash,进行如下的实验。

我们需要把 webpack 的配置进行修改,如下:

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

我们在 index.js 中引用了 index.css,进行一次构建,dist 目录如下:

然后我们对 index.js 进行修改,再一次构建,dist 目录如下:

问题出现了,虽然只是修改了index.js,但是css文件也跟着js文件发生了改变,因为两者是基于同一个chunk内容构建的,所以chunkhash是相同的。但是最理想的状态是我们尚未修改css,是不是可以做到css文件并不会随之改变。接下来介绍的contenthash可以做到。

contenthash

contenthash 会根据文件内容来定义 hash,如果文件内容不改变,contenthash 值则不会改变。

假设在一个页面中引用了 JS 资源,同时也引用了 CSS 资源。我们对某个 JS 资源进行修改,由于当前得 CSS 资源也是使用的 chunkhash,导致未修改 CSS 的情况构建出来的 CSS 文件也发生了改变。

让我们把 webpack 的配置代码改为如下:

module.exports = {
  entry: {
    index: "./src/index.js",
    demo: "./src/demo.js"
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[chunkhash].js"
  },
  module: {
    rules: [
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash].css"
    })
  ]
};

然后执行一次构建,dist 目录如下:

然后我们对引用了 css 的 js 进行修改,然后再次构建,dist 目录如下:


发现与之前不同的点仅仅只是js文件发生了变化,css文件和上一次还是保持一致的。

总结来说:

  • JS资源使用chunkhash
  • 抽离的CSS资源使用contenthash

参考链接

就当是一场梦,醒了很久还是很感动,终于在更新了。

打赏
支付宝
微信
本文作者:FBB
版权声明:本文首发于FBB的博客,转载请注明出处!