Technologieaustausch

Webpack: Verpackungslogik von drei Chunk-Produkten

2024-07-08

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Überblick

  • im vorherigen Artikel Webpack: Dependency Graph verwaltet Abhängigkeiten zwischen Modulen , haben wir ausführlich erklärt, wie die „Build“-Phase mit Entry beginnt, den Modulinhalt nach und nach rekursiv liest und analysiert und schließlich den Modulabhängigkeitsgraphen erstellt – das ModuleGraph-Objekt. In diesem Artikel erklären wir weiterhin, wie man Chunks basierend auf ModuleGraph-Inhalten in der nächsten „Kapselungs“-Phase organisiert und den Hauptprozess von ChunkGroup- und ChunkGraph-Abhängigkeitsobjekten weiter aufbaut.

Neben dem Hauptprozess werden wir auch einige vage Konzepte im Detail erläutern:

  • Was sind die Objekte Chunk, ChunkGroup und ChunGraph? Welche Art von Interaktion gibt es zwischen ihnen?
  • Die Standardregeln für die Unterverpackung von Webpack und Probleme in den Regeln.

ChunkGraph-Erstellungsprozess

vorne Initialisieren, Erstellen, Versiegeln„In“ haben wir eingeführt, dass die zugrunde liegende Konstruktionslogik von Webpack grob unterteilt werden kann in: „Initialisierung, Konstruktion, Verpackung„drei Phasen:

Fügen Sie hier eine Bildbeschreibung ein

In,"Bauen„Die Phase ist dafür verantwortlich, die Abhängigkeiten zwischen Modulen zu analysieren und festzulegen Abhängigkeitsdiagramm(ModuleGraph); dann in „VerkapselungIn dieser Phase werden die Module basierend auf dem Abhängigkeitsdiagramm separat in mehrere Chunk-Objekte gekapselt und die Eltern-Kind-Abhängigkeitsbeziehungen zwischen Chunks werden in ChunkGraph und mehrere ChunkGroup-Objekte sortiert.

Das wichtigste Ziel der „Kapselungs“-Phase besteht darin, das ChunkGraph-Beziehungsdiagramm basierend auf dem in der „Build“-Phase gesammelten ModuleGraph-Beziehungsdiagramm zu erstellen. Die Logik dieses Prozesses ist relativ komplex:

Bitte fügen Sie eine Bildbeschreibung hinzu

Lassen Sie uns hier kurz die Implementierungslogik einiger wichtiger Schritte analysieren.

Der erste Schritt ist sehr kritisch: überweisenseal() Nach der Funktion durchlaufenentry Konfiguration, erstellen Sie für jeden Eintrag eine leereChunk UndEinstiegspunkt Objekt (ein besonderesChunkGroup) und richtete zunächst die Basis ein ChunkGraph Strukturelle Beziehung, bereiten Sie sich auf den nächsten Schritt vor, Schlüsselcode:

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);
    // 触发各种优化钩子
    // ...
  }
}

Nach Abschluss der Ausführung wird die folgende Datenstruktur gebildet:
Fügen Sie hier eine Bildbeschreibung ein

Zweitens, falls zu diesem Zeitpunkt konfiguriert entry.runtime, Webpack stellt zu diesem Zeitpunkt auch Laufzeitcode bereit erstellen Den entsprechenden Chunk und direktverteilen Gebenentry dazugehörigenChunkGroup Objekt.Wird angerufen, wenn alles fertig istErstellen Sie einen ChunkGraph Funktion, gehen Sie zum nächsten Schritt.

Schritt zwei: existierenbuildChunkGraph innerhalb der Funktionüberweisen visitModules Funktion: Durchlaufen Sie den ModuleGraph und weisen Sie alle Module entsprechend ihren Abhängigkeiten verschiedenen Modulen zu.Chunk Objekt; falls während dieses Vorgangs angetroffenAsync-Modul, dann das Modul erstellenneu ChunkGroup UndChunk Objekt, das letztendlich die folgende Datenstruktur bildet:
Bitte fügen Sie eine Bildbeschreibung hinzu

dritter Schritt: existierenbuildChunkGraph in Funktionüberweisen connectChunkGroups Methode, etablierenChunkGroup zwischen,Chunk Abhängigkeiten untereinander, um ein Gesamtbild zu erzeugenChunkGraph Objekt, das letztendlich die folgende Datenstruktur bildet:
Bitte fügen Sie eine Bildbeschreibung hinzu

der vierte Schritt: existierenbuildChunkGraph in Funktionüberweisen cleanupUnconnectedGroups Methode, Bereinigung ist ungültigChunkGroup, spielt hauptsächlich eine Rolle bei der Leistungsoptimierung.

Nachdem Sie diese vier Schritte von oben nach unten durchlaufen haben,ModuleGraph Die in gespeicherten Module werden entsprechend der Art des Moduls selbst drei verschiedenen Chunk-Objekten zugewiesen: Entry, Async und Runtime, und die Abhängigkeiten zwischen Chunks werden in den Sammlungen ChunkGraph und ChunkGroup gespeichert. Sie können basierend auf diesen Objekten fortfahren später ändern. Richtlinien zur Unterauftragsvergabe ändern (z. B.SplitChunksPlugin), realisieren Sie eine Optimierung der Unterauftragsvergabe, indem Sie den Besitz von Modul- und Chunk-Objekten neu organisieren und zuweisen.

Chunk vs. ChunkGroup vs. ChunkGraph

Der obige Konstruktionsprozess umfasst drei Schlüsselobjekte: Chunk, ChunkGroup und ChunkGraph. Fassen wir zunächst ihre Konzepte und Funktionen zusammen, um unser Verständnis zu vertiefen:

  • Chunk: Modul wird verwendet, um Modulinhalte zu lesen, Abhängigkeiten zwischen Modulen aufzuzeichnen usw.; während Chunk mehrere Module basierend auf Modulabhängigkeiten zusammenführt und in Asset-Dateien ausgibt (die Logik der Zusammenführung und Ausgabe von Produkten wird im nächsten Kapitel erläutert):

Bitte fügen Sie eine Bildbeschreibung hinzu

  • ChunkGroup:eins ChunkGroup enthält eine oder mehrereChunk Objekt;ChunkGroup UndChunkGroup Eine Eltern-Kind-Abhängigkeitsbeziehung entsteht zwischen:

Bitte fügen Sie eine Bildbeschreibung hinzu

  • ChunkGraph: Schließlich speichert Webpack die Abhängigkeiten zwischen Chunks und ChunkGroups in compilation.chunkGraph Im Objekt werden folgende Typbeziehungen gebildet:
    Bitte fügen Sie eine Bildbeschreibung hinzu

Standardregeln für die Unterauftragsvergabe

Obenstehendes ChunkGraph Der Build-Prozess organisiert das Modul letztendlich in drei verschiedene Arten von Chunks:

  • Eintragsblock: das Gleiche entry Die durch die untere Berührung erreichten Module sind in einem Chunk organisiert;
  • Async Chunk: Das asynchrone Modul ist separat in einem Chunk organisiert;
  • Laufzeitblock:entry.runtime Wenn es nicht leer ist, wird das Laufzeitmodul in einem Chunk organisiert.

Dies ist in Webpack integriert, sofern es nicht verwendet wird splitChunks Bei anderen Plug-Ins sind die Standardregeln für die Zuordnung der Moduleingabe zur Ausgabe eines der wichtigsten Grundprinzipien von Webpack. Daher ist es erforderlich, die spezifischen Regeln jedes Chunks einzuführen.

Eintragsblock:

Beginnend mit dem Entry Chunk wird zunächst Webpack ausgeführt entry erstellenChunk Objekt, zum Beispiel die folgende Konfiguration:

module.exports = {
  entry: {
    main: "./src/main",
    home: "./src/home",
  }
};

Traverse entry Objekteigenschaften und erstellenchunk[main]chunk[home] Zwei Objekte, die zu diesem Zeitpunkt jeweils zwei Chunks enthaltenmainhome Modul:
Fügen Sie hier eine Bildbeschreibung ein

Nach Abschluss der Initialisierung wird Webpack dies tun ModuleGraph Abhängigkeitsdaten, Willeentry Alle im Folgenden berührten Module werden in den Chunk (vorkommend in) gestopftBesuchsmodule Methode), zum Beispiel für die folgenden Dateiabhängigkeiten:
Fügen Sie hier eine Bildbeschreibung ein

main.js Wenn die vier Dateien a/b/c/d direkt oder indirekt synchron referenziert werden, wird Webpack dies zuerst tunmain.js Das Modul erstellt Chunk- und EntryPoint-Objekte und fügt dann nach und nach a/b/c/d-Module hinzuchunk[main] , schließlich bilden:
Fügen Sie hier eine Bildbeschreibung ein

Asynchroner Block:

Zweitens kompiliert Webpack jede asynchrone Importanweisung (import(xxx) Undrequire.ensure ) wird als separates Chunk-Objekt verarbeitet und alle seine Submodule werden diesem Chunk hinzugefügt – wir nennen ihn Async Chunk. Zum Beispiel für das folgende Beispiel:

// index.js
import './sync-a.js'
import './sync-b.js'

import('./async-a.js')

// async-a.js
import './sync-c.js'

Im Einstiegsmodul index.js In werden sync-a und sync-b auf synchrone Weise eingeführt; das async-a-Modul wird gleichzeitig auf asynchrone Weise eingeführt; in async-a wird das async-a-Modul auf synchrone Weise eingeführt.sync-c Modul, das das folgende Modulabhängigkeitsdiagramm bildet:
Bitte fügen Sie eine Bildbeschreibung hinzu

Zu diesem Zeitpunkt wird Webpack der Einstiegspunkt sein index.js, asynchrones Modul async-a.js Erstellen Sie Unterpakete separat, um die folgende Chunk-Struktur zu bilden:
Fügen Sie hier eine Bildbeschreibung ein

Und chunk[index] Undchunk[async-a] Zwischen ihnen wird eine einseitige Abhängigkeit gebildet, und Webpack speichert diese Abhängigkeit inChunkGroup._parentsChunkGroup._children in Immobilien.

Laufzeit-Chunk:

Endlich, außer entry Zusätzlich zu asynchronen Modulen unterstützt Webpack5 auch das separate Extrahieren von Laufzeitcode in Chunks. Der hier erwähnte Laufzeitcode bezieht sich auf eine Reihe grundlegender Framework-Codes, die von Webpack eingefügt werden, um sicherzustellen, dass das verpackte Produkt normal ausgeführt werden kann. Die allgemeine Struktur des verpackten Webpack-Produkts lautet beispielsweise wie folgt:
Bitte fügen Sie eine Bildbeschreibung hinzu
Der große Codeabschnitt, der im roten Feld im obigen Bild eingekreist ist, ist der von Webpack dynamisch generierte Laufzeitcode. Beim Kompilieren entscheidet Webpack basierend auf dem Geschäftscode (basierend auf).Dependency Unterklasse), zum Beispiel:

  • brauchen __webpack_require__.f__webpack_require__.r und andere Funktionen, um eine minimale modulare Unterstützung zu erreichen;
  • Wenn Sie die dynamische Ladefunktion verwenden, müssen Sie schreiben __webpack_require__.e Funktion;
  • Wenn Sie die Funktion „Module Federation“ verwenden, müssen Sie schreiben __webpack_require__.o Funktion;
  • usw.

Obwohl jeder Teil des Laufzeitcodes klein sein kann, wird das Endergebnis immer größer. Insbesondere bei Anwendungen mit mehreren Einträgen ist es etwas verschwenderisch, bei jedem Eintrag wiederholt eine ähnliche Laufzeit zu packen entry.runtime Konfigurationselemente werden verwendet, um zu deklarieren, wie Laufzeitcode gepackt wird.Zur Verwendung einfach verwendenentry Fügen Sie dem Element eine Zeichenfolgenform hinzuruntime Wert, zum Beispiel:

module.exports = {
  entry: {
    index: { import: "./src/index", runtime: "solid-runtime" },
  }
};

existieren compilation.seal In der Funktion steht Webpack an erster Stelleentry erstellenEntryPoint, dann urteilen entry Enthält die Konfigurationruntime Eigenschaften, falls vorhanden, erstellen Sie sie mitruntime Der Wert ist der Name des Chunks. Daher generiert die obige Beispielkonfiguration zwei Chunks:chunk[index.js]chunk[solid-runtime], und darauf basierend werden schließlich zwei Dateien ausgegeben:

  • Entsprechend dem Eingangsverzeichnis index.js dokumentieren;
  • Laufzeitkonfiguration entsprechend solid-runtime.js dokumentieren.

in vielen entry Im Szenario nur für jedenentry Alle gleich eingestelltruntime Wert wird der Webpack-Laufzeitcode zusammengeführt und in denselben Laufzeitblock geschrieben, wodurch letztendlich eine Optimierung der Produktleistung erreicht wird. Zum Beispiel für die folgende Konfiguration:

module.exports = {
  entry: {
    index: { import: "./src/index", runtime: "solid-runtime" },
    home: { import: "./src/home", runtime: "solid-runtime" },
  }
};

Eingang indexhome das Gleiche teilenruntime Wert und generieren schließlich jeweils drei Chunks:
Bitte fügen Sie eine Bildbeschreibung hinzu

Einlass zu dieser Zeit chunk[index]chunk[home] mit Laufzeitchunk[solid-runtime] Es entsteht auch ein Eltern-Kind-Abhängigkeitsverhältnis.

Probleme mit den Regeln für die Vergabe von Unteraufträgen

Das größte Problem mit den Standard-Unterverpackungsregeln besteht darin, dass Modulduplizierungen nicht behoben werden können. Wenn mehrere Chunks gleichzeitig dasselbe Modul enthalten, wird dieses Modul ohne Einschränkung wiederholt in diese Chunks gepackt. Angenommen, wir haben zwei Einträge main/index, die beide vom selben Modul abhängen:
Fügen Sie hier eine Bildbeschreibung ein

Standardmäßig führt Webpack hierfür keine zusätzliche Verarbeitung durch, sondern packt das C-Modul einfach gleichzeitig in die beiden Haupt-/Index-Chunks, wodurch letztendlich Folgendes entsteht:

Fügen Sie hier eine Bildbeschreibung ein

kann gesehen werden chunk Sind sie voneinander isoliert, wird Modul c wiederholt verpackt, was zu unnötigen Leistungseinbußen beim Endprodukt führen kann!

Um dieses Problem zu lösen, wurde Webpack 3 eingeführt CommonChunkPlugin Das Plugin versucht, gemeinsame Abhängigkeiten zwischen Einträgen in separate zu extrahierenchunk,Aber CommonChunkPlugin Es basiert im Wesentlichen auf der einfachen Eltern-Kind-Beziehungskette zwischen Chunks. Es ist schwierig, daraus zu schließen, dass das extrahierte dritte Paket verwendet werden sollteentry Vaterchunk Immer noch ein KindchunkCommonChunkPlugin einheitliche Verarbeitung als übergeordnetes Elementchunk, in einigen Fällen hat es erhebliche negative Auswirkungen auf die Leistung.

Aus diesem Grund wurden nach Webpack 4 gezielt komplexere Datenstrukturen eingeführt. ChunkGroup Spezialisiert auf die Umsetzung von Beziehungskettenmanagement und ZusammenarbeitSplitChunksPlugin effizienter und intelligenter umgesetzt werden könnenHeuristische Unterauftragsvergabe.

Zusammenfassen

Zusammenfassend ist die Phase „Build“ für den Aufbau des ModuleGraphs basierend auf der Referenzbeziehung des Moduls verantwortlich; die Phase „Encapsulation“ ist für den Aufbau einer Reihe von Chunk-Objekten basierend auf dem ModuleGraph und die Organisation der Abhängigkeiten zwischen Chunks (asynchrone Referenzen, Laufzeit) verantwortlich ) in ChunkGraph – Chunk-Abhängigkeitsdiagrammobjekt. Ähnlich wie bei ModuleGraph kann die Einführung der ChunkGraph-Struktur auch die Verwaltungslogik von Abhängigkeiten zwischen Chunks entkoppeln, wodurch die Gesamtarchitekturlogik vernünftiger und einfacher erweiterbar wird.

Auch wenn es sehr kompliziert aussieht, besteht das wichtigste Ziel der „Verpackungs“-Phase immer noch darin, zu bestimmen, wie viele Chunks vorhanden sind und welche Module in jedem Chunk enthalten sind – das sind die Schlüsselfaktoren, die das endgültige Verpackungsergebnis wirklich beeinflussen.

Für diesen Punkt müssen wir die drei integrierten Unterauftragsregeln von Webpack5 verstehen: Entry Chunk, Async Chunk und Runtime Chunk. Dies sind die originellsten Unterauftragslogiken (z. B splitChunksPlugin) basieren alle darauf, mit Hilfe von buildChunkGraph Verschiedene Hooks lösten später eine weitere Aufteilung, Zusammenführung und Optimierung der Chunk-Struktur aus, um erweiterte Unterauftragseffekte zu erzielen.

denken Chunk Wird nur eine Produktdatei erstellt? Warum?mini-css-extract-pluginfile-loader Wie wird diese Art von Komponente, die zusätzliche Dateien schreiben kann, unten implementiert?