Κοινή χρήση τεχνολογίας

Webpack: Λογική συσκευασίας τριών προϊόντων Chunk

2024-07-08

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

ΣΦΑΙΡΙΚΗ ΕΙΚΟΝΑ

  • στο προηγούμενο άρθρο Συσκευασία Ιστού: Το Γράφημα Εξάρτησης διαχειρίζεται τις εξαρτήσεις μεταξύ των λειτουργικών μονάδων , έχουμε εξηγήσει λεπτομερώς πώς η φάση "κατασκευής" ξεκινά από το Entry και σταδιακά διαβάζει και αναλύει αναδρομικά το περιεχόμενο της ενότητας και τελικά δημιουργεί το γράφημα εξάρτησης της ενότητας - το αντικείμενο ModuleGraph. Σε αυτό το άρθρο, συνεχίζουμε να εξηγούμε πώς να οργανώνουμε Chunks με βάση το περιεχόμενο του ModuleGraph στο επόμενο στάδιο "ενθυλάκωσης" και να χτίζουμε περαιτέρω την κύρια διαδικασία των αντικειμένων εξάρτησης ChunkGroup και ChunkGraph.

Εκτός από την κύρια διαδικασία, θα εξηγήσουμε επίσης πολλές ασαφείς έννοιες λεπτομερώς:

  • Τι είναι τα αντικείμενα Chunk, ChunkGroup και ChunGraph; Τι είδους αλληλεπίδραση υπάρχει μεταξύ τους;
  • Οι προεπιλεγμένοι κανόνες υποσυσκευασίας του Webpack και προβλήματα στους κανόνες.

Διαδικασία κατασκευής ChunkGraph

μπροστά Init, Make, Seal"Στο ", έχουμε εισαγάγει ότι η υποκείμενη κατασκευαστική λογική του 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 θα παρέχει επίσης κώδικα χρόνου εκτέλεσης σε αυτό το στάδιο δημιουργώ Το αντίστοιχο Chunk και απευθείαςδιανέμω Δίνωentry αντίστοιχοςChunkGroup αντικείμενο.Τηλεφώνησε όταν όλα είναι έτοιμαbuildChunkGraph λειτουργία, μεταβείτε στο επόμενο βήμα.

Βήμα δυο: υπάρχειbuildChunkGraph εντός λειτουργίαςΜΕΤΑΦΟΡΑ visitModules Λειτουργήστε, διασχίστε το ModuleGraph και αντιστοιχίστε όλες τις μονάδες σε διαφορετικές ενότητες ανάλογα με τις εξαρτήσεις τους.Chunk Αντικείμενο, εάν συναντηθεί κατά τη διάρκεια αυτής της διαδικασίαςασύγχρονη μονάδα, μετά το module δημιουργώνέος ChunkGroup καιChunk Αντικείμενο, σχηματίζοντας τελικά την ακόλουθη δομή δεδομένων:
Προσθέστε περιγραφή εικόνας

τρίτο βήμα: υπάρχειbuildChunkGraph σε λειτουργίαΜΕΤΑΦΟΡΑ connectChunkGroups μέθοδος, καθιερώνωChunkGroup μεταξύ,Chunk εξαρτήσεις μεταξύ τους για να δημιουργήσουν ένα πλήρεςChunkGraph Αντικείμενο, σχηματίζοντας τελικά την ακόλουθη δομή δεδομένων:
Προσθέστε περιγραφή εικόνας

το τέταρτο βήμα: υπάρχειbuildChunkGraph σε λειτουργίαΜΕΤΑΦΟΡΑ cleanupUnconnectedGroups Μέθοδος, η εκκαθάριση δεν είναι έγκυρηChunkGroup, παίζει κυρίως ρόλο στη βελτιστοποίηση της απόδοσης.

Αφού περάσετε από αυτά τα τέσσερα βήματα από πάνω προς τα κάτω,ModuleGraph Οι μονάδες που είναι αποθηκευμένες στο θα εκχωρηθούν σε τρία διαφορετικά αντικείμενα Chunk: Entry, Async και Runtime ανάλογα με τη φύση της ίδιας της ενότητας και οι εξαρτήσεις μεταξύ των Chunk θα αποθηκευτούν στις συλλογές ChunkGraph και ChunkGroup. Μπορείτε να συνεχίσετε με βάση αυτά τα αντικείμενα αργότερα Τροποποιήστε τις πολιτικές υπεργολαβίας (π.χ.SplitChunksPlugin), πραγματοποιήστε βελτιστοποίηση υπεργολαβίας με την αναδιοργάνωση και την κατανομή της ιδιοκτησίας των αντικειμένων Module και Chunk.

Chunk vs ChunkGroup vs ChunkGraph

Η παραπάνω διαδικασία κατασκευής περιλαμβάνει τρία βασικά αντικείμενα: Chunk, ChunkGroup και ChunkGraph Ας συνοψίσουμε πρώτα τις έννοιες και τις λειτουργίες τους για να εμβαθύνουμε την κατανόησή μας:

  • Chunk: Η μονάδα χρησιμοποιείται για την ανάγνωση του περιεχομένου της ενότητας, την καταγραφή εξαρτήσεων μεταξύ των μονάδων, κ.λπ., ενώ το Chunk συγχωνεύει πολλαπλές ενότητες με βάση τις εξαρτήσεις λειτουργιών και τις εξάγει σε αρχεία στοιχείων (η λογική της συγχώνευσης και της παραγωγής προϊόντων θα εξηγηθεί στο επόμενο κεφάλαιο):

Προσθέστε περιγραφή εικόνας

  • ChunkGroup:ένας ChunkGroup περιέχει ένα ή περισσότεραChunk αντικείμενο;ChunkGroup καιChunkGroup Μια σχέση εξάρτησης γονέα-παιδιού διαμορφώνεται μεταξύ:

Προσθέστε περιγραφή εικόνας

  • ChunkGraph: Τέλος, το Webpack θα αποθηκεύσει τις εξαρτήσεις μεταξύ των Chunks και των ChunkGroups compilation.chunkGraph Στο αντικείμενο, σχηματίζονται οι ακόλουθες σχέσεις τύπου:
    Προσθέστε περιγραφή εικόνας

Προεπιλεγμένοι κανόνες υπεργολαβίας

Τα παραπάνω ChunkGraph Η διαδικασία κατασκευής θα οργανώσει τελικά το Module σε τρεις διαφορετικούς τύπους Chunks:

  • Κομμάτι εισόδου: το ίδιο entry Οι μονάδες στις οποίες φτάνει το κάτω άγγιγμα οργανώνονται σε ένα κομμάτι.
  • Async Chunk: Η ασύγχρονη λειτουργική μονάδα είναι οργανωμένη χωριστά σε ένα Chunk.
  • Κομμάτι χρόνου εκτέλεσης:entry.runtime Όταν δεν είναι κενή, η ενότητα χρόνου εκτέλεσης θα οργανωθεί σε ένα Chunk.

Αυτό είναι ενσωματωμένο στο Webpack, εάν δεν χρησιμοποιείται splitChunks Στην περίπτωση άλλων προσθηκών, οι προεπιλεγμένοι κανόνες για την αντιστοίχιση της εισόδου της μονάδας στην έξοδο είναι μία από τις βασικές βασικές αρχές του Webpack, επομένως είναι απαραίτητο να εισαχθούν οι συγκεκριμένοι κανόνες για κάθε Τσάνκ.

Κομμάτι εισόδου:

Ξεκινώντας με το Entry Chunk, το Webpack θα πρώτα entry δημιουργώChunk Αντιγράψτε, για παράδειγμα, την ακόλουθη διαμόρφωση:

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

Διασχίζω entry ιδιότητες αντικειμένου και δημιουργίαchunk[main]chunk[home] Δύο αντικείμενα, αυτή τη στιγμή περιέχουν τα δύο Κομμάτια αντίστοιχαmainhome Μονάδα μέτρησης:
Εισαγάγετε την περιγραφή της εικόνας εδώ

Αφού ολοκληρωθεί η προετοιμασία, το Webpack θα ModuleGraph δεδομένα εξάρτησης, θαentry Όλες οι ενότητες που αγγίζονται από τα παρακάτω τοποθετούνται στο Τσάνκ (που εμφανίζεται μέσαvisitModules μέθοδος), για παράδειγμα, για τις ακόλουθες εξαρτήσεις αρχείων:
Εισαγάγετε την περιγραφή της εικόνας εδώ

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 εισάγεται με έναν ασύγχρονο τρόπο.sync-c ενότητα, σχηματίζοντας το ακόλουθο διάγραμμα εξάρτησης ενότητας:
Προσθέστε περιγραφή εικόνας

Σε αυτό το σημείο, το Webpack θα είναι το σημείο εισόδου index.js, ασύγχρονη μονάδα async-a.js Δημιουργήστε υποπακέτα ξεχωριστά για να σχηματίσετε την ακόλουθη δομή Chunk:
Εισαγάγετε την περιγραφή της εικόνας εδώ

και chunk[index] καιchunk[async-a] Δημιουργείται μια μονόδρομη εξάρτηση μεταξύ τους και το Webpack θα αποθηκεύσει αυτήν την εξάρτησηChunkGroup._parentsChunkGroup._children σε ακίνητα.

Κομμάτι χρόνου εκτέλεσης:

Τέλος, εκτός entry , Εκτός από τις ασύγχρονες μονάδες, το Webpack5 υποστηρίζει επίσης την εξαγωγή κώδικα χρόνου εκτέλεσης ξεχωριστά σε Chunks. Ο κώδικας χρόνου εκτέλεσης που αναφέρεται εδώ αναφέρεται σε μια σειρά βασικών κωδικών πλαισίου που εισάγονται από το Webpack για να διασφαλιστεί ότι το συσκευασμένο προϊόν μπορεί να εκτελεστεί κανονικά.
Προσθέστε περιγραφή εικόνας
Το μεγάλο τμήμα κώδικα που κυκλώνεται στο κόκκινο πλαίσιο στην παραπάνω εικόνα είναι ο κώδικας χρόνου εκτέλεσης που δημιουργείται δυναμικά από το Webpack Κατά τη μεταγλώττιση, το Webpack θα αποφασίσει ποιον κώδικα χρόνου εκτέλεσης θα εξάγει με βάση τον επιχειρηματικό κώδικα (με βάσηDependency υποκατηγορία), για παράδειγμα:

  • χρειάζομαι __webpack_require__.f__webpack_require__.r και άλλες λειτουργίες για την επίτευξη ελάχιστης αρθρωτής υποστήριξης.
  • Εάν χρησιμοποιείτε τη δυνατότητα δυναμικής φόρτωσης, πρέπει να γράψετε __webpack_require__.e λειτουργία;
  • Εάν χρησιμοποιείτε τη δυνατότητα Module Federation, πρέπει να γράψετε __webpack_require__.o λειτουργία;
  • και τα λοιπά.

Αν και κάθε κομμάτι του κώδικα χρόνου εκτέλεσης μπορεί να είναι μικρό, καθώς οι δυνατότητες αυξάνονται, το τελικό αποτέλεσμα θα γίνεται όλο και μεγαλύτερο entry.runtime Τα στοιχεία διαμόρφωσης χρησιμοποιούνται για να δηλώσουν πώς συσκευάζεται ο κώδικας χρόνου εκτέλεσης.Για χρήση, απλώς χρησιμοποιήστεentry Προσθήκη φόρμας συμβολοσειράς στο αντικείμενοruntime τιμή, για παράδειγμα:

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

υπάρχει compilation.seal Στη συνάρτηση, το Webpack είναι πρώταentry δημιουργώEntryPoint, μετά κρίνετε entry Περιέχει η διαμόρφωσηruntime ιδιότητες, εάν υπάρχουν, δημιουργήστε τις μεruntime Η τιμή είναι το όνομα του κομματιού. Επομένως, η παραπάνω διαμόρφωση παραδείγματος θα δημιουργήσει δύο κομμάτια:chunk[index.js]chunk[solid-runtime], και με βάση αυτό, τελικά εξάγονται δύο αρχεία:

  • Αντίστοιχο με τον δείκτη εισόδου index.js έγγραφο;
  • Ρύθμιση χρόνου εκτέλεσης που αντιστοιχεί σε solid-runtime.js έγγραφο.

σε ΠΟΛΛΟΥΣ entry Στο σενάριο, μόνο για τον καθέναentry Όλα τα ίδιαruntime τιμή, ο κώδικας χρόνου εκτέλεσης Webpack θα συγχωνευθεί και θα γραφτεί στο ίδιο Runtime Chunk, επιτυγχάνοντας τελικά τη βελτιστοποίηση της απόδοσης του προϊόντος. Για παράδειγμα, για την ακόλουθη διαμόρφωση:

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

Είσοδος indexhome μοιραστείτε τα ίδιαruntime αξία και τελικά δημιουργούν τρία κομμάτια, αντίστοιχα:
Προσθέστε περιγραφή εικόνας

Είσοδος αυτή την ώρα chunk[index]chunk[home] με χρόνο εκτέλεσηςchunk[solid-runtime] Θα δημιουργηθεί επίσης σχέση εξάρτησης γονέα-παιδιού.

Ζητήματα με τους κανόνες υπεργολαβίας

Το μεγαλύτερο πρόβλημα με τους προεπιλεγμένους κανόνες υποσυσκευασίας είναι ότι δεν μπορεί να λύσει την αντιγραφή της ενότητας Εάν πολλά κομμάτια περιέχουν την ίδια μονάδα ταυτόχρονα, τότε αυτή η ενότητα θα συσκευαστεί επανειλημμένα σε αυτά τα Τεμάχια χωρίς περιορισμούς. Για παράδειγμα, ας υποθέσουμε ότι έχουμε δύο καταχωρήσεις main/index που και οι δύο εξαρτώνται από την ίδια ενότητα:
Εισαγάγετε την περιγραφή της εικόνας εδώ

Από προεπιλογή, το Webpack δεν θα κάνει πρόσθετη επεξεργασία σε αυτό, αλλά απλώς συσκευάζει τη λειτουργική μονάδα c στο κύριο/ευρετήριο δύο Τεμάχια ταυτόχρονα, σχηματίζοντας τελικά:

Εισαγάγετε την περιγραφή της εικόνας εδώ

μπορεί να ειδωθεί chunk είναι απομονωμένα μεταξύ τους, η ενότητα c συσκευάζεται επανειλημμένα, γεγονός που μπορεί να προκαλέσει περιττή απώλεια απόδοσης στο τελικό προϊόν!

Για την επίλυση αυτού του προβλήματος, παρουσιάστηκε το Webpack 3 CommonChunkPlugin Το πρόσθετο επιχειρεί να εξαγάγει κοινές εξαρτήσεις μεταξύ των καταχωρήσεων σε ξεχωριστέςchunk,αλλά CommonChunkPlugin Ουσιαστικά υλοποιείται με βάση την απλή αλυσίδα σχέσεων γονέα-παιδιού μεταξύ των Chunks. Είναι δύσκολο να συναχθεί ότι το εξαγόμενο τρίτο πακέτο θα πρέπει να χρησιμοποιηθεί ωςentry πατέραςchunk Παιδί ακόμαchunkCommonChunkPlugin ενοποιημένη επεξεργασία ως μητρικήchunk, σε ορισμένες περιπτώσεις έχει σημαντικό αρνητικό αντίκτυπο στην απόδοση.

Για το λόγο αυτό, πιο περίπλοκες δομές δεδομένων εισήχθησαν ειδικά μετά το Webpack 4—— ChunkGroup Εξειδικεύεται στην υλοποίηση διαχείρισης και συνεργασίας αλυσίδας σχέσεωνSplitChunksPlugin μπορεί να εφαρμοστεί πιο αποτελεσματικά και έξυπναΕυρετική υπεργολαβία.

Συνοψίζω

Συνοπτικά, η φάση "Build" είναι υπεύθυνη για τη δημιουργία του ModuleGraph με βάση τη σχέση αναφοράς της ενότητας, η φάση "Encapsulation" είναι υπεύθυνη για τη δημιουργία μιας σειράς αντικειμένων με βάση το ModuleGraph και την οργάνωση των εξαρτήσεων μεταξύ των Chunks (ασύγχρονες αναφορές, χρόνος εκτέλεσης). ) στο αντικείμενο γραφήματος ChunkGraph - Chunk Dependency. Παρόμοια με το ModuleGraph, η εισαγωγή της δομής ChunkGraph μπορεί επίσης να αποσυνδέσει τη λογική διαχείρισης των εξαρτήσεων μεταξύ των Chunks, καθιστώντας τη συνολική αρχιτεκτονική λογική πιο λογική και ευκολότερη στην επέκταση.

Ωστόσο, αν και φαίνεται πολύ περίπλοκο, ο πιο σημαντικός στόχος του σταδίου "συσκευασίας" εξακολουθεί να είναι να καθοριστεί πόσα Chunks υπάρχουν και ποιες Ενότητες περιλαμβάνονται σε κάθε Chunk - αυτοί είναι οι βασικοί παράγοντες που επηρεάζουν πραγματικά το τελικό αποτέλεσμα συσκευασίας.

Για αυτό το σημείο, πρέπει να κατανοήσουμε τους τρεις ενσωματωμένους κανόνες υπεργολαβίας του Webpack5: Entry Chunk, Async Chunk και Runtime Chunk Αυτές είναι οι πιο πρωτότυπες λογικές υπεργολαβίας (π splitChunksPlugin) βασίζονται όλα σε αυτό, με τη βοήθεια του buildChunkGraph Διάφορα άγκιστρα ενεργοποιήθηκαν αργότερα, περαιτέρω διαχωρισμός, συγχώνευση και βελτιστοποίηση της δομής Chunk για την επίτευξη διευρυμένων αποτελεσμάτων υπεργολαβίας.

νομίζω Chunk Θα δημιουργηθεί και μόνο ένα αρχείο προϊόντος; Γιατί;mini-css-extract-pluginfile-loader Πώς υλοποιείται αυτός ο τύπος στοιχείου που μπορεί να γράψει πρόσθετα αρχεία στο κάτω μέρος;