2024-07-08
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
En plus du processus principal, nous expliquerons également en détail plusieurs concepts vagues :
devant Init, Créer, Sceller"Dans ", nous avons introduit que la logique de construction sous-jacente de Webpack peut être grossièrement divisée en : "Initialisation, construction, packaging"trois phases :
dans,"Construction"La phase est chargée d'analyser les dépendances entre les modules et d'établir les Graphique de dépendance(ModuleGraph); puis, dans "Encapsulation", basée sur le graphe de dépendances, les modules sont encapsulés séparément dans plusieurs objets Chunk et les relations de dépendance parent-enfant entre les Chunks sont triées dans ChunkGraph et plusieurs objets ChunkGroup.
L'objectif le plus important de la phase « d'encapsulation » est de construire le graphe de relations ChunkGraph basé sur le graphe de relations ModuleGraph collecté lors de la phase de « construction ». La logique de ce processus est relativement complexe :
Analysons ici brièvement la logique de mise en œuvre de plusieurs étapes importantes.
La première étape est très critique : transfertseal()
Après la fonction, parcourezentry
Configuration, créez-en un vide pour chaque entréeChunk
etPoint d'accès objet (un objet spécialChunkGroup
), et configurez initialement la base ChunkGraph
Relation structurelle, préparez-vous à l'étape suivante, code clé :
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);
// 触发各种优化钩子
// ...
}
}
Une fois l’exécution terminée, la structure de données suivante est formée :
Deuxièmement, si configuré à ce moment entry.runtime
, Webpack fournira également le code d'exécution à ce stade créer Le Chunk correspondant et directementdistribuer Donnerentry
correspondantChunkGroup
objet.Appelé quand tout est prêtconstruireChunkGraph fonction, passez à l’étape suivante.
Deuxième étape : existerbuildChunkGraph
dans la fonctiontransfert visitModules
Fonction, parcourez le ModuleGraph et attribuez tous les modules à différents modules en fonction de leurs dépendances.Chunk
Objet ; s'il est rencontré au cours de ce processus.module asynchrone, puis le module créernouveau ChunkGroup
etChunk
Objet, formant finalement la structure de données suivante :
troisième étape: existerbuildChunkGraph
en fonctiontransfert connectChunkGroups
méthode, établirChunkGroup
entre,Chunk
dépendances entre elles pour générer un ensemble completChunkGraph
Objet, formant finalement la structure de données suivante :
la quatrième étape : existerbuildChunkGraph
en fonctiontransfert cleanupUnconnectedGroups
Méthode, le nettoyage n'est pas valideChunkGroup
, joue principalement un rôle dans l’optimisation des performances.
Après avoir parcouru ces quatre étapes de haut en bas,ModuleGraph
Les modules stockés dans seront attribués à trois objets Chunk différents : Entry, Async et Runtime selon la nature du module lui-même, et les dépendances entre Chunks seront stockées dans les collections ChunkGraph et ChunkGroup. Vous pouvez continuer en fonction de ces objets. Modifier ultérieurement les politiques de sous-traitance (par ex.SplitChunksPlugin
), réaliser une optimisation de la sous-traitance en réorganisant et en attribuant la propriété des objets Module et Chunk.
Le processus de construction ci-dessus implique trois objets clés : Chunk, ChunkGroup et ChunkGraph. Résumons d'abord leurs concepts et fonctions pour approfondir notre compréhension :
Chunk
: Le module est utilisé pour lire le contenu du module, enregistrer les dépendances inter-modules, etc. ; tandis que Chunk fusionne plusieurs modules en fonction des dépendances des modules et les génère dans des fichiers d'actifs (la logique de fusion et de sortie des produits sera expliquée dans le chapitre suivant) :ChunkGroup
:un ChunkGroup
contient un ou plusieursChunk
objet;ChunkGroup
etChunkGroup
Une relation de dépendance parent-enfant se noue entre :ChunkGraph
: Enfin, Webpack stockera les dépendances entre Chunks et ChunkGroups dans compilation.chunkGraph
Dans l'objet, les relations de type suivantes sont formées :Ci-dessus ChunkGraph
Le processus de construction organisera finalement le module en trois types différents de morceaux :
entry
Les modules atteints par la touche inférieure sont organisés en un Chunk ;entry.runtime
Lorsqu'il n'est pas vide, le module d'exécution sera organisé en un Chunk.Ceci est intégré à Webpack, s'il n'est pas utilisé splitChunks
Dans le cas d'autres plug-ins, les règles par défaut de mappage des entrées et sorties du module sont l'un des principes clés sous-jacents de Webpack, il est donc nécessaire d'introduire les règles spécifiques de chaque Chunk.
Partie d'entrée :
En commençant par le Entry Chunk, Webpack va d'abord entry
créerChunk
Objet, par exemple, la configuration suivante :
module.exports = {
entry: {
main: "./src/main",
home: "./src/home",
}
};
traverser entry
propriétés de l'objet et créerchunk[main]
、chunk[home]
Deux objets, à ce moment les deux Chunks contiennent respectivementmain
、home
Module:
Une fois l'initialisation terminée, Webpack ModuleGraph
les données de dépendance, serontentry
Tous les modules touchés par ce qui suit sont insérés dans le Chunk (se produisant dansvisiterModules méthode), par exemple, pour les dépendances de fichiers suivantes :
main.js
Si les quatre fichiers a/b/c/d sont référencés directement ou indirectement de manière synchrone, Webpack va d'abordmain.js
Le module crée des objets Chunk et EntryPoint, puis ajoute progressivement des modules a/b/c/d àchunk[main]
, formant finalement :
Morceau asynchrone :
Deuxièmement, Webpack compilera chaque instruction d'importation asynchrone (import(xxx)
etrequire.ensure
) est traité comme un objet Chunk distinct et tous ses sous-modules sont ajoutés à ce Chunk - nous l'appelons Async Chunk. Par exemple, pour l'exemple suivant :
// index.js
import './sync-a.js'
import './sync-b.js'
import('./async-a.js')
// async-a.js
import './sync-c.js'
Dans le module d'entrée index.js
Dans, sync-a et sync-b sont introduits de manière synchrone ; le module async-a est introduit de manière asynchrone, dans async-a, le module async-a est introduit de manière synchrone ;sync-c
module, formant le diagramme de dépendances de module suivant :
À ce stade, Webpack sera le point d'entrée index.js
, module asynchrone async-a.js
Créez des sous-packages séparément pour former la structure Chunk suivante :
et chunk[index]
etchunk[async-a]
Une dépendance à sens unique se forme entre eux, et Webpack enregistrera cette dépendance dansChunkGroup._parents
、ChunkGroup._children
dans les propriétés.
Bloc d'exécution :
Enfin, sauf entry
, En plus des modules asynchrones, Webpack5 prend également en charge l'extraction du code d'exécution séparément dans des morceaux. Le code d'exécution mentionné ici fait référence à une série de codes de structure de base injectés par Webpack pour garantir que le produit packagé peut fonctionner normalement. Par exemple, la structure courante du produit packagé Webpack est la suivante :
La grande section de code entourée dans la case rouge dans l'image ci-dessus est le code d'exécution généré dynamiquement par Webpack Lors de la compilation, Webpack décidera quel code d'exécution sortir en fonction du code métier (en fonction de).Dependency
sous-classe), par exemple :
__webpack_require__.f
、__webpack_require__.r
et d'autres fonctions pour obtenir un support modulaire minimum ;__webpack_require__.e
fonction;__webpack_require__.o
fonction;Bien que chaque morceau de code d'exécution puisse être petit, à mesure que les fonctionnalités augmentent, le résultat final deviendra de plus en plus grand. Surtout pour les applications à entrées multiples, il est un peu inutile de regrouper à plusieurs reprises un runtime similaire à chaque entrée. entry.runtime
Les éléments de configuration sont utilisés pour déclarer comment le code d'exécution est empaqueté.Pour l'utilisation, utilisez simplemententry
Ajouter une forme de chaîne à l'élémentruntime
valeur, par exemple :
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
}
};
exister compilation.seal
Dans la fonction, Webpack est d'abordentry
créerEntryPoint
, alors juge entry
La configuration contient-elleruntime
propriétés, s'il y en a, créez-les avecruntime
La valeur est le nom du Chunk. Par conséquent, l'exemple de configuration ci-dessus générera deux Chunks :chunk[index.js]
、chunk[solid-runtime]
, et sur cette base, deux fichiers sont finalement générés :
index.js
document;solid-runtime.js
document.dans de nombreux entry
Dans le scénario, juste pour chacunentry
Tous réglés de la même manièreruntime
valeur, le code d'exécution du Webpack sera fusionné et écrit dans le même Runtime Chunk, permettant ainsi d'optimiser les performances du produit. Par exemple, pour la configuration suivante :
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
home: { import: "./src/home", runtime: "solid-runtime" },
}
};
Entrée index
、home
partager la même choseruntime
valeur, et enfin générer trois Chunks, respectivement :
Entrée à cette heure chunk[index]
、chunk[home]
avec exécutionchunk[solid-runtime]
Une relation de dépendance parent-enfant va également se nouer.
Le plus gros problème avec les règles de sous-packaging par défaut est qu'elles ne peuvent pas résoudre la duplication de module. Si plusieurs fragments contiennent le même module en même temps, alors ce module sera regroupé à plusieurs reprises dans ces fragments sans restriction. Par exemple, supposons que nous ayons deux entrées main/index qui dépendent toutes deux du même module :
Par défaut, Webpack n'effectuera pas de traitement supplémentaire à ce sujet, mais emballera simplement le module c dans les deux morceaux principaux/index en même temps, formant finalement :
peut être vu chunk
sont isolés les uns des autres, le module c est emballé à plusieurs reprises, ce qui peut entraîner une perte de performances inutile pour le produit final !
Pour résoudre ce problème, Webpack 3 a introduit CommonChunkPlugin
Le plugin tente d'extraire les dépendances communes entre les entrées dans des fichiers séparés.chunk
,mais CommonChunkPlugin
Il est essentiellement implémenté sur la base de la simple chaîne de relation parent-enfant entre Chunks. Il est difficile de déduire que le troisième package extrait doit être utilisé comme.entry
pèrechunk
Encore un enfantchunk
,CommonChunkPlugin
traitement unifié en tant que parentchunk
, dans certains cas, cela a un impact négatif considérable sur les performances.
Pour cette raison, des structures de données plus complexes ont été spécifiquement introduites après Webpack 4—— ChunkGroup
Spécialisé dans la réalisation de la gestion de la chaîne relationnelle et de la coopérationSplitChunksPlugin
peut être mis en œuvre de manière plus efficace et intelligenteSous-traitance heuristique.
En résumé, la phase "Build" est chargée de construire le ModuleGraph en fonction de la relation de référence du module ; la phase "Encapsulation" est chargée de construire une série d'objets Chunk basés sur le ModuleGraph et d'organiser les dépendances entre les Chunks (références asynchrones, runtime ) dans ChunkGraph - Objet graphique Chunk Dependency. Semblable à ModuleGraph, l'introduction de la structure ChunkGraph peut également découpler la logique de gestion des dépendances entre les Chunks, rendant la logique globale de l'architecture plus raisonnable et plus facile à développer.
Cependant, même si cela semble très compliqué, l'objectif le plus important de l'étape "packaging" reste de déterminer le nombre de Chunks et quels modules sont inclus dans chaque Chunk - ce sont les facteurs clés qui affectent réellement le résultat final du packaging.
Pour ce point, nous devons comprendre les trois règles de sous-traitance intégrées à Webpack5 : Entry Chunk, Async Chunk et Runtime Chunk. Ce sont les logiques de sous-traitance les plus originales (comme les autres plug-ins). Plugin splitChunks) sont tous basés sur cela, avec l'aide de buildChunkGraph
Divers hooks déclenchés plus tard divisent, fusionnent et optimisent la structure Chunk pour obtenir des effets de sous-traitance étendus.
pense Chunk
Un seul fichier produit sera-t-il produit ? Pourquoi?mini-css-extract-plugin
、file-loader
Comment ce type de composant capable d'écrire des fichiers supplémentaires est-il implémenté en bas ?