задний план
Все друзья фронтенда знают, что для уменьшения размера пакета зависимые интерфейсные модули часто упаковываются независимо друг от друга, напримерvue
,vue-router
попасть в отдельный пакетvendor
середина. Кроме того, каждая страница сложной страницы с несколькими маршрутами часто упаковывается отдельно, и только при доступе к странице загружается js-пакет страницы для ускорения рендеринга домашней страницы.
Будь тоreact
все ещеvue
Все они предоставляют полные инструменты, помогающие нам избежать утомительной работы по настройке. Когда мы создаем код, разделение кода выполняется автоматически.
Поэтому многие друзья не знают, что происходит за кадром. Что касается того, почему он так разделен и как управлять разделением кода, это еще более запутанно.
Вопрос викторины
Прежде чем мы начнем, давайте рассмотрим вопрос. Если вы уже знаете ответ на вопрос и понимаете, почему, вам не нужно читать дальше. Если вы не знаете ответа или знаете ответ, но не знаете причины. Что ж, настоятельно рекомендуется прочитать эту статью.
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: { app: "./src/index.js" },
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist")
},
optimization: {
splitChunks: {
chunks: "all"
}
},
plugins: [
new HtmlWebpackPlugin()
]
};
// index.js
import "vue"
import(/*webpackChunkName: 'a' */ "./a");
import(/*webpackChunkName: 'b' */ "./b");
// a.js
import "vue-router";
import "./someModule"; // 模块大小大于30kb
// b.js
import "vuex";
import "./someModule"; // 模块大小大于30kb
// someModule.js
// 该模块大小超过30kb
// ...
Три способа разделения кода
В webpack есть три распространенных метода разделения кода:
- точка входа: использовать
entry
Конфигурация вручную разделяет код. - Динамический импорт: разделение кода с помощью встроенных вызовов функций модулей.
- Чтобы предотвратить повторение: используйте
splitChunks
Дедупликация и разделение чанков. Первый способ очень простой, нужно толькоentry
Вы можете настроить несколько записей в:
entry: { app: "./index.js", app1: "./index1.js" }
Второй способ заключается в автоматическом использованииimport()
Загруженные модули разделены на отдельные пакеты:
//...
import("./a");
//...
Третий способ заключается в использованииsplitChunks
плагин, настроить правила разделения, затемwebpack
автоматически удовлетворяет правиламchunk
разделение. Все делается автоматически.
Первые два метода разделения просты для понимания. В этой статье в основном обсуждается третий метод.
Разделение кода splitChunks
splitChunks
распределение по умолчанию
splitChunks: {
// 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
chunks: "async",
// 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
minSize: 30000,
// 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
minChunks: 1,
// 表示按需加载文件时,并行请求的最大数目。默认为5。
maxAsyncRequests: 5,
// 表示加载入口文件时,并行请求的最大数目。默认为3。
maxInitialRequests: 3,
// 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
automaticNameDelimiter: '~',
// 设置chunk的文件名。默认为true。当为true时,splitChunks基于chunk和cacheGroups的key自动命名。
name: true,
// cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
//
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
Приведенная выше конфигурация резюмируется следующими четырьмя условиями:
- Модули повторно используются в коде или берутся из
node_modules
папка - Объем модуля больше или равен 30кб (до сжатия)
- При загрузке чанков по запросу максимальное количество параллельных запросов не может превышать 5
- При начальной загрузке страницы максимальное количество параллельных запросов не может превышать 3.
// index.js
import("./a");
// ...
// a.js
import "vue";
// ...
В приведенном выше коде результат сборки в конфигурации по умолчанию выглядит следующим образом:
Анализ причин:
-
index.js
В качестве входного файла он относится к случаю ручной настройки кода разделения в точке входа, поэтому он будет упакован независимо. (приложение.js) -
a.js
пройти черезimport()
Загрузка — это случай динамического импорта, поэтому пакет будет напечатан независимо. (1.js) -
vue
отnode_modules
директории и имеет размер более 30 КБ; переместите его изa.js
После демонтажа сa.js
Параллельная загрузка, количество запросов на параллельную загрузку равно 2, что не превышает 5 по умолчанию;vue
После разделения количество загружаемых параллельно входных файлов не увеличивается и не превышает установленного по умолчанию 3.vue
также встретитьсяsplitChunks
Условие разделения , отдельный пакет (2.js)
понимать куски
chunks
рассказатьsplitChunks
Объект действия , необязательные значения которогоasync
,initial
иall
. Значение по умолчаниюasync
, то есть по умолчанию для разделения кода выбираются только асинхронно загруженные чанки. Это мы проверили на примере в начале. Здесь мы используем два примера, чтобы увидеть, когда значение кусковinitial
иall
, каков результат упаковки.
Сначала измените значение кусков наinitial
:
chunks: "initial"
Результат сборки следующий:
Анализ причин:
когдаchunks
значениеinitial
час,splitChunks
Область действия становится начальным куском, который не загружается асинхронно, например, нашindex.js
Это кусок, который существует, когда он инициализирован. Модуль vue — это чанк, загружаемый асинхронно.a.js
Введен в, так что не будет изолирован.
chunks
все еще используетсяinitial
, Мыindex.js
иa.js
Небольшая модификация:
// index.js
import 'vue'
import('./a')
// a.js
console.log('a')
Результат сборки следующий:
Анализ причин:
vue
существуетindex.js
импортируется напрямую, в то время какindex.js
Это начальный кусок, поэтому он отделяется и попадаетvendors~app.js
середина.
ты можешь позволитьsplitChunks
Как насчет обработки как начальных фрагментов, так и асинхронных фрагментов? Ответ - да, вам просто нужноchunks
изменить наall
:
chunks: "all"
правильноindex.js
иa.js
Небольшая модификация:
// index.js
import 'vue-router'
import('./a')
// a.js
import 'vue'
console.log('a')
Результат сборки следующий:
Анализ причин:
chunks
значениеall
час,splitChunks
Область обработки включает в себя сценарии как начального, так и асинхронного фрагмента, поэтомуindex.js
серединаvue-router
был разделен наvendors~app.js
, а чанки загружаются асинхронноa.js
серединаvue
был разделен на3.js
середина. Рекомендуется в разработкеchunks
Установить какall
.
Понимание maxInitialRequests
maxIntialRequests
выражатьsplitChunks
После разделения чанков начальное количество чанков, которые необходимо запросить на странице, не превышает заданного значения. Так называемый начальный фрагмент относится к js, который необходимо загрузить в начале при рендеринге страницы, что отличается от js, который загружается асинхронно после загрузки страницы.
правильноsplitChunks
Внесите следующие изменения, другие используют конфигурацию по умолчанию:
chunks: 'initial',
maxInitialRequests: 1
Небольшая модификация index.js:
// index.js
import 'vue'
Результат сборки следующий:
Анализ причин:
так какmaxInitialRequests
равно 1, еслиvue
отindex.js
Если он удален из середины, вновь созданный фрагмент используется в качестве начального фрагмента.index.js
Предварительная зависимость страницы должна быть запрошена первой при инициализации страницы. Тогда количество запросов при инициализации становится равным 2, поэтому условие разделения не выполняется, поэтомуsplitChunks
без правindex.js
расколоть.
Понимание maxAsyncRequests
иmaxInitialRequests
относительно,maxAsyncRequests
выражатьsplitChunks
После разделения чанков количество асинхронных чанков, загружаемых параллельно, не превышает указанного значения.
правильноsplitChunks
Внесите следующие изменения, другие используют конфигурацию по умолчанию:
maxAsyncRequests: 1
правильноindex.js
Небольшая модификация:
// index.js
import('./a')
// a.js
import 'vue'
console.log('a')
Результат сборки следующий:
Анализ причин:так какmaxAsyncRequests
равно 1, так какa.js
черезimport()
Загружается асинхронно, количество параллельных асинхронных запросов в это время равно 1. еслиvue
отa.js
Если он распакован, распакованный пакет также станет фрагментом асинхронного запроса. В этом случае, когда асинхронный запросa.js
Когда количество параллельных запросов равно 2. Следовательно, условие расщепления не выполняется, поэтомуsplitChunks
без правa.js
расколоть.
Понимание minChunks
minChunks
Указывает, что модуль должен быть разделен по крайней мере указанным числом фрагментов, прежде чем его можно будет разделить. По умолчанию 1.
правильноsplitChunks
Внесите следующие изменения, другие используют конфигурацию по умолчанию:
chunks: 'all',
minChunks: 2
правильноindex.js
Небольшая модификация:
// index.js
import 'vue'
Результат сборки следующий:
Анализ причин:
так какminChunks
равно 2, поэтому только тогда, когдаvue
Он будет разделен только тогда, когда он будет разделен как минимум на 2 фрагмента.
мыслительные вопросы
Каков результат сборки следующего кода?
chunks: 'all',
minChunks: 2
// index.js
import 'vue'
import './a'
// a.js
import 'vue'
console.log('a')
понимать группы кеша
cacheGroups
наследоватьsplitChunks
Значения всех свойств в , таких какchunks
,minSize
,minChunks
,maxAsyncRequests
,maxInitialRequests
,automaticNameDelimiter
,name
, мы также можемcacheGroups
переназначить, перезаписатьsplitChunks
значение . Кроме того, есть некоторые свойства, которые можно использовать только вcacheGroups
используется в:test
,priority
,reuseExistingChunk
.
пройти черезcacheGroups
, мы можем определить пользовательские группы фрагментов с помощьюtest
Модули фильтра условий, и модули, удовлетворяющие условиям, относятся к одной группе.
cacheGroups
Есть две группы по умолчанию, однаvendors
, преобразует все изnode_modules
Модуль каталога;default
, который содержит модули, используемые более чем двумя чанками.
В предыдущем примере вы, возможно, заметили, насколько странными являются имена некоторых разделенных чанков, напримерvendors~app
(по умолчаниюcacheGroups
Ключ группы + имя исходного чанка). Давайте посмотрим, как настроить имя разделенного чанка.
Сначала найдите группу, к которой принадлежит чанк, этот примерvendors
Группа, внесите следующие изменения, другие используют конфигурацию по умолчанию:
chunks:'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "customName",
priority: -10
}
}
Небольшая модификация index.js:
// index.js
import 'vue'
Результат сборки следующий:
Анализ причин:
смотреть изnode_modules
каталог, назначенный по умолчаниюvendors
группа, если не указаноname
Если да, то будет использоваться имя чанка по умолчанию, здесь мы указалиname
, поэтому последний фрагмент называетсяcustomName
.
Модули также могут быть отнесены к нескольким различным группам, но в конечном итоге ониpriority
Приоритет определяет, в какой фрагмент упаковываться.
Добавьте новую группу:
chunks:'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "customName",
priority: -10
},
customGroup: {
test: /[\\/]node_modules[\\/]/,
name: "customName1",
priority: 0
}
}
Результат сборки:
Анализ причин:
Несмотря на то чтоvendors
иcustomGroup
Условия для обеих групп соблюдены, но поскольку последняя имеет более высокий приоритет, в конечном итоге она будетvue
в упаковкеcustomName1.js
середина.
Суммировать
Объяснение здесь, по-видимому, вы правыwebpack
Иметь четкое представление о том, как выполнять разделение кода. Не могли бы вы дать свой ответ на вопрос в начале статьи?