webpack打包的3种hash值区别

更新日期: 2022-02-07阅读: 2k标签: Hash

我们都知道,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://fly63.com/article/detial/11090

关于Webpack中hash的用法

在webpack的配置项中,可能会见到hash这样的字符。这种带哈希值的文件名,可以帮助实现静态资源的长期缓存,在生产环境中非常有用。带hash的文件是现在web启用缓存来提升性能比较建议的形式。

location.hash属性#_window.location.hash 使用

hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分),#是用来指导浏览器动作的,都会被浏览器解读为位置标识符 , 这意味着这些字符都不会被发送到服务器端。对服务器端完全无用。所以,HTTP请求中不包括#。

vue-router之hash模式和history模式

hash模式即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。history模式利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。

一致性hash算法和实现

一致性hash算法,是麻省理工学院1997年提出的一种算法,目前主要应用于分布式缓存当中。一致性hash算法可以有效地解决分布式存储结构下动态增加和删除节点所带来的问题。

多阶Hash算法

在分布式系统中,会经常用到K-V存储,一般实现的方式有红黑树或者哈希表,在Redis中还用到了跳表。都是通过一个确定的Key值,来查找Key附带的Value属性。本文会介绍一种高效的算法——多阶Hash。

vue-router的两种模式:hash和history详解

Vue.js是一种流行的JavaScript框架,用于构建交互式Web应用程序。Vue-router是Vue.js框架中的一个重要组件,它允许您为应用程序创建路由。在Vue-router中,有两种不同的路由模式:hash模式和history模式

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