Глядя на хэш-стратегию веб-пакета из исходного кода

Webpack

Недавно было обнаружено, что хеш, сгенерированный webpack, упаковывающим один и тот же код на нескольких машинах, отличается.В сообществе нет статьи, в которой подробно объясняется стратегия генерации хэша, поэтому я просмотрел исходный код webpack и нашел абсолютный путь который содержит проект при создании хэша. Наконец, напишите подключаемый модуль веб-пакета для решения этой проблемы. В этой статье в основном объясняется использование и принцип хэша, а также способы устранения ям, таких как несогласованность хэшей между несколькими машинами.

хэш-стратегия webpack

Студенты, изучающие интерфейс, знают, что браузер будет кэшировать статический ресурс после его первой загрузки. Тот же ресурс не будет запрашиваться, если срок действия кеша не истек. Итак, как уведомить браузер о том, что ресурс изменился, когда ресурс обновляется? Хэширование имен файлов ресурсов создано для решения этой проблемы; webpack является основным инструментом построения интерфейса, поэтому в этой статье в основном описывается стратегия хэширования имен файлов после сборки webpack; webpack делится на три типа хэшей: hash, chunkhash и contenthash Давайте поговорим об использовании и принципах трех хэшей по очереди.

hash

При использовании webpack для сборки чаще всего используется хеш.После сборки webpack хэши выходных файлов js и css всего проекта одинаковые, например в проекте 6 компонентов, а компонентов 1, 2 , и 3 нужно использовать как блоки кода (chunk) Выводить набор файлов js и css, компоненты 4 и 5 выводить набор файлов js и css как блоки кода (chunk), webpack настроен следующим образом:

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

Хеши первой группы js и css файлов выводимых после сборки через webpack одинаковые, хэши второй и первой групп тоже одинаковые На следующем рисунке показано влияние хэша в проекте:

hash2

Следовательно, при изменении определенного файла хэш всех выходных файлов будет соответственно изменяться, следовательно, у этого есть недостаток: после изменения определенного файла файловый кеш всего проекта становится недействительным.

chunkhash

Влияние хэша относительно невелико по сравнению с хэшем. При использовании хэша каждый выходной файл блока кода (фрагмента) соответствует хэшу. После изменения исходного файла сохраняется только хэш выходного файла блока кода (фрагмента), где расположение исходного файла изменится; например, проект имеет 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, различаются, но хэши каждого набора внутренних js и css одинаковы.На следующем рисунке показано влияние chunkhash в проекте:

hash3

contenthash

когда используешьmini-css-extract-pluginПри подключении вы также можете использовать contenthash для получения хэша файла.По сравнению с chunkhash, contenthash имеет меньшую сферу влияния: выходные файлы js и css в каждом блоке кода (чанке) будут генерировать хеш независимо. js исходный файл изменится, изменится только хэш js файла, выводимого блоком кода (chunk), например, в проекте 6 компонентов, а компоненты 1, 2 и 3 нужно вывести как группу кода блоки (chunk).js и css файлы, компоненты 4 и 5 выводят набор js и css файлов в виде блоков кода (chunk), webpack настроен следующим образом:

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

Две группы выходных хэшей после упаковки и сборки webpack отличаются, и хэши каждой группы внутренних js и css также отличаются.На следующем рисунке показано влияние contenthash в проекте:

hash4

Разница между тремя хэшами

тип хеша разница
hash Хэш основан на целом проекте, до тех пор, пока файлы изменяются в проекте, значение HASH всего проекта будет изменено, и все файлы имеют одно и то же значение GOHH.
chunkhash Chunkhash анализирует зависимые файлы в соответствии с различными входными файлами (Entry), строит соответствующие блоки кода (фрагменты) и генерирует соответствующие хеш-значения.При изменении файла изменяется только хэш соответствующего блока кода (фрагмента) файла.
contentHash Выходные файлы js и css в каждом блоке кода (чанке) будут генерировать хэш независимо. Когда исходный файл js в блоке кода (чанке) изменен, будет только хеш файла js, выведенного блоком кода (чанком). быть сгенерировано. изменится

Хэш-принцип веб-пакета

Хэш веб-пакета черезcryptoРеализованный алгоритмами шифрования и хеширования, webpack предоставляет hashDigest (метод кодирования, используемый при генерации хэша, по умолчанию'hex'), hashDigestLength (длина префикса хеш-дайджеста, по умолчанию20), hashFunction (алгоритм хеширования, по умолчанию'md5'), hashSalt (необязательное значение соли) и другие параметры для реализации пользовательского хеширования; по очереди описываются следующие три стратегии генерации хэша.

Все три стратегии генерации хэшей в webpack генерируются на основе содержимого исходного кода, но исходный код был инкапсулирован webpack в код, который может работать в среде webpack, включая абсолютный путь к каждому исходному файлу; webpack даст это по исходному коду на этапе сборки.Соответствующий модуль (модуль) генерирует _buildHash (впоследствии на основе этого значения генерируется хэш модуля).Как показано на рисунке ниже, вы можете видеть, что исходный код содержит абсолютный путь.

hash1

Webpack генерирует три хэша на этапе печати и, наконец, решает, какой хеш использовать в соответствии с выходной конфигурацией.Compilation.createHashфункция для генерации хэша.

Процесс генерации хэша и чанкхэша

Далее в основном говорится о процессе генерации хэша, в который включен процесс генерации чанкхэша. Первым шагом для генерации хеша веб-пакетом является получение всех модулей в процессе компиляции и использование _buildHash, сгенерированного всеми модулями на этапе сборки, в качестве содержимого для создания нового хеш-значения; затем получение всех блоков кода (фрагментов) и размещение блоков кода соответственно Хэш модуля, содержащегося в (chunk), используется в качестве хэша блока кода генерации контента (chunk), как показано в исходном коде ниже.

// 非源码,代码有删减
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

Генерация хеша контента отличается от первых двух генераций хэша, она генерируетсяmini-css-extract-pluginа такжеJavascriptModulesPluginХэш, сгенерированный плагином;mini-css-extract-pluginЭто плагин, который разделяет модули типа css при упаковке и сборке веб-пакета.При использовании этого плагина он будет генерировать хэши для файлов типа css отдельно, он будет преобразовывать все типы в блоке кода (чанке) вcss/mini-extractХэш модуля используется в качестве содержимого для генерации хэша.

     // 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 будет вызвано событие contentHash, зарегистрированное подключаемым модулем JavascriptModulesPlugin, и хэш всех модулей, тип которых является функцией в блоке кода (чанке), будет использоваться в качестве содержимого для генерации хэша.

// 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 приносит нам большое удобство, он также имеет некоторые недостатки; все три хэш-стратегии webpack полагаются на _buildHash модуля, а значение _buildHash зависит от содержимого исходного файла и абсолютного пути к модулю, поэтому то же Значение хеш-функции исходного кода, созданного на разных машинах, не обязательно будет одинаковым, если только пути проекта на двух машинах не совпадают; если для сборки и развертывания одного и того же проекта подключено несколько компьютеров, значение хеш-функции может быть разные, в результате чего при доступе к js или css появляется явление 404.

Если вы хотите развернуть хэш на нескольких машинах, следующая стратегия решения многомашинной сборки для генерации хэша:

  • В исходном файле js в проекте (исключая node_modules) генерация хэша использует исходный код, который не инкапсулирован webpack, но использует содержимое исходного файла js для решения проблемы несогласованности хэшей, вызванной абсолютным путем исходного кода после инкапсуляция.
  • Хэш исходного файла CSS в проекте (исключая node_modules) генерирует используемый исходный код CSS, и проблемы с путями, как раньше, нет.
  • Генерация хеша css в node_modules использует относительный путь файла css плюс номер версии пакета npm, чтобы решить проблему пути, вызванную наличием sourceMap в файле стиля в node_modules.
  • js node_modules следующий хеш, сгенерированный с использованием относительного пути к файлу плюс номер версии пакета js npm, хэш решает некоторые несоответствия сгенерированный пакет node_modules npm.