Spec-Zone.ru › webpack 5

SplitChunksPlugin

Изначально, модули (и импортированные внутри них модули) были связаны родительско-детскими отношениями во внутренней графе webpack. CommonsChunkPlugin использовалось для предотвращения дублирования зависимостей между ними, но дальнейшая оптимизация была невозможна.

Начиная с webpack v4, CommonsChunkPlugin было удалено в пользу optimization.splitChunks.

Значения по умолчанию

Из коробки SplitChunksPlugin должно хорошо работать для большинства пользователей.

По умолчанию, оно влияет только на загружаемые по запросу чанки, потому что изменение начальных чанков повлияет на тег script, которые должен включать HTML-файл для запуска проекта.

Webpack автоматически разделит чанки на основе следующих условий:

  • Новый чанк может быть общим ИЛИ модули взяты из папки node_modules
  • Новый чанк будет больше 20 КБ (до минимизации + gzip)
  • Максимальное количество параллельных запросов при загрузке чанков по требованию будет меньше или равно 30
  • Максимальное количество параллельных запросов при начальной загрузке страницы будет меньше или равно 30

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

Конфигурация

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

предупреждение

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

optimization.splitChunks

Этот объект конфигурации представляет поведение по умолчанию SplitChunksPlugin.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
предупреждение

При обработке путей файлов webpack, они всегда содержат / на Unix-системах и \ на системах Windows. Поэтому использование [\\/] в полях {cacheGroup}.test необходимо для представления разделителя путей. / или \ в {cacheGroup}.test вызовет проблемы при кроссплатформенном использовании.

предупреждение

Начиная с webpack 5, передача имени входа в {cacheGroup}.test и использование имени существующего чанка для {cacheGroup}.name больше не допускается.

splitChunks.automaticNameDelimiter

string = '~'

По умолчанию webpack будет генерировать имена, используя исходник и имя чанка (например, vendors~main.js). Эта опция позволяет указать разделитель, используемый для сгенерированных имён.

splitChunks.chunks

string = 'async' function (chunk) RegExp

Это указывает, какие чанки будут выбраны для оптимизации. Когда передаётся строка, допустимые значения — all, async, и initial. Передача all может быть особенно мощной, потому что это означает, что чанки могут быть общими даже между асинхронными и не асинхронными чанками.

Обратите внимание, что это также применяется к группе кэширования по умолчанию (splitChunks.fallbackCacheGroup.chunks).

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // include all types of chunks
      chunks: 'all',
    },
  },
};

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

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks(chunk) {
        // exclude `my-excluded-chunk`
        return chunk.name !== 'my-excluded-chunk';
      },
    },
  },
};

Если вы используете webpack версии 5.86.0 или новее, вы также можете передать регулярное выражение:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: /foo/,
    },
  },
};
подсказка

Вы можете объединить эту конфигурацию с HtmlWebpackPlugin для одностраничных приложений или ChunksWebpackPlugin для многостраничных приложений. Это позволит автоматически встраивать сгенерированные vendor чанки.

splitChunks.maxAsyncRequests

number = 30

Максимальное количество параллельных запросов при загрузке по требованию.

splitChunks.maxInitialRequests

number = 30

Максимальное количество параллельных запросов при входной точке.

splitChunks.defaultSizeTypes

[string] = ['javascript', 'unknown']

Задает типы размеров, которые используются при использовании чисел для размеров.

splitChunks.minChunks

number = 1

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

splitChunks.hidePathInfo

boolean

Предотвращает раскрытие информации о пути при создании имён для частей, разделенных по maxSize.

splitChunks.minSize

number = 20000 { [index: string]: number }

Минимальный размер в байтах для генерации чанка.

splitChunks.minSizeReduction

number { [index: string]: number }

Минимальное уменьшение размера основного чанка (пакета) в байтах, необходимое для генерации чанка. Это означает, что если разделение в чанк не уменьшает размер основного чанка (пакета) на заданную величину байтов, он не будет разделен, даже если он удовлетворяет splitChunks.minSize значению.

подсказка

Для генерации чанка должны быть выполнены оба splitChunks.minSizeReduction и splitChunks.minSize условия.

splitChunks.enforceSizeThreshold

splitChunks.cacheGroups.{cacheGroup}.enforceSizeThreshold

number = 50000

Пороговое значение размера, при котором разделение применяется, и другие ограничения (minRemainingSize, maxAsyncRequests, maxInitialRequests) игнорируются.

splitChunks.minRemainingSize

splitChunks.cacheGroups.{cacheGroup}.minRemainingSize

number = 0

Опция splitChunks.minRemainingSize была введена в webpack 5, чтобы избежать модулей нулевого размера, гарантируя, что минимальный размер чанка, оставшегося после разделения, превышает предел. По умолчанию равно 0 в режиме 'development' mode. В других случаях splitChunks.minRemainingSize по умолчанию соответствует значению splitChunks.minSize, поэтому его не нужно указывать вручную, за исключением редких случаев, когда требуется глубокий контроль.

предупреждение

splitChunks.minRemainingSize применяется только когда остается только один чанк.

splitChunks.layer

splitChunks.cacheGroups.{cacheGroup}.layer

RegExp string function

Назначение модулей в группу кэша по слою модуля.

splitChunks.maxSize

number = 0

Использование maxSize (либо глобально optimization.splitChunks.maxSize, по группам кэша optimization.splitChunks.cacheGroups[x].maxSize, или для группы кэша по умолчанию optimization.splitChunks.fallbackCacheGroup.maxSize) сообщает webpack о попытке разделить чанки, превышающие maxSize байт, на более мелкие части. Части будут иметь размер не менее minSize (рядом с maxSize) байтов. Алгоритм детерминирован, и изменения в модулях будут иметь только локальные последствия. Поэтому он пригоден для использования с кэшированием длительного срока и не требует записей. maxSize является лишь подсказкой и может быть нарушен, когда модули больше maxSize или разделение нарушает minSize.

Если чанк уже имеет имя, каждая часть получит новое имя, производное от этого имени. В зависимости от значения optimization.splitChunks.hidePathInfo будет добавлена ключевая информация, полученная из первого имени модуля или хэша от него.

Опция maxSize предназначена для использования с HTTP/2 и кэшированием длительного срока. Она увеличивает количество запросов для лучшего кэширования. Она также может быть использована для уменьшения размера файла для более быстрого перестроения.

подсказка

maxInitialRequest/maxAsyncRequests имеет больший приоритет, чем maxSize. Фактический приоритет maxSize < maxInitialRequest/maxAsyncRequests < minSize.

подсказка

Установка значения для maxSize устанавливает значение как для maxAsyncSize, так и для maxInitialSize.

splitChunks.maxAsyncSize

number

Как и maxSize, maxAsyncSize может применяться глобально (splitChunks.maxAsyncSize), к группам кэша (splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize) или к группе кэша по умолчанию (splitChunks.fallbackCacheGroup.maxAsyncSize).

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

splitChunks.maxInitialSize

number

Как и maxSize, maxInitialSize может применяться глобально (splitChunks.maxInitialSize), к группам кэша (splitChunks.cacheGroups.{cacheGroup}.maxInitialSize) или к группе кэша по умолчанию (splitChunks.fallbackCacheGroup.maxInitialSize).

Разница между maxInitialSize и maxSize заключается в том, что maxInitialSize будет влиять только на чанки начальной загрузки.

splitChunks.name

boolean = false function (module, chunks, cacheGroupKey) => string string

Также доступно для каждой группы кэша: splitChunks.cacheGroups.{cacheGroup}.name.

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

Передача строки или функции позволяет использовать пользовательское имя. Указание строки или функции, которая всегда возвращает одну и ту же строку, объединит все общие модули и vendor модули в один чанк. Это может привести к большим начальным загрузкам и замедлению загрузки страницы.

Если вы выберете указание функции, вы можете найти свойство chunk.name (где chunk — элемент массива chunks) особенно полезным при выборе имени для вашего чанка.

Если splitChunks.name совпадает с именем точки входа, чанк точки входа и группа кэша будут объединены в один чанк.

подсказка

splitChunks.cacheGroups.{cacheGroup}.name может использоваться для перемещения модулей в чанк, который является родителем исходного чанка. Например, используйте name: "entry-name" для перемещения модулей в чанк entry-name. Вы также можете использовать чанки с именами по требованию, но вы должны быть внимательны, чтобы выбранные модули использовались только в этом чанке.

main.js

import _ from 'lodash';

console.log(_.join(['Hello', 'webpack'], ' '));

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          // cacheGroupKey here is `commons` as the key of the cacheGroup
          name(module, chunks, cacheGroupKey) {
            const moduleFileName = module
              .identifier()
              .split('/')
              .reduceRight((item) => item);
            const allChunksNames = chunks.map((item) => item.name).join('~');
            return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
          },
          chunks: 'all',
        },
      },
    },
  },
};

Запуск webpack с конфигурацией splitChunks также выведет чанк группы common с именем commons-main-lodash.js.e7519d2bb8777058fa27.js (хэш указан как пример реального выходного значения).

предупреждение

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

splitChunks.usedExports

splitChunks.cacheGroups{cacheGroup}.usedExports

boolean = true

Определение используемых модулей для приведения имен экспорта, исключения неиспользуемых экспортов и генерации более эффективного кода. Если оно true: анализирует используемые экспорты для каждого выполнения, если оно "global": анализирует экспорты глобально для всех выполнений вместе.

splitChunks.cacheGroups

Группы кэша могут наследовать и/или переопределять любые параметры из splitChunks.*; но test, priority и reuseExistingChunk могут быть сконфигурированы только на уровне группы кэша. Чтобы отключить любые группы кэша по умолчанию, установите их в false.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.priority

number = -20

Модуль может принадлежать нескольким группам кэша. Оптимизация отдаст предпочтение группе кэша с более высоким priority. У групп по умолчанию отрицательный приоритет, чтобы позволить пользовательским группам иметь более высокий приоритет (значение по умолчанию для пользовательских групп 0).

splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk

boolean = true

Если текущий фрагмент содержит модули, уже разделенные из основного пакета, он будет повторно использован вместо создания нового. Это может повлиять на имя результирующего файла фрагмента.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          reuseExistingChunk: true,
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.type

function RegExp string

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

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        json: {
          type: 'json',
        },
      },
    },
  },
};

splitChunks.cacheGroups.test

splitChunks.cacheGroups.{cacheGroup}.test

function (module, { chunkGraph, moduleGraph }) => boolean RegExp string

Управляет выбором модулей этой группой кэша. Пропуская этот параметр, выбираются все модули. Он может соответствовать абсолютному пути к ресурсу модуля или именам фрагментов. При соответствии имени фрагмента, выбираются все модули в фрагменте.

Предоставление функции для{cacheGroup}.test:

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        svgGroup: {
          test(module) {
            // `module.resource` contains the absolute path of the file on disk.
            // Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
            const path = require('path');
            return (
              module.resource &&
              module.resource.endsWith('.svg') &&
              module.resource.includes(`${path.sep}cacheable_svgs${path.sep}`)
            );
          },
        },
        byModuleTypeGroup: {
          test(module) {
            return module.type === 'javascript/auto';
          },
        },
      },
    },
  },
};

Для просмотра доступной информации в объектах module и chunks, можно поместить инструкцию debugger; в коллбэк. Затем запустите сборку webpack в отладочном режиме для проверки параметров в Chromium DevTools.

Предоставление RegExp к {cacheGroup}.test:

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          // Note the usage of `[\\/]` as a path separator for cross-platform compatibility.
          test: /[\\/]node_modules[\\/]|vendor[\\/]analytics_provider|vendor[\\/]other_lib/,
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.filename

string function (pathData, assetInfo) => string

Позволяет переопределить имя файла только тогда, когда это начальный фрагмент. Все доступные плейсхолдеры в output.filename также доступны здесь.

предупреждение

Этот параметр также может быть задан глобально в splitChunks.filename, но это не рекомендуется и, вероятно, приведёт к ошибке, если splitChunks.chunks не задано значение 'initial'. Избегайте глобальной установки.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: '[name].bundle.js',
        },
      },
    },
  },
};

И как функция:

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: (pathData) => {
            // Use pathData object for generating filename string based on your requirements
            return `${pathData.chunk.name}-bundle.js`;
          },
        },
      },
    },
  },
};

Можно создать структуру папок, предоставив префикс пути к имени файла: 'js/vendor/bundle.js'.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: 'js/[name]/bundle.js',
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.enforce

boolean = false

Указывает webpack игнорировать параметры splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests и splitChunks.maxInitialRequests и всегда создавать фрагменты для этой группы кэша.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          enforce: true,
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.idHint

string

Устанавливает подсказку для идентификатора фрагмента. Она будет добавлена к имени файла фрагмента.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          idHint: 'vendors',
        },
      },
    },
  },
};

Примеры

По умолчанию: Пример 1

// index.js

import('./a'); // dynamic import
// a.js
import 'react';

//...

Результат: Будет создан отдельный фрагмент, содержащий react. При вызове импорта этот фрагмент загружается параллельно с исходным фрагментом, содержащим ./a.

Почему:

  • Условие 1: Фрагмент содержит модули из node_modules
  • Условие 2: react больше, чем 30 Кб
  • Условие 3: Количество параллельных запросов при вызове импорта равно 2
  • Условие 4: Не влияет на запросы при начальной загрузке страницы

Почему так? react вероятно, будет меняться реже, чем код вашего приложения. Переместив его в отдельный фрагмент, этот фрагмент может быть кэширован отдельно от кода вашего приложения (предполагая, что вы используете chunkhash, записи, Cache-Control или другой подход для долговременного кэширования).

По умолчанию: Пример 2

// entry.js

// dynamic imports
import('./a');
import('./b');
// a.js
import './helpers'; // helpers is 40kb in size

//...
// b.js
import './helpers';
import './more-helpers'; // more-helpers is also 40kb in size

//...

Результат: Будет создан отдельный фрагмент, содержащий ./helpers и все его зависимости. При вызове импорта этот фрагмент загружается параллельно с исходными фрагментами.

Почему:

  • Условие 1: Фрагмент используется в обоих вызовах импорта
  • Условие 2: helpers больше, чем 30 Кб
  • Условие 3: Количество параллельных запросов при вызовах импорта равно 2
  • Условие 4: Не влияет на запросы при начальной загрузке страницы

Помещение содержимого helpers в каждый фрагмент приведёт к его коду загрузится дважды. Используя отдельный фрагмент, это произойдёт только один раз. Мы платим за дополнительный запрос, что можно считать компромиссом. Поэтому есть минимальный размер в 30 Кб.

Разделение фрагментов: Пример 1

Создать фрагмент commons, который включает весь код, общий для точек входа.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2,
        },
      },
    },
  },
};
предупреждение

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

Разделение фрагментов: Пример 2

Создать фрагмент vendors, который включает весь код из node_modules во всем приложении.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  },
};
предупреждение

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

Разделение фрагментов: Пример 3

Создать фрагмент custom vendor, который содержит определённые node_modules пакеты, соответствующие RegExp.

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
      },
    },
  },
};
подсказка

Это приведёт к разделению react и react-dom в отдельный фрагмент. Если вы не уверены, какие пакеты были включены в фрагмент, вы можете обратиться к разделу Анализ пакетов для получения подробностей.

© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://webpack.js.org/plugins/split-chunks-plugin

Spec-Zone.ru

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