Схема постоянного кэширования на основе веб-пакета

внешний интерфейс JavaScript CSS Webpack

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

Как разработчик, самая большая привлекательность заключается в том, что он может быть стабильным после создания без изменения внутреннего содержания записи.

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'
  }),
],
...

Суммировать

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

  1. Применить к файлам скриптов[chunkhash]Файловое приложение для извлечения приложения TextPlugin[contenthash];
  2. использоватьCommonsChunkPluginРазумное извлечение публичных библиотекvendor(включая библиотеки инструментов сообщества, такие как lodash), при необходимости вы также можете извлечь общедоступные бизнес-библиотеки.common(бизнес-логика публичной части) и веб-пакетаruntime;
  3. Использование в среде разработкиNamedModulesPluginчтобы закрепить идентификатор модуля и использовать его в производственной средеHashedModuleIdsPluginчтобы закрепить идентификатор модуля
  4. использоватьNamedChunksPluginЧтобы закрепить идентификатор фрагмента, разделенного во время выполнения и при использовании динамической загрузки.
  5. Рекомендуется прочитать полный текст.