webpack打包的3种hash值区别

更新日期: 2022-02-07阅读: 502标签: webpack

我们都知道,webpack有各种hash值,包括每次项目构建hash,不同入口的chunkhash、文件的内容contenthash,这么多hash,它们有什么区别呢?


三种设置的区别

hash类型区别
hashhash是根据整个项目构建,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值
chunkhashchunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的代码块(chunk),生成对应的哈希值,某文件变化时只有该文件对应代码块(chunk)的hash会变化
contentHash每一个代码块(chunk)中的js和css输出文件都会独立生成一个hash,当某一个代码块(chunk)中的js源文件被修改时,只有该代码块(chunk)输出的js文件的hash会发生变化

1、Hash

hash是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值。

使用webpack构建时hash是使用最多的一种,webpack构建后整个项目的js和css输出文件的hash都相同;例如一个项目有6个组件,需要把组件1、2、3作为代码块(chunk)输出一组js和css文件,组件4、5作为代码块(chunk)输出一组js和css文件,webpack如下配置:
output: {
    path: path.resolve(__dirname, OUTPUT_PATH),
    filename: '[name].[hash].js',// 使用hash
    publicPath: '/dist/webpack/'
  }

通过webpack构建完后输出的第一组js、css文件的hash相同,并且第二组和第一组的hash也相同,下图是hash在项目中的效果:


所以只要某一个文件被修改,所有输出文件的hash都会跟着变化;因此它有一个弊端,一旦修改了某一个文件,整个项目的文件缓存都会失效。

2、chunkhash

chunkhash,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。简单来说这种是根据不同入口来配置的,比如react,react-router、resux等公共入口文件,只要这些没有改变,那么他对应生成的js的hash值也不会改变。

chunkhash相对hash影响范围比较小,使用chunkhash时,每一个代码块(chunk)输出文件对应一个hash,某源文件被修改后,只有该源文件所在代码块(chunk)的输出文件的hash会变化;例如一个项目有6个组件,需要把组件1、2、3作为代码块(chunk)输出一组js和css文件,组件4、5作为代码块(chunk)输出一组js和css文件,webpack如下配置:

output: {
    path: path.resolve(__dirname, OUTPUT_PATH),
    filename: '[name].[chunkhash].js', // 使用chunkhash
    publicPath: '/dist/webpack/'
  }

通过webpack打包构建完后输出的两组hash不同,但是每一组内部js和css的hash相同,下图是chunkhash在项目中的效果:


3、contenthash

contenthash主要是处理关联性,比如一个js文件中引入css,但是会生成一个js文件,一个css文件,但是因为入口是一个,导致他们的hash值也相同,所以当只有js修改时,关联输出的css、img等文件的hash值也会改变,这种情况下就需要contenthash了。

当使用mini-css-extract-plugin插件时还可以使用contenthash来获取文件的hash,contenthash相对于chunkhash影响范围更小;每一个代码块(chunk)中的js和css输出文件都会独立生成一个hash,当某一个代码块(chunk)中的js源文件被修改时,只有该代码块(chunk)输出的js文件的hash会发生变化;例如一个项目有6个组件,需要把组件1、2、3作为代码块(chunk)输出一组js和css文件,组件4、5作为代码块(chunk)输出一组js和css文件,webpack如下配置:

output: {
    path: path.resolve(__dirname, OUTPUT_PATH),
    filename: '[name].[contenthash].js', // 使用contenthash
    publicPath: '/dist/webpack/'
  }

通过webpack打包构建完后输出的两组hash不同,而且每一组内部js和css的hash也不同,下图是contenthash在项目中的效果:



使用哈希值的作用

1、提升webpack打包速度和项目体积

将webpack入口的chunk文件中所有公共的代码提取出来,减少代码体积;同时提升webpack打包速度。

2、利用缓存机制

依赖的公共模块文件一般很少更改或者不会更改,这样独立模块文件提取出可以长期缓存。

3、利用浏览器缓存

方便我们在改动代码的时候,线上代码发版后及时读取最新的js文件,防止出现缓存问题。


webpack的hash原理

webpack的hash是通过crypto加密和哈希算法实现的,webpack提供了hashDigest(在生成 hash 时使用的编码方式,默认为 'hex')、hashDigestLength(散列摘要的前缀长度,默认为 20)、hashFunction(散列算法,默认为 'md5')、hashSalt(一个可选的加盐值)等参数来实现自定义hash;下面依次讲述三种hash生成策略。

webpack的三种hash生成策略都是根据源码内容来生成,只是该源码已经被webpack封装成能在webpack环境中运行的代码了,包含每一个源文件的绝对路径;webpack会在build阶段根据源码给对应的模块(module)生成一个_buildHash(后续根据该值生成模块的hash),如下图所示可以看到源码中包含绝对路径。

webpack在seal阶段生成三种hash,最后根据output的配置决定使用哪种hash,webpack通过执行Compilation.createHash函数来生成hash。

hash和chunkhash的生成过程

下面主要讲一下hash的生成过程,其中chunkhash的生成过程包含在其中。webpack生成hash的第一步是获取Compilation下面的所有modules,把所有的module在build阶段生成的_buildHash作为内容生成一个新的hash值;然后获取到所有的代码块(chunks),分别把代码块(chunk)中包含的module的hash作为内容生成代码块(chunk)的hash,该hash就是配置chunkhash时需要使用的hash值;最后把所有代码块(chunks)的hash作为内容生成一个hash就是最终的hash,如下源码所示。

// 非源码,代码有删减
createHash() {
		// 把所有的module根据在build阶段生成_buildHash来生成一个新的hash值
		const modules = this.modules;
		for (let i = 0; i < modules.length; i++) {
			const module = modules[i];
			const moduleHash = createHash(hashFunction);
			module.updateHash(moduleHash);
		}
		// clone needed as sort below is inplace mutation
		const chunks = this.chunks.slice();
	
	// 给所有的chunks分别生成一个hash
		for (let i = 0; i < chunks.length; i++) {
			const chunk = chunks[i];
			const chunkHash = createHash(hashFunction);
			try {
				chunk.updateHash(chunkHash);
				// chunk中包含的所有module的hash作为内容生成一个hash值
				template.updateHashForChunk(
					chunkHash,
					chunk,
					this.moduleTemplates.javascript,
					this.dependencyTemplates
				);
				chunk.hash = chunkHash.digest(hashDigest);
				
				// 把所有的chunks的hash作为内容
				hash.update(chunk.hash);
        
				// 生成contentHash
				this.hooks.contentHash.call(chunk);
			} catch (err) {}
		}
		// 生成hash
		this.fullHash = hash.digest(hashDigest);
		this.hash = this.fullHash.substr(0, hashDigestLength);
	}

contenthash生成过程

contenthash生成跟前两种hash生成不一样,它是通过mini-css-extract-plugin和JavascriptModulesPlugin插件生成的hash;mini-css-extract-plugin是webpack打包构建时把css类型的module单独分类出来的插件,使用该插件时会为css类型的文件单独生成hash;它会把代码块(chunk)中所有类型为css/mini-extract的module的hash作为内容生成chunkhash。

     // mini-css-extract-plugin插件的css文件hash生成的钩子函数
     
     compilation.hooks.contentHash.tap(pluginName, chunk => {
        const { outputOptions } = compilation;
        const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
        const hash = createHash(hashFunction);
        // 把chunk中所有类型为`css/mini-extract`的module的hash作为内容生成hash
        for (const m of chunk.modulesIterable) {
          if (m.type === MODULE_TYPE) {
            m.updateHash(hash);
          }
        }
        const { contentHash } = chunk;
        // 把生成的内容放入chunk对象的contentHash中
        contentHash[MODULE_TYPE] = hash.digest(hashDigest).substring(0, hashDigestLength);
      });

contentHash钩子触发时会调用JavascriptModulesPlugin插件注册的contentHash事件,把代码块(chunk)中所有类型为函数的module的hash作为内容生成hash。

// JavascriptModulesPlugin插件为js生成contentHash的钩子函数

compilation.hooks.contentHash.tap("JavascriptModulesPlugin", chunk => {
				// ...此处有删减
					for (const m of chunk.modulesIterable) {
						if (typeof m.source === "function") {
							hash.update(m.hash);
						}
					}
					chunk.contentHash.javascript = hash
						.digest(hashDigest)
						.substr(0, hashDigestLength);
				});


多机器构建方案

webpack的hash虽然给我们带来了极大的方便,但是也存在一些弊端;webpack的三种hash策略都依赖module的_buildHash,而_buildHash值又依赖module的源文件内容和绝对路径,所以同一份源码在不同的机器上构建出来的hash值不一定一样,除非两台机器上的项目路径完全相同;若线上存在多机器构建部署同一个项目时,可能hash值不同而导致访问js或者css时出现404现像。
若想多机器部署hash一样,下面是解决多机器构建生成hash的策略:

  • 项目中的js源文件(排除node_modules)hash生成使用的是非webpack封装后的源码,而是使用js源文件内容,解决封装后源码有绝对路径而导致hash不一致问题。
  • 项目中的css源文件(排除node_modules)hash生成使用的css源码,跟以前一样不存在路径问题。
  • node_modules下面的css的hash生成使用该css文件的相对路径加上该npm包版本号,解决node_modules中的样式文件存在sourceMap而导致路径问题。
  • node_modules下面的js的hash生成使用该js文件的相对路径加上该npm包版本号,解决node_modules中某些npm包生成的hash不一致问题。


链接: https://www.fly63.com/article/detial/11090

浅谈Webpack打包工具的应用

webpack 在前端工程中随处可见,当前流行的 vue, react, weex 等解决方案都推崇 webpack 作为打包工具。前端工具云集的时代,这是你值得选择的之一。

一步一步webpack,webpack的学习入门

webpack是前端工程构建的一套工具,为什么一个程序称之为一套呢,是因为webpack其实是npm的一个模块,使用起来的话,这期间还需要很多其它模块来进行支持,所以我称之为一套工具。

如何写 Webpack 配置文件

本文从一个小Demo开始,通过不断增加功能来说明webpack的基本配置,只针对新手。webpack基本的配置就可以熟悉了,会引入loader,配置loader选项,会设置alias,会用plugins差不多。

WebPack中Plugins的使用和整理,以及常用的Plugins插件

Plugins是webpack的基础,我们都知道webpage的plugin是基于事件机制工作的,这样最大的好处是易于扩展。讲解如果扩展内置插件和其他插件,以及我们常用的Plugins插件

大多数项目中会用到的webpack小技巧

webpack技巧的总结:进度汇报、压缩、复数文件打包、分离app文件与第三方库文件、资源映射、输出css文件、开发模式、分析包的大小、更小的react项目、更小的Lodash、引入文件夹中所有文件、清除extract-text-webpack-plugin日志。

优化Webpack构建性能的几点建议

Webpack 作为目前最流行的前端构建工具之一,在 vue/react 等 Framework 的生态圈中都占据重要地位。在开发现代 Web 应用的过程中,Webpack 和我们的开发过程和发布过程都息息相关,如何改善 Webpack 构建打包的性能也关系到我们开发和发布部署的效率。

Webpack 4正式发布了!

新版 Webpack 中我们所做的每一个更新目的都在于此,为了当大家在使用 Webpack 的时候敏捷连续毫无顿挫感。 webpack 4 进行构建性能测试,得出的结果非常有趣。结果很惊人,构建时间降低了 60%-98%!

Webpack 4.0.0不再支持 Node.js 4

Webpack 是一个现代 JavaScript 应用程序的模块打包器 (module bundler) 。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块

我当初为什么写webpack_Tobias Koppers

Tobias Koppers是一位自由软件开发者,家住德国纽伦堡。他因写出webpack这个已有数百万开发者使用的开源软件而名噪一时。他目前专注于JavaScript和开源项目。以下是我对他个人的专访,希望对大家有所启发。

webpack项目轻松混用css module

本文讲述css-loader开启css模块功能之后,如何与引用的npm包中样式文件不产生冲突。比如antd-mobilenpm包的引入。在不做特殊处理的前提下,样式文件将会被转译成css module。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!