моя контактная информация
Почтамезофия@protonmail.com
2024-07-08
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Помимо основного процесса, мы также подробно объясним несколько расплывчатых понятий:
спереди Инициализация, Создание, Печать"В " мы представили, что базовую логику построения Webpack можно грубо разделить на: "Инициализация, сборка, упаковка«три фазы:
в,"Построить«Этап отвечает за анализ зависимостей между модулями и установление Граф зависимостей(ModuleGraph); затем в "ИнкапсуляцияНа этапе графа зависимостей модули отдельно инкапсулируются в несколько объектов Chunk, а отношения родительско-дочерних зависимостей между Chunks сортируются в ChunkGraph и несколько объектов ChunkGroup.
Наиболее важной целью этапа «инкапсуляции» является построение графа отношений ChunkGraph на основе графа отношений ModuleGraph, собранного на этапе «сборки». Логика этого процесса относительно сложна:
Давайте кратко проанализируем логику реализации нескольких важных шагов.
Первый шаг очень важен: передачаseal()
После функции проходимentry
Конфигурация, создайте пустую для каждой записиChunk
иВходная точка объект (специальныйChunkGroup
) и первоначально настроил основные ChunkGraph
Структурные взаимоотношения, подготовка к следующему шагу, ключевой код:
class Compilation {
seal(callback) {
// ...
const chunkGraphInit = new Map();
// 遍历入口模块列表
for (const [name, { dependencies, includeDependencies, options }] of this
.entries) {
// 为每一个 entry 创建对应的 Chunk 对象
const chunk = this.addChunk(name);
// 为每一个 entry 创建对应的 ChunkGroup 对象
const entrypoint = new Entrypoint(options);
// 关联 Chunk 与 ChunkGroup
connectChunkGroupAndChunk(entrypoint, chunk);
// 遍历 entry Dependency 列表
for (const dep of [...this.globalEntry.dependencies, ...dependencies]) {
// 为每一个 EntryPoint 关联入口依赖对象,以便下一步从入口依赖开始遍历其它模块
entrypoint.addOrigin(null, { name }, /** @type {any} */ (dep).request);
const module = this.moduleGraph.getModule(dep);
if (module) {
// 在 ChunkGraph 中记录入口模块与 Chunk 关系
chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint);
// ...
}
}
}
// 调用 buildChunkGraph 方法,开始构建 ChunkGraph
buildChunkGraph(this, chunkGraphInit);
// 触发各种优化钩子
// ...
}
}
После завершения выполнения формируется следующая структура данных:
Во-вторых, если настроено в это время entry.runtime
, на этом этапе Webpack также предоставит код времени выполнения. создавать Соответствующий Чанк и непосредственнораспространять Даватьentry
соответствующийChunkGroup
объект.Звоню, когда все будет готовоbuildChunkGraph функцию, перейдите к следующему шагу.
Шаг второй: существоватьbuildChunkGraph
внутри функциипередача visitModules
Функция, просмотрите ModuleGraph и назначьте все модули различным модулям в соответствии с их зависимостями.Chunk
Объект, если он обнаружен во время этого процесса;асинхронный модуль, то модуль создаватьновый ChunkGroup
иChunk
Object, в конечном итоге образуя следующую структуру данных:
третий шаг: существоватьbuildChunkGraph
в функциипередача connectChunkGroups
метод, построитьChunkGroup
между,Chunk
зависимости между собой для создания полногоChunkGraph
Object, в конечном итоге образуя следующую структуру данных:
четвертый шаг: существоватьbuildChunkGraph
в функциипередача cleanupUnconnectedGroups
Метод, очистка недействительнаChunkGroup
, в основном играет роль в оптимизации производительности.
Пройдя эти четыре шага сверху вниз,ModuleGraph
Модули, хранящиеся в, будут назначены трем различным объектам Chunk: Entry, Async и Runtime в соответствии с характером самого модуля, а зависимости между чанками будут храниться в коллекциях ChunkGraph и ChunkGroup. Вы можете продолжить работу на основе этих объектов. в будущем изменить политику субподряда (например.SplitChunksPlugin
), реализовать оптимизацию субподряда путем реорганизации и распределения прав собственности на объекты Module и Chunk.
Описанный выше процесс построения включает в себя три ключевых объекта: Chunk, ChunkGroup и ChunkGraph. Давайте сначала суммируем их концепции и функции, чтобы углубить наше понимание:
Chunk
: Модуль используется для чтения содержимого модуля, записи межмодульных зависимостей и т. д., в то время как Chunk объединяет несколько Модулей на основе зависимостей модулей и выводит их в файлы ресурсов (логика объединения и вывода продуктов будет объяснена в следующей главе):ChunkGroup
:один ChunkGroup
содержит один или несколькоChunk
объект;ChunkGroup
иChunkGroup
Отношения зависимости между родителями и детьми формируются между:ChunkGraph
: Наконец, Webpack сохранит зависимости между чанками и группами чанк в compilation.chunkGraph
В объекте формируются следующие отношения типов:Выше ChunkGraph
В конечном итоге процесс сборки разделит модуль на три разных типа блоков:
entry
Модули, доступные при нижнем касании, организованы в блок;entry.runtime
Если он не пуст, модуль времени выполнения будет организован в чанк.Это встроено в Webpack, если не используется. splitChunks
В случае других плагинов правила по умолчанию для сопоставления ввода и вывода модуля являются одним из ключевых основополагающих принципов Webpack, поэтому необходимо ввести конкретные правила для каждого чанка.
Входной фрагмент:
Начиная с Entry Chunk, Webpack сначала entry
создаватьChunk
Объект, например, следующей конфигурации:
module.exports = {
entry: {
main: "./src/main",
home: "./src/home",
}
};
Траверс entry
свойства объекта и создатьchunk[main]
、chunk[home]
Два объекта, в настоящее время два куска соответственно содержатmain
、home
Модуль:
После завершения инициализации Webpack будет ModuleGraph
данные зависимости, будутentry
Все модули, затрагиваемые следующим, помещаются в чанк (происходит впосетитеМодули метод), например, для следующих зависимостей файлов:
main.js
Если четыре файла a/b/c/d прямо или косвенно ссылаются синхронно, Webpack сначалаmain.js
Модуль создает объекты Chunk и EntryPoint, а затем постепенно добавляет модули a/b/c/d вchunk[main]
, окончательно образуя:
Асинхронный фрагмент:
Во-вторых, Webpack скомпилирует каждый оператор асинхронного импорта (import(xxx)
иrequire.ensure
) обрабатывается как отдельный объект Chunk, и все его подмодули добавляются в этот Chunk — мы называем его Async Chunk. Например, для следующего примера:
// index.js
import './sync-a.js'
import './sync-b.js'
import('./async-a.js')
// async-a.js
import './sync-c.js'
Во входном модуле index.js
В sync-a и sync-b вводятся синхронно; в то же время в async-a модуль async-a вводится синхронно.sync-c
модуль, образуя следующую диаграмму зависимости модуля:
На этом этапе Webpack будет точкой входа. index.js
, асинхронный модуль async-a.js
Создайте подпакеты отдельно, чтобы сформировать следующую структуру чанка:
и chunk[index]
иchunk[async-a]
Между ними образуется односторонняя зависимость, и Webpack сохранит эту зависимость вChunkGroup._parents
、ChunkGroup._children
в свойствах.
Фрагмент времени выполнения:
Наконец, кроме entry
Помимо асинхронных модулей, Webpack5 также поддерживает извлечение кода времени выполнения отдельно в чанки. Упомянутый здесь код среды выполнения относится к серии базовых кодов платформы, внедренных Webpack для обеспечения нормальной работы упакованного продукта. Например, общая структура упакованного продукта Webpack выглядит следующим образом:
Большая часть кода, обведенная красным прямоугольником на рисунке выше, представляет собой код времени выполнения, динамически создаваемый Webpack. При компиляции Webpack решает, какой код времени выполнения выводить на основе бизнес-кода (на основе).Dependency
подкласс), например:
__webpack_require__.f
、__webpack_require__.r
и другие функции для достижения минимальной модульной поддержки;__webpack_require__.e
функция;__webpack_require__.o
функция;Хотя каждый фрагмент кода времени выполнения может быть небольшим, по мере увеличения возможностей конечный результат будет становиться все больше и больше. Особенно для приложений с несколькими входами, несколько раз упаковывать одинаковую среду выполнения для каждой записи — немного расточительно. entry.runtime
Элементы конфигурации используются для объявления того, как упаковывается код среды выполнения.Для использования просто используйтеentry
Добавить строковую форму к элементуruntime
значение, например:
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
}
};
существовать compilation.seal
В функции сначала Webpackentry
создаватьEntryPoint
, тогда суди entry
Содержит ли конфигурацияruntime
свойства, если они есть, создайте их с помощьюruntime
Значением является имя чанка. Следовательно, приведенный выше пример конфигурации будет генерировать два чанка:chunk[index.js]
、chunk[solid-runtime]
, и на основании этого окончательно выводятся два файла:
index.js
документ;solid-runtime.js
документ.во многих entry
В сценарии только для каждогоentry
Все установлено одинаковоruntime
Если значение, код времени выполнения Webpack будет объединен и записан в один и тот же фрагмент времени выполнения, что в конечном итоге приведет к оптимизации производительности продукта. Например, для следующей конфигурации:
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
home: { import: "./src/home", runtime: "solid-runtime" },
}
};
Вход index
、home
поделитесь тем жеruntime
value и, наконец, сгенерируйте три чанка соответственно:
Вход в это время chunk[index]
、chunk[home]
со временем выполненияchunk[solid-runtime]
Также будут сформированы отношения зависимости между родителями и детьми.
Самая большая проблема с правилами субупаковки по умолчанию заключается в том, что они не могут решить проблему дублирования модулей. Если несколько чанк одновременно содержат один и тот же модуль, то этот модуль будет повторно упаковываться в эти чанки без ограничений. Например, предположим, что у нас есть две записи main/index, которые зависят от одного и того же модуля:
По умолчанию Webpack не будет выполнять дополнительную обработку, а просто одновременно упаковывает модуль c в фрагменты main/index, в конечном итоге формируя:
можно увидеть chunk
изолированы друг от друга, модуль c упаковывается повторно, что может привести к ненужной потере производительности конечного продукта!
Чтобы решить эту проблему, в Webpack 3 появился CommonChunkPlugin
Плагин пытается извлечь общие зависимости между записями в отдельныеchunk
,но CommonChunkPlugin
По сути, он реализован на основе простой цепочки отношений родитель-потомок между чанками. Трудно сделать вывод, что извлеченный третий пакет следует использовать в качестве.entry
отецchunk
Еще ребенокchunk
,CommonChunkPlugin
унифицированная обработка в качестве родительскогоchunk
, в некоторых случаях это оказывает существенное негативное влияние на производительность.
По этой причине после Webpack 4 были специально введены более сложные структуры данных. ChunkGroup
Специализируется на реализации управления цепочкой взаимоотношений и сотрудничества.SplitChunksPlugin
может быть реализовано более эффективно и разумноЭвристический субподряд.
Таким образом, этап «Сборка» отвечает за построение ModuleGraph на основе ссылочных отношений модуля; этап «Инкапсуляция» отвечает за построение серии объектов Chunk на основе ModuleGraph и организацию зависимостей между чанками (асинхронные ссылки, среда выполнения). ) в ChunkGraph — объект графа зависимостей фрагментов. Подобно ModuleGraph, введение структуры ChunkGraph также может отделить логику управления зависимостями между чанками, что делает общую логику архитектуры более разумной и легкой для расширения.
Однако, хотя это и выглядит очень сложно, самой важной целью этапа «упаковки» по-прежнему является определение того, сколько имеется чанков и какие модули включены в каждый чанк — это ключевые факторы, которые действительно влияют на конечный результат упаковки.
Для этого нам нужно понять три встроенных правила суб-упаковки Webpack5: Entry Chunk, Async Chunk и Runtime Chunk. Это самая оригинальная логика суб-упаковки. Другие плагины. splitChunksPlugin) все основано на этом, с помощью buildChunkGraph
Различные хуки, срабатывающие позже, дополнительно разделяли, объединяли и оптимизировали структуру чанка для достижения расширенного эффекта субподряда.
думать Chunk
Будет ли создан только один файл продукта? Почему?mini-css-extract-plugin
、file-loader
Как реализован этот тип компонента, который может записывать дополнительные файлы?