Spec-Zone.ru › webpack 4

Кэширование

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

Итак, мы используем webpack для сборки нашей модульной программы, что даёт нам каталог для развёртывания /dist. После того, как содержимое /dist будет развернуто на сервере, клиенты (обычно браузеры) обратятся к этому серверу для получения сайта и его ресурсов. Последний шаг может занять много времени, поэтому браузеры используют технику, называемую кэшированием. Это позволяет сайтам загружаться быстрее с меньшим количеством ненужных сетевых запросов. Однако это также может вызвать проблемы, когда требуется загрузка нового кода.

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

Имена выходных файлов

Мы можем использовать output.filename подстановки для определения имён выходных файлов. webpack предоставляет способ шаблонизации имён файлов с использованием скобочных строк, называемых подстановками. Подстановка [contenthash] добавит уникальный хеш, основанный на содержимом ресурса. Когда содержимое ресурса меняется, [contenthash] также изменится.

Давайте настроим наш проект, используя пример из начального руководства с plugins из управления выводом, чтобы нам не приходилось вручную поддерживать файл index.html.

project

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
|- /node_modules

webpack.config.js

  const path = require('path');
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      // new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
-       title: 'Output Management',
+       title: 'Caching',
      }),
    ],
    output: {
-     filename: 'bundle.js',
+     filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
    },
  };

Выполнение нашей команды сборки, npm run build, с этой настройкой должно дать следующий результат:

...
                       Asset       Size  Chunks                    Chunk Names
main.7e2c49a622975ebd9b7e.js     544 kB       0  [emitted]  [big]  main
                  index.html  197 bytes          [emitted]
...

Как вы можете видеть, имя пакета теперь отражает его содержимое (через хеш). Если мы запустим ещё одну сборку без внесения изменений, мы ожидаем, что имя файла останется неизменным. Однако, если мы запустим её снова, мы можем обнаружить, что это не так:

...
                       Asset       Size  Chunks                    Chunk Names
main.205199ab45963f6a62ec.js     544 kB       0  [emitted]  [big]  main
                  index.html  197 bytes          [emitted]
...

Это происходит потому, что webpack включает определённый шаблон, в частности, runtime и manifest, в пакет начального модуля.

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

Извлечение вспомогательного кода

Как мы узнали в разбиении кода, SplitChunksPlugin можно использовать для разделения модулей в отдельные пакеты. webpack предоставляет функцию оптимизации для разделения кода runtime в отдельный пакет с помощью параметра optimization.runtimeChunk. Установите его в значение single для создания одного пакета runtime для всех пакетов:

webpack.config.js

  const path = require('path');
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      // new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        title: 'Caching',
      }),
    ],
    output: {
      filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
    },
+   optimization: {
+     runtimeChunk: 'single',
+   },
  };

Давайте запустим ещё одну сборку, чтобы увидеть извлечённый runtime пакет:

Hash: 82c9c385607b2150fab2
Version: webpack 4.12.0
Time: 3027ms
                          Asset       Size  Chunks             Chunk Names
runtime.cc17ae2a94ec771e9221.js   1.42 KiB       0  [emitted]  runtime
   main.e81de2cf758ada72f306.js   69.5 KiB       1  [emitted]  main
                     index.html  275 bytes          [emitted]
[1] (webpack)/buildin/module.js 497 bytes {1} [built]
[2] (webpack)/buildin/global.js 489 bytes {1} [built]
[3] ./src/index.js 309 bytes {1} [built]
    + 1 hidden module

Также хорошей практикой является извлечение сторонних библиотек, таких как lodash или react, в отдельный vendor пакет, поскольку они менее склонны к изменениям, чем наш локальный исходный код. Этот шаг позволит клиентам запрашивать ещё меньше с сервера для поддержания актуальности. Это можно сделать, используя параметр cacheGroups плагина SplitChunksPlugin, продемонстрированного в примере 2 SplitChunksPlugin. Давайте добавим optimization.splitChunks с cacheGroups с последующими параметрами и выполним сборку:

webpack.config.js

  const path = require('path');
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      // new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        title: 'Caching',
      }),
    ],
    output: {
      filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
      runtimeChunk: 'single',
+     splitChunks: {
+       cacheGroups: {
+         vendor: {
+           test: /[\\/]node_modules[\\/]/,
+           name: 'vendors',
+           chunks: 'all',
+         },
+       },
+     },
    },
  };

Давайте запустим ещё одну сборку, чтобы увидеть наш новый vendor пакет:

...
                          Asset       Size  Chunks             Chunk Names
runtime.cc17ae2a94ec771e9221.js   1.42 KiB       0  [emitted]  runtime
vendors.a42c3ca0d742766d7a28.js   69.4 KiB       1  [emitted]  vendors
   main.abf44fedb7d11d4312d7.js  240 bytes       2  [emitted]  main
                     index.html  353 bytes          [emitted]
...

Теперь мы видим, что наш main пакет не содержит vendor код из каталога node_modules и уменьшился в размере до 240 bytes!

Идентификаторы модулей

Давайте добавим ещё один модуль, print.js, в наш проект:

project

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- print.js
|- /node_modules

print.js

+ export default function print(text) {
+   console.log(text);
+ };

src/index.js

  import _ from 'lodash';
+ import Print from './print';

  function component() {
    const element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+   element.onclick = Print.bind(null, 'Hello webpack!');

    return element;
  }

  document.body.appendChild(component());

Выполнение ещё одной сборки, мы ожидали, что изменится только хеш нашего main пакета, однако...

...
                           Asset       Size  Chunks                    Chunk Names
  runtime.1400d5af64fc1b7b3a45.js    5.85 kB      0  [emitted]         runtime
  vendor.a7561fb0e9a071baadb9.js     541 kB       1  [emitted]  [big]  vendor
    main.b746e3eb72875af2caa9.js    1.22 kB       2  [emitted]         main
                      index.html  352 bytes          [emitted]
...

... мы видим, что все три изменились. Это потому, что каждый module.id по умолчанию увеличивается в зависимости от порядка разрешения. Это означает, что при изменении порядка разрешения идентификаторы также изменятся. Итак, вкратце:

  • Пакет main изменился из-за нового содержимого.
  • Пакет vendor изменился, потому что его module.id был изменён.
  • И пакет runtime изменился, поскольку он теперь содержит ссылку на новый модуль.

Первый и последний пункты ожидаемы, именно хеш vendor мы хотим исправить. Давайте воспользуемся optimization.moduleIds с параметром 'hashed':

webpack.config.js

  const path = require('path');
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      // new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        title: 'Caching',
      }),
    ],
    output: {
      filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
+     moduleIds: 'hashed',
      runtimeChunk: 'single',
      splitChunks: {
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all',
          },
        },
      },
    },
  };

Теперь, несмотря на любые новые локальные зависимости, наш хеш vendor должен оставаться постоянным между сборками:

...
                          Asset       Size  Chunks             Chunk Names
   main.216e852f60c8829c2289.js  340 bytes       0  [emitted]  main
vendors.55e79e5927a639d21a1b.js   69.5 KiB       1  [emitted]  vendors
runtime.725a1a51ede5ae0cfde0.js   1.42 KiB       2  [emitted]  runtime
                     index.html  353 bytes          [emitted]
Entrypoint main = runtime.725a1a51ede5ae0cfde0.js vendors.55e79e5927a639d21a1b.js main.216e852f60c8829c2289.js
...

И давайте изменим наш src/index.js для временного удаления этой дополнительной зависимости:

src/index.js

  import _ from 'lodash';
- import Print from './print';
+ // import Print from './print';

  function component() {
    const element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
-   element.onclick = Print.bind(null, 'Hello webpack!');
+   // element.onclick = Print.bind(null, 'Hello webpack!');

    return element;
  }

  document.body.appendChild(component());

И, наконец, запустим нашу сборку ещё раз:

...
                          Asset       Size  Chunks             Chunk Names
   main.ad717f2466ce655fff5c.js  274 bytes       0  [emitted]  main
vendors.55e79e5927a639d21a1b.js   69.5 KiB       1  [emitted]  vendors
runtime.725a1a51ede5ae0cfde0.js   1.42 KiB       2  [emitted]  runtime
                     index.html  353 bytes          [emitted]
Entrypoint main = runtime.725a1a51ede5ae0cfde0.js vendors.55e79e5927a639d21a1b.js main.ad717f2466ce655fff5c.js
...

Мы можем видеть, что обе сборки дали значение 55e79e5927a639d21a1b в имени файла пакета vendor.

Заключение

Кэширование может быть сложным, но выгода для пользователей приложения или сайта делает это стоящим усилий. См. раздел Дополнительные материалы ниже, чтобы узнать больше.

Дополнительные материалы

  • Проблема 652

© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://v4.webpack.js.org/guides/caching

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API