Как сделать постоянное кэширование на основе веб-пакета, похоже, не было лучшей практики. В Интернете есть множество различных статей, а также куча открытых отзывов об ошибках и предложений, которые легко могут сбить людей с толку и сломить их психику.
Как разработчик, самая большая привлекательность заключается в том, что он может быть стабильным после создания без изменения внутреннего содержания записи.
TL;DR;
Потяните до конца, чтобы увидеть сводку XD
Два способа расчета хэша
Первым шагом к постоянному кэшированию является хеширование, которое предоставляется в веб-пакете двумя способами:hash
а такжеchunkhash
Может быть много студентов, которые стирают разницу между ними здесь:
hash
: объект компиляции будет сгенерирован в одной сборке веб-пакета, значение хеш-функции вычисляется из всего содержимого в компиляции,
chunkhash
: каждый фрагмент рассчитывается на основе собственного содержимого.
Только из описания обращения,chunkhash
Должен быть более эффективным в постоянном кеше.
Так ли это?Далее задаем сценарий применения.
Установить сцену
entry | входной файл | Цепочка зависимостей входного файла |
---|---|---|
pageA | a.js | a.less <- a.css common.js <- common.less <- common.css lodash |
pageB | b.js | b.less <- b.css common.js <- common.less <- common.css lodash |
- хеш вычисляется как
hash
Время:
......
module.exports = {
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
},
output: {
path: path.join(cwd, 'dist'),
filename: '[name]-[hash].js'
},
module: {
rules: ...
},
plugins: [
new ExtractTextPlugin('[name]-[hash].css'),
]
}
Результат сборки:
Hash: 7ee8fcb953c70a896294
Version: webpack 3.8.1
Time: 6308ms
Asset Size Chunks Chunk Names
pageB-7ee8fcb953c70a896294.js 525 kB 0 [emitted] [big] pageB
pageA-7ee8fcb953c70a896294.js 525 kB 1 [emitted] [big] pageA
pageA-7ee8fcb953c70a896294.css 147 bytes 1 [emitted] pageA
pageB-7ee8fcb953c70a896294.css 150 bytes 0 [emitted] pageB
Если вы будете осторожны и попробуете еще несколько раз, вы можете обнаружить, что значение хеша изменится, даже если все содержимое не изменилось.Причина в том, что мы используем извлечение, а извлечение само по себе включает асинхронный процесс извлечения, поэтому он существует, когда генерация ресурсов активов.Есть неопределенность (последовательность), и updateHash чувствителен к ней, поэтому возникает ситуация изменения хэша, о которой говорилось выше. Кроме того, хеш-значения всех ресурсов активов остаются согласованными, что не имеет большого значения для постоянного кэширования всех ресурсов.
- хеш вычисляется как
chunkhash
Время:
......
module.exports = {
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
},
output: {
path: path.join(cwd, 'dist'),
filename: '[name]-[chunkhash].js'
},
module: {
rules: ...
},
plugins: [
new ExtractTextPlugin('[name]-[chunkhash].css'),
]
}
Результат сборки:
Hash: 1b432b2e0ea7c80439ff
Version: webpack 3.8.1
Time: 1069ms
Asset Size Chunks Chunk Names
pageB-58011d1656e7b568204e.js 525 kB 0 [emitted] [big] pageB
pageA-5c744cecf5ed9dd0feaf.js 525 kB 1 [emitted] [big] pageA
pageA-5c744cecf5ed9dd0feaf.css 147 bytes 1 [emitted] pageA
pageB-58011d1656e7b568204e.css 150 bytes 0 [emitted] pageB
На этом этапе вы можете обнаружить, что сколько раз он запускался, изменение хэша исчезло, и каждая запись имеет свое собственное уникальное хеш-значение.Если вы будете осторожны, вы можете обнаружить, что хеш-значение ресурса стиля соответствует сценарий входа в это время. Это, кажется, не так. Не соответствует нашим представлениям, это говорит нам, что что-то плохое случилось.
Затем попробуйте изменить его по желаниюb.css
Затем перестройте, чтобы получить следующие журналы:
Hash: 50abba81a316ad20f82a
Version: webpack 3.8.1
Time: 1595ms
Asset Size Chunks Chunk Names
pageB-58011d1656e7b568204e.js 525 kB 0 [emitted] [big] pageB
pageA-5c744cecf5ed9dd0feaf.js 525 kB 1 [emitted] [big] pageA
pageA-5c744cecf5ed9dd0feaf.css 147 bytes 1 [emitted] pageA
pageB-58011d1656e7b568204e.css 147 bytes 0 [emitted] pageB
Произошел невероятный ужас, хеш-значение скрипта PageB и стиль не изменились. Почему? Это не сложно понять, если подумать, потому что весь контент в вебпаке расценивается как часть js, и когда происходит сборка и вступает в силу извлечение, стиль извлекается из чанка входа, чего не происходит с Изменить, потому что измененная часть была извлечена и превращена в обычный фрагмент, а хеш-функция основана на содержимом фрагмента, поэтому никакие изменения не должны соответствовать ожидаемому поведению. Хотя принципы и результаты соответствуют ожиданиям, это не то, что нужно постоянному кешу. К счастью, плагин extract-text-plugin предоставляет извлеченный контент.contenthash
который:new ExtractTextPlugin('[name]-[contenthash].css')
Hash: 50abba81a316ad20f82a
Version: webpack 3.8.1
Time: 1177ms
Asset Size Chunks Chunk Names
pageB-58011d1656e7b568204e.js 525 kB 0 [emitted] [big] pageB
pageA-5c744cecf5ed9dd0feaf.js 525 kB 1 [emitted] [big] pageA
pageA-3ebfe4559258be46a13401ec147e4012.css 147 bytes 1 [emitted] pageA
pageB-c584acc56d4dd7606ab09eb7b3bd5e9f.css 147 bytes 0 [emitted] pageB
В этот момент мы модифицируемb.css
Затем перестройте, чтобы получить следующие журналы:
Hash: 08c8682f823ef6f0d661
Version: webpack 3.8.1
Time: 1313ms
Asset Size Chunks Chunk Names
pageB-58011d1656e7b568204e.js 525 kB 0 [emitted] [big] pageB
pageA-5c744cecf5ed9dd0feaf.js 525 kB 1 [emitted] [big] pageA
pageA-3ebfe4559258be46a13401ec147e4012.css 147 bytes 1 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 0 [emitted] pageB
отличный! Все работает как положено, изменился только хэш стиля pageB. Вы думаете, что все кончено, но всегда есть повороты
Далее мы пытаемсяa.js
удалить зависимостиa.less
, сделайте еще одну сборку и получите следующий лог
Hash: 649f27b36d142e5e39cc
Version: webpack 3.8.1
Time: 1557ms
Asset Size Chunks Chunk Names
pageB-0ca5aed30feb05b1a5e2.js 525 kB 0 [emitted] [big] pageB
pageA-1a8ce6dcab969d4e4480.js 525 kB 1 [emitted] [big] pageA
pageA-f83ea969c4ec627cb92bea42f12b75d6.css 91 bytes 1 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 0 [emitted] pageB
Снова произошли странные вещи, здесь мы можем понять, что сценарий и стиль страницы А изменились. Но не так, как ожидалось, сценарий страницы B также изменился.
Итак, давайте перейдем к pageB.js, чтобы посмотреть, что изменилось.
Мы можем получить конкретное место изменения с помощью следующей команды
$ git diff dist/pageB-58011d1656e7b568204e.js dist/pageB-0ca5aed30feb05b1a5e2.js
Результат:
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
-/******/ return __webpack_require__(__webpack_require__.s = 75);
+/******/ return __webpack_require__(__webpack_require__.s = 74);
/******/ })
/************************************************************************/
/******/ ([
/***/ }),
/* 73 */,
-/* 74 */,
-/* 75 */
+/* 74 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
console.log('bx');
-__webpack_require__(76);
+__webpack_require__(75);
__webpack_require__(38);
__webpack_require__(40);
/***/ }),
-/* 76 */
+/* 75 */
/***/ (function(module, exports) {
// removed by extract-text-webpack-plugin
Выше мы можем ясно понять, когда после удаления a.less внутри страницы произошло общее изменение идентификатора. Можно предположить, что id представляет конкретную ссылку на модуль.
На самом деле, в конце строительства WebPack даст удостоверение личности, к которому мы выделили каждый модуль.
case: pageA перед удалением a.less
[73] ./a.js 93 bytes {1} [built]
[74] ./a.less 41 bytes {1} [built]
[75] ./b.js 94 bytes {0} [built]
[76] ./b.less 41 bytes {0} [built]
case: pageA после удаления a.less
[73] ./a.js 72 bytes {1} [built]
[74] ./b.js 94 bytes {0} [built]
[75] ./b.less 41 bytes {0} [built]
Путем сравнения обнаруживается, что до того, как pageA удалит зависимость a.less, в коде, который она создает, есть скрытые скрытые./* 73 */,
а также/* 74 */,
, то есть сценарий страницы B содержитa.js
, a.less
информация об идентификаторе модуля. Это не так, как ожидается для настойчивости. Мы ожидаем, что страница B не будет содержать ничего, что к ней не относится.
Вот два предложения
Предложение 1: Как исключить нерелевантный идентификатор модуля или контент
Предложение 2: Как сохранить идентификатор модуля как можно более постоянным
изменение идентификатора модуля
Давайте посмотрим один за другим.
Предложение 1: Как исключить нерелевантный идентификатор модуля или контент
Короче говоря, наша цель - исключить этот неактуальный контент в дополнение к Pagea Pagea и PageB.
Люди, знакомые с webpack, более или менее слышали о разделении кода, которое, по сути, представляет собой процесс разделения и повторного объединения фрагментов. Кто может выполнить это задание?
Я уверен, вы догадались -CommonsChunkPlugin
Далее откатываем все предыдущие изменения. чтобы проверить, верна ли наша догадка.
В конфигурацию сборки добавляемCommonsChunkPlugin
...
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'runtime'
+ }),
],
...
case: pageA перед удалением a.less
Hash: fc0f3a602209ca0adea9
Version: webpack 3.8.1
Time: 1182ms
Asset Size Chunks Chunk Names
pageB-ec1c1e788034e2312e56.js 316 bytes 0 [emitted] pageB
pageA-cd16b75b434f1ff41442.js 315 bytes 1 [emitted] pageA
runtime-3f77fc83f59d6c4208c4.js 529 kB 2 [emitted] [big] runtime
pageA-8c3d50283e85cb98eafa5ed6a3432bab.css 56 bytes 1 [emitted] pageA
pageB-64db1330bc88b15e8c5ae69a711f8179.css 59 bytes 0 [emitted] pageB
runtime-f83ea969c4ec627cb92bea42f12b75d6.css 91 bytes 2 [emitted] runtime
case: pageA после удаления a.less
Hash: 8881467bf592ceb67696
Version: webpack 3.8.1
Time: 1185ms
Asset Size Chunks Chunk Names
pageB-8e3a2584840133ffc827.js 316 bytes 0 [emitted] pageB
pageA-a5d2ad06fbaf6a0e42e0.js 190 bytes 1 [emitted] pageA
runtime-f8bc79ce500737007969.js 529 kB 2 [emitted] [big] runtime
pageB-64db1330bc88b15e8c5ae69a711f8179.css 59 bytes 0 [emitted] pageB
runtime-f83ea969c4ec627cb92bea42f12b75d6.css 91 bytes 2 [emitted] runtime
В этот момент мы передаем следующую команду
$ git diff dist/pageB-8e3a2584840133ffc827.js dist/pageB-ec1c1e788034e2312e56.js
Сравните скрипт pageB
webpackJsonp([0],{
-/***/ 74:
+/***/ 75:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
console.log('bx');
-__webpack_require__(75);
+__webpack_require__(76);
__webpack_require__(27);
__webpack_require__(28);
/***/ }),
-/***/ 75:
+/***/ 76:
/***/ (function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ })
-},[74]);
\ No newline at end of file
+},[75]);
\ No newline at end of file
Обнаружено, что содержимое модуля, наконец, больше не содержит другого содержимого, не относящегося к странице B. Другими словамиCommonsChunkPlugin
Наши ожидания оправдались, по сути, эта часть контента является средой выполнения webpack, в которой хранится информация о модулях и чанках webpack. Также интересно, что страницы A и B также удивительно уменьшены в размере из-за поведения по умолчаниюCommonsChunkPlugin
Модули, содержащиеся в фрагменте записи, будут извлечены в эту именованную среду выполнения.
в обычном куске. Наша цель в постоянном кэшировании — свести изменения к минимуму. Но в приведенных выше двух изменениях нетрудно обнаружить, что мы изменили только страницу A, но страница B во время выполнения изменилась.CommonsChunkPlugin
Поведение извлечения lodash по умолчанию, у нас есть веские основания полагать, что lodash не обновляется, а нуждается в обновлении с большими затратами, что не соответствует принципу минимизации.
Итак, еще один момент, который необходимо упомянуть здесь, заключается в том, чтоCommonsChunkPlugin
Его использование не ограничивается автоматическим извлечением.В контексте постоянного кэширования нам также необходимо вручную вмешиваться в эту часть контента, извлекать общедоступный контент в истинном смысле и стараться гарантировать, что он не будет изменен в будущем. .
Здесь нам нужно сделать еще один шаг, чтобы настроить содержимое публичного раздела. Уведомлениеruntime
Ставить последним!
...
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
+ "vendor": [ "lodash" ],
},
...
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'vendor',
+ minChunks: Infinity
+ }),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
],
...
Затем мы откатываем все изменения. Посмотрим, оправдает ли он наши ожидания!
case: pageA перед удалением a.less
Hash: 719ec2641ed362269d4e
Version: webpack 3.8.1
Time: 4190ms
Asset Size Chunks Chunk Names
vendor-32e0dd05f48355cde3dd.js 523 kB 0 [emitted] [big] vendor
pageB-204aff67bf5908c0939c.js 559 bytes 1 [emitted] pageB
pageA-44af68ebd687b6c800f7.js 558 bytes 2 [emitted] pageA
runtime-77e92c75831aa5a249a7.js 5.88 kB 3 [emitted] runtime
pageA-3ebfe4559258be46a13401ec147e4012.css 147 bytes 2 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 1 [emitted] pageB
case: pageA после удаления a.less
Hash: 93ab4ab5c33423421e51
Version: webpack 3.8.1
Time: 4039ms
Asset Size Chunks Chunk Names
vendor-329a6b18e90435921ff8.js 523 kB 0 [emitted] [big] vendor
pageB-96f40d170374a713b0ce.js 559 bytes 1 [emitted] pageB
pageA-1d31b041a29dcde01cc5.js 433 bytes 2 [emitted] pageA
runtime-f612a395e44e034757a4.js 5.88 kB 3 [emitted] runtime
pageA-f83ea969c4ec627cb92bea42f12b75d6.css 91 bytes 2 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 1 [emitted] pageB
Пока разумное использованиеCommonsChunkPlugin
Решаем предложение 1
Предложение 2: Как сохранить идентификатор модуля как можно более постоянным
идентификатор модуля — это уникальный идентификатор модуля, и этот идентификатор появится в коде после построения, например, в следующем фрагменте скрипта pageB
/***/ 74:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
console.log('bx');
__webpack_require__(75);
__webpack_require__(13);
__webpack_require__(15);
/***/ }),
скопировать код
Увеличение или уменьшение модуля или изменение ссылочного веса обязательно приведут к изменению id (как выделить id здесь обсуждаться не будет. Если вам интересно, вы можете использовать плагин OccurrenceOrderPlugin в webpack@1 как врезка. Плагин находится в webpack@2. встроен по умолчанию). Так что нетрудно представить, что если вы хотите решить эту проблему, вы должны найти другой контент, который сможет поддерживать уникальность и коррекцию id во время построения.
Таким образом, предложение 2 разбивается на две части.
- Найти альтернативный числовой идентификатор модуля
- Найдите возможность исправить id
Найти альтернативный числовой идентификатор модуля
Интуитивной первой реакцией должен быть путь, потому что путь ресурса должен быть уникальным в сборке, и нам также может очень повезти, что путь ресурса должен быть получен в ссылке модуля разрешения в веб-пакете.
Но когда дело доходит до путей, мы должны беспокоиться о том, что разделение путей под окнами и macos несовместимо.Если мы возьмем генерацию id и сделаем это сами, нам придется иметь дело с множеством возможных различий?вопрос. С такой путаницей я проверил исходный код вебпака, который находится вContextModule#74а такжеContextModule#35В веб-пакете путь модуля исправлен по-разному.
То есть мы можем безопасно получить путь к модулю через метод модуля libIdent.
Найдите возможность исправить id
Я всегда думаю, что самое важное в веб-пакете — это то, что реализация его общего плагина полностью основана на его системе событий с возможностью нажатия, которая идеальна с точки зрения гибкости. Я перепишу статью, чтобы в дальнейшем поделиться содержанием механизма событий.
Здесь нам нужно только знать, что события, связанные с идентификатором модуля во время всего процесса выполнения веб-пакета:
before-module-ids
-> optimize-module-ids
-> after-optimize-module-ids
Так что нам просто нужноbefore-module-ids
Идентификатор может быть исправлен в это время.
Реализовать стабильность идентификатора модуля
// 插件实现核心片段
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
});
}
});
});
});
}
Эта часть контента была извлечена как встроенный плагин webpack.NamedModulesPlugin
Так что просто добавьте этот плагин в конфигурацию сборки одним маленьким шагом.
...
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
"vendor": [ "lodash" ],
},
...
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
+ new webpack.NamedModulesPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
],
...
Откатываем все изменения кода до этого, проведем соответствующее сравнение
case: pageA перед удалением a.less
Hash: 563971a30d909bbcb0db
Version: webpack 3.8.1
Time: 1271ms
Asset Size Chunks Chunk Names
vendor-a5620db988a639410257.js 539 kB 0 [emitted] [big] vendor
pageB-42b894ca482a061570ae.js 681 bytes 1 [emitted] pageB
pageA-b7d7de62392f41af1f78.js 680 bytes 2 [emitted] pageA
runtime-dc322ed118963cd2e12a.js 5.88 kB 3 [emitted] runtime
pageA-3ebfe4559258be46a13401ec147e4012.css 147 bytes 2 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 1 [emitted] pageB
case: pageA после удаления a.less
Hash: 0d277f49f54159bc7286
Version: webpack 3.8.1
Time: 950ms
Asset Size Chunks Chunk Names
vendor-a5620db988a639410257.js 539 kB 0 [emitted] [big] vendor
pageB-42b894ca482a061570ae.js 681 bytes 1 [emitted] pageB
pageA-bedb93c1db950da4fea1.js 539 bytes 2 [emitted] pageA
runtime-85b317d7b21588411828.js 5.88 kB 3 [emitted] runtime
pageA-f83ea969c4ec627cb92bea42f12b75d6.css 91 bytes 2 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 1 [emitted] pageB
использовать с этого моментаNamedModulesPlugin
Мы добились того, что изменения на странице А вызывают изменения только в сценариях, стилях и среде выполнения страницы А, в то время как производитель, сценарии и стили страницы Б не изменились.
Фрагмент кода для страницы B
/***/ "./b.js":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
console.log('bx');
__webpack_require__("./b.less");
__webpack_require__("./common.js");
__webpack_require__("./node_modules/_lodash@4.17.4@lodash/lodash.js");
/***/ }),
Действительно, идентификатор модуля заменяется путем к модулю. Но проблема, которую следует избегать, заключается в том, что размер стал больше, потому что количество символов в id-номере и пути не одного порядка.На примере поставщика размер до и после схемы приложения возрос.16KB
.或许有同学已经想到,那我对路径做次 hash 然后取几位不就得了,是的没错,webpack 官方就是这么做的。NamedModulesPlugin
Подходит для среды разработки, но в производственной среде используйтеHashedModuleIdsPlugin.
Итак, в производстве, чтобы получить наилучший размер, нам нужно изменить конфигурацию сборки.
...
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
"vendor": [ "lodash" ],
},
...
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
- new webpack.NamedModulesPlugin(),
+ new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
],
...
Hash: 80871a9833e531391384
Version: webpack 3.8.1
Time: 1230ms
Asset Size Chunks Chunk Names
vendor-2e968166c755a7385f9b.js 524 kB 0 [emitted] [big] vendor
pageB-68be4dda51b5b08538f2.js 595 bytes 1 [emitted] pageB
pageA-a70b7fa4d67cb16cb1f7.js 461 bytes 2 [emitted] pageA
runtime-6897b6cc7d074a5b2039.js 5.88 kB 3 [emitted] runtime
pageA-f83ea969c4ec627cb92bea42f12b75d6.css 91 bytes 2 [emitted] pageA
pageB-0651d43f16a9b34b4b38459143ac5dd8.css 150 bytes 1 [emitted] pageB
Замена NamedModulesPlugin на HashedModuleIdsPlugin в производственной среде достигла приемлемого диапазона с точки зрения увеличения размера пакета.В качестве примера вендора он увеличился всего на 1 КБ.
В этот момент я думал, что все кончено, пока я не изменил среду выполнения и не обнаружил, что постоянный кеш, похоже, может продолжать копать глубже.
$ diff --git a/dist/runtime-85b317d7b21588411828.js b/dist/runtime-dc322ed118963cd2e12a.js
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
-/******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"0":"a5620db988a639410257","1":"42b894ca482a061570ae","2":"bedb93c1db950da4fea1"}[chunkId] + ".js";
+/******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"0":"a5620db988a639410257","1":"42b894ca482a061570ae","2":"b7d7de62392f41af1f78"}[chunkId] + ".js";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
Мы обнаружили, что при изменении содержимого фрагмента записи, когда три элемента входа не изменились, изменение соответствующего сценария среды выполнения включает только изменение идентификатора фрагмента. Основываясь на опыте идентификатора модуля, естественно подумать, существует ли соответствующий уникальный контент для замены существующего идентификатора фрагмента, потому что числовой идентификатор фрагмента всегда имеет неопределенность.
Итак, пока проблема снова распадается на два предложения:
- Найдите способ заменить существующий идентификатор чанка, чтобы выразить уникальность
- Найдите возможность исправить id чанка
Нестабильность идентификатора чанка
Давайте посмотрим один за другим
Предложение 1: Найдите способ заменить существующий идентификатор чанка, чтобы выразить уникальность
Потому что мы знаем, что эта запись на самом деле уникальна в веб-пакете, а имя чанка записи происходит от нашей настройки имени записи. Таким образом, проблема здесь становится очень простой: нам нужно только указать идентификатор, соответствующий каждому фрагменту, на имя соответствующего фрагмента.
Предложение 2: Найдите возможность исправить идентификатор чанка
События, связанные с идентификатором модуля во время всего процесса выполнения веб-пакета,
before-chunk-ids
-> optimize-chunk-ids
-> after-optimize-chunk-ids
Так что нам просто нужноbefore-chunk-ids
Идентификатор чанка может быть исправлен в это время.
Поддельный код:
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("before-chunk-ids", (chunks) => {
chunks.forEach((chunk) => {
if(chunk.id === null) {
chunk.id = chunk.name;
}
});
});
});
}
очень простой.
В период webpack@2 автор внедрил реализацию этой части в официальный плагин, а именноNamedChunksPlugin
.
Итак, в общем случае нам нужно только добавить в конфигурацию сборкиNamedChunksPlugin
плагин.
...
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
"vendor": [ "lodash" ],
},
...
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
new webpack.NamedModulesPlugin(),
+ new webpack.NamedChunksPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
],
...
разница во время выполнения
/******/
/******/ // objects to store loaded and loading chunks
/******/ var installedChunks = {
-/******/ 3: 0
+/******/ "runtime": 0
/******/ };
/******/
/******/ // The require function
@@ -91,7 +91,7 @@
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
-/******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"0":"a5620db988a639410257","1":"42b894ca482a061570ae","2":"b7d7de62392f41af1f78"}[chunkId] + ".js";
+/******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"vendor":"45cd76029c7d91d6fc76","pageA":"0abd02f11fa4c29e99b3","pageB":"2b8c3672b02ff026db06"}[chunkId] + ".js";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
Вы можете видеть, что значение id, указывающее на уникальность чанка, заменено именем нашей записи. Превосходно! Вероятность того, что что-то пойдет не так, значительно снижается.
Еще одна причина для обсуждения этой проблемы заключается в том, что динамический импорт в webpack@2 или require.ensure в webpack@1 будет извлекать код для формирования независимого пакета.В webpack мы называем это поведением Code Splitting, как только код извлечен, 0. [hash].js 1.[hash].js в конечном итоге появится в результате сборки, более или менее все были обеспокоены этим.
Можно ожидать, что с помощью этого плагина мы сможем решить эту задачу лучше, с одной стороны, мы можем попытаться определить имена этих динамически загружаемых модулей, с другой стороны, мы также можем встретиться, предполагая, что сцена сборки будет генерировать несколько [chunk-id] ].[chunkhash].js, когда необходимо изменить фрагмент Code Splitting, например уменьшить единицу, вы не можете гарантировать, что он будет по-прежнему назначен [chunk-id] в предыдущем компиляция в новой компиляции, поэтому передайте способ, которым названо имя, кстати, решает эту проблему.
Просто здесь нужно быть немного правымNamedChunksPlugin
Внести некоторые изменения.
...
entry: {
"pageA": "./a.js",
"pageB": "./b.js",
"vendor": [ "lodash" ],
},
...
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
new webpack.NamedModulesPlugin(),
- new webpack.NamedChunksPlugin(),
+ new webpack.NamedChunksPlugin((chunk) => {
+ if (chunk.name) {
+ return chunk.name;
+ }
+ return chunk.mapModules(m => path.relative(m.context, m.request)).join("_");
+ }),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
],
...
Суммировать
Чтобы добиться постоянного кэширования, вам необходимо сделать следующее:
- Применить к файлам скриптов
[chunkhash]
Файловое приложение для извлечения приложения TextPlugin[contenthash]
; - использовать
CommonsChunkPlugin
Разумное извлечение публичных библиотекvendor
(включая библиотеки инструментов сообщества, такие как lodash), при необходимости вы также можете извлечь общедоступные бизнес-библиотеки.common
(бизнес-логика публичной части) и веб-пакетаruntime
; - Использование в среде разработки
NamedModulesPlugin
чтобы закрепить идентификатор модуля и использовать его в производственной средеHashedModuleIdsPlugin
чтобы закрепить идентификатор модуля - использовать
NamedChunksPlugin
Чтобы закрепить идентификатор фрагмента, разделенного во время выполнения и при использовании динамической загрузки. - Рекомендуется прочитать полный текст.