предисловие
Как сделать постоянное кэширование на основе веб-пакета, в настоящее время кажется, что не было очень хорошего решения для практики. В Интернете есть много статей, но очень мало действительно полезных, и нет статей, которые действительно глубоко исследованы и резюмированы. Теперь, опираясь на онлайн-проект раннего образования и собственную практику, у нас есть полный план.
текст
1. Два способа посчитать хэш webpack
Если вы хотите использовать постоянное кэширование, вы должны полагаться наwebpack
дваhash
:hash
а такжеchunkhash
.
Давайте посмотрим на конкретное значение и разницу между этими двумя значениями:
hash
: webpack
При каждой сборкеcompilation
объект, этоhash
стоимость основана наcompilation
Значение рассчитывается из всего, что внутри.
chunkhash
: Это значение основано на каждомchunk
Расчетное значение содержимого.
Так что, просто основываясь на приведенном выше описании,chunkhash
Он наиболее эффективен для постоянного кэширования.
2, тест на хэш и чанкхеш
entry | входной файл | входная зависимость |
---|---|---|
pageA | a.js | a.less->a.css, common.js->common.css |
pageB | b.js | b.less->b.css, common.js->common.css |
- Использовать вычисление хэша
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
entry: {
pageA: './src/a.js',
pageB: './src/b.js'
},
output: {
filename: '[name]-[hash].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader?minimize']
})
}
]
},
plugins: [new ExtractTextPlugin('[name]-[hash].css')]
}
построить результат
Hash: 80c922b349f516e79fb5
Version: webpack 3.8.1
Time: 1014ms
Asset Size Chunks Chunk Names
pageB-80c922b349f516e79fb5.js 2.86 kB 0 [emitted] pageB
pageA-80c922b349f516e79fb5.js 2.84 kB 1 [emitted] pageA
pageA-80c922b349f516e79fb5.css 21 bytes 1 [emitted] pageA
pageB-80c922b349f516e79fb5.css 21 bytes 0 [emitted] pageB
В заключение
Все файлы можно найтиhash
Это все равно, но вы строите его несколько разhash
все разные. Причина в том, что мы используемExtractTextPlugin
,ExtractTextPlugin
сам по себе включает асинхронный процесс извлечения, поэтому при созданииassets
Существует неопределенность (последовательность) ресурсов, иupdateHash
Он чувствителен к этому, поэтому возникает ситуация с изменением хэша, о которой говорилось выше. Кроме того, всеassets
Ресурсыhash
Значение остается постоянным, что не имеет особого смысла для постоянного кэширования всех ресурсов.
-
Вычислить с помощью chunkhash
```js
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
entry: {
pageA: './src/a.js',
pageB: './src/b.js'
},
output: {
filename: '[name]-[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
]{ test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: ['css-loader?minimize'] }) }
},
plugins: [new ExtractTextPlugin('[name]-[chunkhash].css')]
}
**构建结果**
```js
Hash: 810904f973cc0cf41992
Version: webpack 3.8.1
Time: 1038ms
Asset Size Chunks Chunk Names
pageB-e9ed5150262ba39827d4.js 2.86 kB 0 [emitted] pageB
pageA-3a2e5ef3d4506fce8d93.js 2.84 kB 1 [emitted] pageA
pageA-3a2e5ef3d4506fce8d93.css 21 bytes 1 [emitted] pageA
pageB-e9ed5150262ba39827d4.css 21 bytes 0 [emitted] pageB
В заключение
На этом этапе можно обнаружить, что независимо от того, сколько раз он запускался, изменения хэша исчезли, и каждая запись имеет свое собственное уникальное хеш-значение.Если вы будете осторожны, вы можете обнаружить, что хеш-значение ресурса стиля согласуется со сценарием входа на данный момент. Это не похоже на это. Не соответствует нашим представлениям, это говорит нам о том, что произошло что-то плохое.
3, чтобы изучить взаимосвязь между входом и хэшем файла CSS
В приведенных выше результатах построения мы обнаружили, что хеш-значение CSS совпадает с хеш-значением входного файла, Здесь мы склонны к сомнениям, есть ли связь между этими двумя файлами? Не сомневайтесь и измените содержимое файла b.css, чтобы получить результат сборки:
Hash: 3d95035f096f3ca08761
Version: webpack 3.8.1
Time: 1028ms
Asset Size Chunks Chunk Names
pageB-e9ed5150262ba39827d4.js 2.86 kB 0 [emitted] pageB
pageA-3a2e5ef3d4506fce8d93.js 2.84 kB 1 [emitted] pageA
pageA-3a2e5ef3d4506fce8d93.css 21 bytes 1 [emitted] pageA
pageB-e9ed5150262ba39827d4.css 41 bytes 0 [emitted] pageB
Нани? ? ? Измените содержимое файла css, почему хэш файла css не изменился? Ненаучно, хэш файла входа не изменился. Тщательно обдумав это, webpack рассматривает все как часть файла js. В процессе построения используется ExtractTextPlugin для извлечения стиля из фрагмента записи, но сам фрагмент входа в это время не изменился, а была извлечена часть css. Чанкунхэш рассчитывается на основе чанка, поэтому не изменять его должно быть нормальным. Но это не соответствует требованиям к постоянному кешу, который мы хотим сделать, потому что, если вы его измените, вы должны изменить и хэш.
К счастью, плагин ExtractTextPlugin предоставляет намcontenthash
изменить:
plugins: [new ExtractTextPlugin('[name]-[contenthash].css')]
Измените результаты двух сборок до и после b.css:
Hash: 3d95035f096f3ca08761
Version: webpack 3.8.1
Time: 1091ms
Asset Size Chunks Chunk Names
pageB-e9ed5150262ba39827d4.js 2.86 kB 0 [emitted] pageB
pageA-3a2e5ef3d4506fce8d93.js 2.84 kB 1 [emitted] pageA
pageA-9783744431577cdcfea658734b7db20f.css 21 bytes 1 [emitted] pageA
pageB-2d03aa12ae45c64dedd7f66bb88dd3db.css 41 bytes 0 [emitted] pageB
Hash: 7a96bcf1ef668a49c9d8
Version: webpack 3.8.1
Time: 1193ms
Asset Size Chunks Chunk Names
pageB-e9ed5150262ba39827d4.js 2.86 kB 0 [emitted] pageB
pageA-3a2e5ef3d4506fce8d93.js 2.84 kB 1 [emitted] pageA
pageA-9783744431577cdcfea658734b7db20f.css 21 bytes 1 [emitted] pageA
pageB-7e05e00e24f795b674df5701f6a38bd9.css 42 bytes 0 [emitted] pageB
Сравнение показало, что после изменения файла стиля изменился только хэш файла стиля, что соответствует нашим ожиданиям.
4. Неуправляемость и исправление id модуля
После вышеуказанного теста мы считаем само собой разумеющимся, что я завершил стабилизацию хеширования постоянного кеша. Затем мы случайно удалили файл a.less в a.js, а затем дважды собрали его:
Hash: 88ab71080c53db9d9f70
Version: webpack 3.8.1
Time: 1279ms
Asset Size Chunks Chunk Names
pageB-a2d1e1d73336f17e2dc4.js 3.82 kB 0 [emitted] pageB
pageA-96c9f5afea30e7e09628.js 3.8 kB 1 [emitted] pageA
pageA-d7ac82de795ddf50c9df43291d77b4c8.css 92 bytes 1 [emitted] pageA
pageB-56185455ea60f01155a65497e9bf6c85.css 108 bytes 0 [emitted] pageB
Hash: 172153ea2b39c2046a92
Version: webpack 3.8.1
Time: 1260ms
Asset Size Chunks Chunk Names
pageB-884da67fe2322246ab28.js 3.81 kB 0 [emitted] pageB
pageA-4c0dfb634722c556ffa0.js 3.68 kB 1 [emitted] pageA
pageA-35be2c21107ce4016c324daaa1dd5e28.css 49 bytes 1 [emitted] pageA
pageB-56185455ea60f01155a65497e9bf6c85.css 108 bytes 0 [emitted] pageB
Произошли странные вещи, я удалил файл a.less и обнаружил, что хэш файла входа pageB изменился. Я могу понять, изменился ли только хэш файлов, связанных со страницей А. но? ? ? ? Почему все изменилось? ? ? Нет, я должен посмотреть, почему он изменился.
В приведенном выше diff обнаружено, что общий идентификатор изменился после того, как мы удалили a.less. Тогда мы можем предположить, что идентификатор этого места представляет конкретный модуль, на который ссылаются.
Затем мы смотрим на информацию о двух строительных блоках до и после:
[3] ./src/a.js 284 bytes {1} [built]
[4] ./src/a.less 41 bytes {1} [built]
[5] ./src/b.js 284 bytes {0} [built]
[6] ./src/b.less 41 bytes {0} [built]
[3] ./src/a.js 264 bytes {1} [built]
[4] ./src/b.js 284 bytes {0} [built]
[5] ./src/b.less 41 bytes {0} [built]
Путем сравнения обнаруживается, что предыдущий серийный номер имеет скрытую информацию, относящуюся к странице А, в сконструированной странице Б, что очень неудобно для постоянного кэширования. Мы ожидаем, что страница B содержит только информацию, относящуюся к самой себе, и не содержит другой информации, не связанной с ней самой.
5. Изменения в идентификаторе модуля
Исключить несвязанные идентификаторы модулей или содержимое
У людей, которые будут использовать webpack, вероятно, есть одна особенность:Code Splitting
, что, по сути, представляет собой процесс разделения и повторного объединения фрагментов. Конкретно как это сделать?
The answer is CommonsChunkPlugin
, добавьте в плагин:
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
]
Далее давайте посмотрим на изменения до и после удаления a.less на странице A:
Hash: 697b36118920d991364a
Version: webpack 3.8.1
Time: 1488ms
Asset Size Chunks Chunk Names
pageB-9b2eb6768499c911a728.js 491 bytes 0 [emitted] pageB
pageA-c342383ca09604e8e7b8.js 495 bytes 1 [emitted] pageA
runtime-b6ec3c0d350aef6cbf3e.js 6.8 kB 2 [emitted] runtime
pageA-b812cf5b72744af29181f642fe4dbf38.css 43 bytes 1 [emitted] pageA
pageB-af8f1e92fd031bd1d1d8db5390b5d0d5.css 59 bytes 0 [emitted] pageB
runtime-35be2c21107ce4016c324daaa1dd5e28.css 49 bytes 2 [emitted] runtime
Hash: 7ddaf109d5aa67c43ce2
Version: webpack 3.8.1
Time: 1793ms
Asset Size Chunks Chunk Names
pageB-613cc5a6a90adfb635f4.js 491 bytes 0 [emitted] pageB
pageA-0b72f85fda69a9442076.js 375 bytes 1 [emitted] pageA
runtime-a41b8b8bfe7ec70fd058.js 6.79 kB 2 [emitted] runtime
pageB-af8f1e92fd031bd1d1d8db5390b5d0d5.css 59 bytes 0 [emitted] pageB
runtime-35be2c21107ce4016c324daaa1dd5e28.css 49 bytes 2 [emitted] runtime
Затем посмотрите на сравнение pageB в двух сборках:
После сравнения мы обнаружили, что на страницу B включен только собственный контент. Так что использование CommonsChunkPlugin оправдало наши ожидания. Извлеченный код является кодом среды выполнения webpack. Код среды выполнения также хранит информацию о модулях и чанках в webpack. Также мы обнаружили, что размер файла страницы A и страницы B также изменился. Причина этого изменения в том, что CommonsChunkPlugin будет извлекать модули, содержащиеся во входном чанке, в обычный чанк, который мы назвали runtime по умолчанию.
Предположим, мы используем некоторые библиотеки инструментов для каждой страницы в нашей разработке, такие как lodash. Поскольку поведение CommonsChunkPlugin по умолчанию извлекает общие части, возможно, lodash не изменился, но когда он извлекается из кода среды выполнения, он каждый раз будет запрашивать новый. Это не соответствует принципу минимального обновления, который нам требуется. Поэтому нам приходится вручную вмешиваться в какой-то код.
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
Бревна построены дважды до и после второй противоположной стороны:
Hash: a703a57c828ec32b24e1
Version: webpack 3.8.1
Time: 1493ms
Asset Size Chunks Chunk Names
vendor-f11f58b8150930590a10.js 541 kB 0 [emitted] [big] vendor
pageB-7d065cd319176f44c605.js 938 bytes 1 [emitted] pageB
pageA-2b7e3707314e7ec4d770.js 910 bytes 2 [emitted] pageA
runtime-e68dec8bcad8a5870f0c.js 5.88 kB 3 [emitted] runtime
pageA-d7ac82de795ddf50c9df43291d77b4c8.css 92 bytes 2 [emitted] pageA
pageB-56185455ea60f01155a65497e9bf6c85.css 108 bytes 1 [emitted] pageB
Hash: 26fc9ad18554b28cd8e1
Version: webpack 3.8.1
Time: 1806ms
Asset Size Chunks Chunk Names
vendor-d9bad56677b04b803651.js 541 kB 0 [emitted] [big] vendor
pageB-a55dadfbf25a45856d6a.js 929 bytes 1 [emitted] pageB
pageA-7cbd77a502262ddcdd19.js 790 bytes 2 [emitted] pageA
runtime-fa8eba6e81ed41f50d6f.js 5.88 kB 3 [emitted] runtime
pageA-35be2c21107ce4016c324daaa1dd5e28.css 49 bytes 2 [emitted] pageA
pageB-56185455ea60f01155a65497e9bf6c85.css 108 bytes 1 [emitted] pageB
Пока что мы ее решили: исключили проблему id модуля или контента, не связанного с собой.
Стабилизируйте идентификатор модуля и оставьте идентификатор модуля неизменным, насколько это возможно.
Идентификатор модуля — это уникальный идентификатор модуля, и этот идентификатор появится в коде после создания соответствующего фрагмента записи. Взгляните на пример кода страницы B после сборки:
__webpack_require__(7)
const sum = __webpack_require__(0)
const _ = __webpack_require__(3)
Согласно предыдущему эксперименту увеличение или уменьшение модуля приведет к изменению идентификатора модуля, поэтому, чтобы не вызвать изменение идентификатора модуля, мы можем найти только одну вещь, чтобы заменить идентификатор модуля в качестве индикатора. . Мы будем искать альтернативный идентификатор для замены идентификатора модуля в процессе сборки.
Таким образом, приведенное выше утверждение можно перевести в два шага к действию.
- Найдите способ заменить идентификатор модуля
- Найдите возможность заменить id модуля
6. Операции, связанные со стабильным идентификатором модуля
Найдите способ заменить идентификатор модуля
В нашей повседневной разработке мы часто обращаемся к модулям, на которые все ссылаются по адресам. Отсюда мы можем черпать вдохновение, можем ли мы заменить все идентификаторы модулей на путь? Еще одна вещь, которую мы узнали, это то, что мы определенно можем получить путь к ресурсу на этапе модуля разрешения веб-пакета. В начале мы беспокоились о разнице путей платформы. К счастью, исходный код webpack находится вContextModule#74а такжеContextModule#35В веб-пакете путь модуля исправлен по-разному. То есть мы можем безопасно получить путь к модулю через libIdent модуля.
Есть три хука, связанные с идентификатором модуля во время выполнения всего веб-пакета:
before-module-ids
-> optimize-module-ids
-> after-optimize-module-ids
Так что мы простоbefore-module-ids
Просто внесите в него изменения.
Напишите плагин:
'use strict'
class moduleIDsByFilePath {
constructor(options) {}
apply(compiler) {
compiler.plugin('compilation', compilation => {
compilation.plugin("before-module-ids", (modules) => {
modules.forEach((module) => {
if(module.id === null && module.libIdent) {
module.id = module.libIdent({
context: this.options.context || compiler.options.context
})
}
})
})
})
}
}
module.exports = moduleIDsByFilePath
Вышеупомянутое фактически было извлечено в плагин веб-пакетом:
NamedModulesPlugin
Так что просто добавьте в раздел плагинов
new webpack.NamedModulesPlugin()
Далее сравните изменения в файлах до и после следующих двух сборок:
Hash: e5bc78237ca9a3ad31f8
Version: webpack 3.8.1
Time: 1508ms
Asset Size Chunks Chunk Names
vendor-ebd9bfc583f45a344630.js 541 kB 0 [emitted] [big] vendor
pageB-432105effc229524c683.js 1.09 kB 1 [emitted] pageB
pageA-158bf2a923c98ab49be2.js 1.09 kB 2 [emitted] pageA
runtime-9ca4cebe90e444e723b9.js 5.88 kB 3 [emitted] runtime
pageA-d7ac82de795ddf50c9df43291d77b4c8.css 92 bytes 2 [emitted] pageA
pageB-56185455ea60f01155a65497e9bf6c85.css 108 bytes 1 [emitted] pageB
Hash: 7dce5d9dc88f619522fe
Version: webpack 3.8.1
Time: 1422ms
Asset Size Chunks Chunk Names
vendor-ebd9bfc583f45a344630.js 541 kB 0 [emitted] [big] vendor
pageB-432105effc229524c683.js 1.09 kB 1 [emitted] pageB
pageA-dae883ddaeff861761da.js 940 bytes 2 [emitted] pageA
runtime-c874a0c304fa03493296.js 5.88 kB 3 [emitted] runtime
pageA-35be2c21107ce4016c324daaa1dd5e28.css 49 bytes 2 [emitted] pageA
pageB-56185455ea60f01155a65497e9bf6c85.css 108 bytes 1 [emitted] pageB
Ничего себе, мы сравнили и обнаружили, что изменились только связанные файлы и код среды выполнения, а ни поставщик, ни pageB не изменились. Вкусно~~
Теперь, когда мы достигли нашей цели, мы можем пойти и посмотреть наш построенный код:
__webpack_require__("./src/b.less")
const sum = __webpack_require__("./src/common.js")
const _ = __webpack_require__("./node_modules/lodash/lodash.js")
Это действительно стало путем, успех~~. Но, похоже, снова возникла новая проблема.По сравнению с предыдущими файлами мы обнаруживаем, что наши файлы в целом больше, чем предыдущие. Ну, это было вызвано, когда мы изменили путь к файлу. Можем ли мы сейчас использовать хэш вместо пути к файлу? Ответ положительный, и мы можем использовать официальные плагины:
new webpack.HashedModuleIdsPlugin()
Официально NamedModulesPlugin подходит для среды разработки, и, пожалуйста, используйте HashedModuleIdsPlugin в рабочей среде.
Таким образом, мы добились использования хеша вместо исходного идентификатора модуля, чтобы сделать его стабильным. И код после сборки тоже особо не меняется.
Я думал, что это закончится здесь. Но внимательный человек обнаружит, что исполняемый файл изменяется каждый раз при компиляции. Что вызвало это? Проверьте это:
Мы заметили, что, когда количество наших входных фрагментов не изменилось, изменение содержимого входного фрагмента приводит к изменению содержимого среды выполнения только тогда, когда проблема заключается в идентификаторе фрагмента. Согласно приведенной выше операции по стабилизации идентификатора модуля числовой идентификатор фрагмента слишком нестабилен, нам необходимо изменить его так же, как указано выше.
- Найдите способ стабилизировать идентификатор чанка
- Найдите время, чтобы изменить идентификатор чанка
7. Операции, связанные со стабильным чанком
Как найти id стабильного чанка
Поскольку мы знаем, что запись уникальна при упаковке webpack, можем ли мы просто использовать имя, соответствующее записи? Так что здесь все проще: мы просто заменяем id чанка на имя нашей записи.
Найдите время, чтобы изменить идентификатор чанка
По опыту в модуле есть вышеописанный процесс, так что думаю там тоже чанки.
before-chunk-ids
-> optimize-chunk-ids
-> after-optimize-chunk-ids
Итак, напишите плагин:
'use strict'
class chunkIDsByFilePath {
constructor(options) {}
apply(compiler) {
compiler.plugin('compilation', compilation => {
compilation.plugin('before-chunk-ids', chunks => {
chunks.forEach(chunk => {
chunk.id = chunk.name
})
})
})
}
}
module.exports = chunkIDsByFilePath
К сожалению, у официалов тоже есть этот плагин, так что писать его не нужно.
NamedChunksPlugin
В построенном коде мы видим:
/******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"vendor":"ed00d7222262ac99e510","pageA":"b5b4e2893bce99fd5c57","pageB":"34be879b3374ac9b2072"}[chunkId] + ".js";
Исходный идентификатор фрагмента теперь стал именем записи, и риск изменения немного меньше. Вкусно~~
После того, как мы изменим имя, проблема будет такой же, как указанный выше идентификатор модуля изменится на имя, и файл станет больше. В настоящее время я все еще думаю об использовании хэша, чтобы справиться с этим так же, как описано выше. В это время действительно необходимо написать плагин. Волна Amway, которую мы написали сами
webpack-hashed-chunk-id-plugin.
До сих пор основные проблемы, возникающие при постоянном кэшировании, были решены.
наконец
Если вы хотите быстро построить проект, добро пожаловать на эту сторону архитектуры проекта, о.
webpack-project-seedУже есть онлайн-проекты, использующие это для запуска. Кстати, звезда.
благодарный:@pigcan