Κώδικας-διαίρεση σε γωνιακή ή πώς να μοιράζονται τα στοιχεία μεταξύ τεμπέλης ενότητες

Αυτό το άρθρο θα σας δώσει μια καλύτερη κατανόηση του τρόπου με τον οποίο ο Γωνιακός διαιρέσει τον κώδικα σας σε κομμάτια.

Εάν φοβάστε τη γωνιακή έξοδο CLI που δείξατε παραπάνω ή αν είστε περίεργος πώς συμβαίνει αυτός ο διαχωρισμός κώδικα τότε αυτή η ανάρτηση είναι για σας.

Ο διαχωρισμός κώδικα σάς επιτρέπει να διαιρέσετε τον κωδικό σας σε διάφορες δέσμες, οι οποίες στη συνέχεια μπορούν να φορτωθούν κατόπιν παραγγελίας. Εάν χρησιμοποιηθεί σωστά, μπορεί να έχει σημαντικό αντίκτυπο στο χρόνο φόρτωσης.

Περιεχόμενα

  1. Γιατί να με νοιάζει?
  2. Γωνιακός κωδικός CLI-διαίρεση κάτω από την κουκούλα
  3. Απλή γωνιακή εφαρμογή με τεμπέλης μονάδες
  4. Πώς να μοιράζεστε εξαρτήματα μεταξύ των τεμπέλης μονάδων
  5. συμπέρασμα

Γιατί να με νοιάζει?

Ας φανταστούμε ότι ξεκινήσατε ένα ολοκαίνουργιο έργο γωνίας. Διαβάσατε πολλούς πόρους σχετικά με τον τρόπο αρχιτεκτονικής μιας γωνιακής εφαρμογής, ποια είναι η κατάλληλη δομή του φακέλου και, τι είναι πιο σημαντικό, πώς να διατηρήσετε εξαιρετική απόδοση εκκίνησης.

Επιλέξατε τη γωνιακή CLI και δημιούργησα μια αρθρωτή εφαρμογή με πολλές λειτουργικές λειτουργίες. Και βέβαια, δημιουργήσατε μια κοινόχρηστη ενότητα, στην οποία βάζετε κοινώς χρησιμοποιούμενες οδηγίες, σωλήνες και εξαρτήματα.

Μετά από λίγο, έχετε πιάσει τον εαυτό σας να σκέφτεται ότι μόλις η νέα λειτουργική μονάδα σας απαιτεί κάποια λειτουργικότητα από άλλες λειτουργικές μονάδες, τείνετε να μετακινήσετε αυτή τη λειτουργία σε εκείνη την ενιαία κοινόχρηστη λειτουργική μονάδα.

Η εφαρμογή εξελίσσεται και σύντομα παρατηρήσατε ότι ο χρόνος εκκίνησης δεν ανταποκρίνεται στην προσδοκία σας (και, κυρίως, στον πελάτη σας).

Τώρα, έχετε αμφιβολίες ...

  • Αν έβαλα όλους τους σωλήνες, τις οδηγίες και τα κοινά στοιχεία μου σε μια μεγάλη κοινόχρηστη ενότητα και στη συνέχεια την εισήγαγα σε τεμπέλης φορτωμένες μονάδες (όπου χρησιμοποιώ μόνο μία ή δύο από τις εισαγόμενες λειτουργίες), πιθανότατα μπορεί να προκαλέσει διπλότυπα αχρησιμοποίητου κώδικα στα αρχεία εξόδου .
  • Από την άλλη πλευρά, εάν χωρίσω κοινά χαρακτηριστικά μεταξύ πολλών κοινόχρηστων μονάδων και εισάγω μόνο αυτά που χρειάζονται σε κάθε συγκεκριμένη ενότητα, θα μειώσει το μέγεθος της εφαρμογής μου; Ή Γωνιακή κάνει όλες αυτές τις βελτιστοποιήσεις από προεπιλογή;

Ας απομυθοποιήσουμε!

Γωνιακός κωδικός CLI-διαίρεση κάτω από την κουκούλα

Όπως όλοι γνωρίζουμε, η τρέχουσα γωνιακή έκδοση CLI χρησιμοποιεί webpack για να πραγματοποιήσει τη δέσμευση. Ωστόσο, παράλληλα, η διαδικτυακή συσκευασία είναι επίσης υπεύθυνη για τη διάσπαση κώδικα.

Έτσι, ας ρίξουμε μια ματιά στο πώς το κάνει το webpack.

Το Webpack 4 εισήγαγε το SplitChunksPlugin που μας επιτρέπει να ορίσουμε κάποια ευρετικά στοιχεία για να χωρίσουμε τις μονάδες σε κομμάτια. Πολλοί άνθρωποι παραπονιούνται ότι αυτή η διαμόρφωση φαίνεται μυστηριώδης. Και ταυτόχρονα, αυτό είναι το πιο ενδιαφέρον κομμάτι του διαχωρισμού κώδικα.

Αλλά πριν την εφαρμογή της βελτιστοποίησης του SplitChunksPlugin, το webpack δημιουργεί ένα νέο κομμάτι:

  • για κάθε σημείο εισόδου

Ο γωνιακός CLI διαμορφώνει τα ακόλουθα σημεία εισόδου

κύρια στυλ polyfills

η οποία θα έχει ως αποτέλεσμα τα κομμάτια με τα ίδια ονόματα.

  • για κάθε δυναμικά φορτισμένη ενότητα (χρησιμοποιώντας τη σύνταξη εισαγωγής () που συμμορφώνεται με την πρόταση ECMAScript για δυναμικές εισαγωγές)

Θυμάσαι τη σύνταξη φόρτωσης των παιδιών; Αυτό είναι το σήμα για το webpack για να δημιουργήσετε ένα κομμάτι.

Τώρα ας προχωρήσουμε στο SplitChunksPlugin. Μπορεί να ενεργοποιηθεί μέσα στο μπλοκ βελτιστοποίησης του webpack.config.js

Ας δούμε τον πηγαίο κώδικα γωνιακού CLI και βρείτε αυτήν την ενότητα διαμόρφωσης:

Διαμόρφωση SplitChunksPlugin στο Γωνιακό CLI 8

Θα επικεντρωθούμε εδώ στις επιλογές cacheGroups, καθώς αυτή είναι η "συνταγή" για το webpack σχετικά με τον τρόπο δημιουργίας χωριστών κομμάτια με βάση κάποιες συνθήκες.

cacheGroups είναι ένα απλό αντικείμενο όπου το κλειδί είναι ένα όνομα ομάδας. Βασικά, μπορούμε να σκεφτούμε μια ομάδα κρυφής μνήμης ως πιθανή ευκαιρία για ένα νέο κομμάτι που θα δημιουργηθεί.

Κάθε ομάδα έχει πολλές διαμορφώσεις και μπορεί να κληρονομήσει διαμόρφωση από επίπεδο splitChunks.

Ας πάμε πραγματικά γρήγορα πάνω σε αυτά των επιλογών που είδαμε στη ρύθμιση γωνίας CLI παραπάνω:

  • η τιμή χονδρών μπορεί να χρησιμοποιηθεί για να φιλτράρει μονάδες ανάμεσα στα κομμάτια συγχρονισμού και ασύγχρονου συγχρονισμού. Η αξία του μπορεί να είναι αρχική, async ή όλα. αρχικά σημαίνει μόνο να προσθέσετε αρχεία στο κομμάτι εάν εισάγονται μέσα σε κομμάτια συγχρονισμού. async σημαίνει μόνο να προσθέσετε αρχεία στο κομμάτι εάν εισάγονται μέσα σε τμήματα async (async από προεπιλογή)
  • Το minChunks λέει ότι η δικτυακή τσάντα πρέπει να εισάγει μονάδες μόνο σε κομμάτι εάν είναι κοινόχρηστο μεταξύ τουλάχιστον 2 τεμαχίων (1 από προεπιλογή)
  • όνομα λέει στο webpack να χρησιμοποιεί αυτό το όνομα για ένα νέο κομμάτι. Καθορίζοντας είτε μια συμβολοσειρά είτε μια συνάρτηση που πάντα επιστρέφει την ίδια συμβολοσειρά θα συγχωνεύσει όλες τις κοινές ενότητες σε ένα μόνο κομμάτι.
  • η τιμή προτεραιότητας χρησιμοποιείται για τον εντοπισμό των βέλτιστων τμημάτων όταν ένα δομοστοιχείο πέφτει κάτω από πολλές ομάδες κομματιών.
  • enforce λέει στο webpack να αγνοήσει τις επιλογές minSize, minChunks, maxAsyncRequests και maxInitialRequests και να δημιουργεί πάντα κομμάτια για αυτήν την ομάδα κρυφής μνήμης. Υπάρχει ένα μικρό gotcha εδώ: αν κάποια από αυτές τις επιλογές που παραβλέπονται παρέχονται στο επίπεδο cacheGroup τότε αυτή η επιλογή θα εξακολουθεί να χρησιμοποιείται.
  • ελέγξτε ποια στοιχεία επιλέγονται από αυτήν την ομάδα κρυφής μνήμης. Όπως θα μπορούσαμε να παρατηρήσουμε, το γωνιακό CLI χρησιμοποιεί αυτή την επιλογή για να μετακινήσει όλες τις εξαρτήσεις του κόμβου modem στο κομμάτι του πωλητή.
  • Το minSize χρησιμοποιείται για τον προσδιορισμό του ελάχιστου μεγέθους, σε bytes, για ένα κομμάτι που θα δημιουργηθεί. Δεν εμφανίστηκε στο γωνιακό CLI config αλλά είναι μια πολύ σημαντική επιλογή που πρέπει να γνωρίζουμε. (Όπως αναφέρει το πηγαίο κώδικα, είναι 30kb από προεπιλογή στην παραγωγή και 10kb σε περιβάλλον dev)
Συμβουλή: παρά το γεγονός ότι η τεκμηρίωση για το webpack ορίζει τις προεπιλογές, θα αναφερθώ στον πηγαίο κώδικα webpack για να βρω τις ακριβείς τιμές

Ας ανακεφαλαιώσουμε εδώ: Το γωνιακό CLI θα μετακινήσει μια ενότητα σε:

  • τεμάχιο προμηθευτή αν αυτή η ενότητα προέρχεται από τον κατάλογο node_modules.
  • προεπιλεγμένο τεμάχιο εάν η ενότητα αυτή εισάγεται μέσα σε μια λειτουργική μονάδα ασύγχρονου συγχρονισμού και μοιράζεται μεταξύ τουλάχιστον δύο ενοτήτων. Σημειώστε ότι υπάρχουν πολλά προεπιλεγμένα κομμάτια εδώ. Θα εξηγήσω πώς το webpack δημιουργεί ονόματα για αυτά τα κομμάτια αργότερα.
  • ένα κοινό κομμάτι εάν η ενότητα αυτή εισάγεται μέσα σε μια μονάδα ασύγχρονης επικοινωνίας και μοιράζεται μεταξύ τουλάχιστον δύο ενοτήτων και δεν εμπίπτει στο προεπιλεγμένο κομμάτι (hello priority) και επίσης δεν έχει σημασία ποιο μέγεθος είναι (χάρη στην επιλογή force)

Αρκετή θεωρία, ας ασκήσουμε.

Απλή γωνιακή εφαρμογή με τεμπέλης μονάδες

Για να εξηγήσουμε τη διαδικασία του SplitChunksPlugin, θα ξεκινήσουμε με μια απλοποιημένη έκδοση της Γωνιακής εφαρμογής:

app ├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ ├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ │ ts │ └── cd │ └── cd.component.ts │ └── cd.module.ts │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────── │ └───────────────────────────────────────────────────────────────────────────────

Εδώ a, b, c και d είναι τεμπέλης ενότητες, που σημαίνουν ότι εισάγονται χρησιμοποιώντας τη σύνταξη της εισαγωγής ().

Τα a και b εξαρτήματα χρησιμοποιούν το ab component στα πρότυπα τους. Τα στοιχεία c και d χρησιμοποιούν συστατικό cd.

Εξαρτήσεις μεταξύ γωνιακών ενοτήτων

Η διαφορά μεταξύ ab.module και cd.module είναι ότι το ab.module εισάγεται σε a.module και b.module ενώ το cd.module εισάγεται σε shared.module.

Αυτή η δομή περιγράφει ακριβώς τις αμφιβολίες που θέλαμε να απομυθοποιήσουμε. Ας υπολογίσουμε πού τα modules ab και cd θα είναι στην τελική έξοδο.

Αλγόριθμος

1) Ο αλγόριθμος του SplitChunksPlugin ξεκινά με το να δίνει σε κάθε προηγουμένως δημιουργημένο κομμάτι ένα ευρετήριο.

κομμάτια από δείκτη

2) Στη συνέχεια, βγάζει πάνω από όλες τις μονάδες στη σύνταξη για να γεμίσει το χάρτη chunkSetsInGraph. Αυτό το λεξικό δείχνει ποια κομμάτια έχουν τον ίδιο κωδικό.

chunkSetsInGraph

Π.χ. 1,2 κύρια, πολυφυλλική σειρά σημαίνει ότι υπάρχει τουλάχιστον μία μονάδα που εμφανίζεται σε δύο κομμάτια: main και polyfill.

Οι μονάδες a και b μοιράζονται τον ίδιο κώδικα από την υπομονάδα -ab, ώστε να παρατηρήσουμε επίσης τον συνδυασμό (4.5) παραπάνω.

3) Περιηγηθείτε σε όλες τις μονάδες και υπολογίστε αν είναι δυνατό να δημιουργήσετε ένα νέο κομμάτι για μια συγκεκριμένη ομάδα cacheGroup.

3α) Πρώτα απ 'όλα, το webpack καθορίζει εάν μια ενότητα μπορεί να προστεθεί σε συγκεκριμένη ομάδα cacheGroup ελέγχοντας την ιδιότητα cacheGroup.test.

ab.module δοκιμές

προκαθορισμένη δοκιμή undefined => ok
κοινή δοκιμή undefined => ok
λειτουργία δοκιμής προμηθευτή => ψευδής

η προεπιλεγμένη και κοινή ομάδα προσωρινής μνήμης δεν ορίζει την ιδιότητα δοκιμής, οπότε πρέπει να την μεταβιβάσει. η ομάδα προσωρινής μνήμης προμηθευτή ορίζει μια λειτουργία όπου υπάρχει ένα φίλτρο που περιλαμβάνει μόνο τις μονάδες από τη διαδρομή thenode_modules.

Οι δοκιμές cd.module είναι οι ίδιες.

3β) Τώρα είναι καιρός να περπατήσετε σε όλους τους συνδυασμούς τεμαχίων.

Κάθε ενότητα κατανοεί σε ποιά κομμάτια εμφανίζεται (χάρη στην ιδιότητα module.chunksIterable).

ab.module εισάγεται σε δύο τεμπέλης κομμάτια. Έτσι οι συνδυασμοί είναι (4), (5) και (4,5).

Από την άλλη πλευρά, το cd.module εισάγεται μόνο στην ενωμένη ενότητα, που σημαίνει ότι εισάγεται μόνο στο βασικό τεμάχιο. Οι συνδυασμοί του είναι μόνο (1).

Στη συνέχεια, οι φίλτρο των φίλτρων συνδυάζονται με το μέγεθος minChunk:

αν (chunkCombination.size 

Δεδομένου ότι ο ab.module έχει τον συνδυασμό (4,5) θα πρέπει να περάσει αυτόν τον έλεγχο. Αυτό δεν μπορούμε να πούμε για το cd.module. Σε αυτό το σημείο, αυτή η ενότητα παραμένει να ζει μέσα στο κύριο κομμάτι.

3c) Υπάρχει ένας ακόμη έλεγχος από cacheGroup.chunkds (αρχική, async ή όλα)

Το ab.module εισάγεται μέσα στα async (τεμπέλης φορτωμένα) κομμάτια. Αυτό ακριβώς απαιτούν οι προεπιλεγμένες και οι κοινές ομάδες κρυφής μνήμης. Αυτός ο τρόπος ab.module προστίθεται σε δύο νέα πιθανά κομμάτια (προεπιλεγμένα και κοινά).

Το υποσχέθηκα νωρίτερα, οπότε πηγαίνουμε εδώ.

Πώς δημιουργεί το webpack το όνομα για ένα τεμάχιο που δημιουργήθηκε από το SplitChunksPlugin;

Η απλουστευμένη εκδοχή αυτής μπορεί να αναπαρασταθεί ως

που:

  • groupName είναι το όνομα της ομάδας (προεπιλογή στην περίπτωσή μας)
  • ~ είναι ένα defaultAutomaticNameDelimiter
  • το chunkNames αναφέρεται στη λίστα με όλα τα ονόματα κομματιών που περιλαμβάνονται σε αυτήν την ομάδα. Το όνομα αυτό είναι σαν μια διαδρομή πλήρους διαδρομής, αλλά αντί για κάθετο που χρησιμοποιεί -.

Για παράδειγμα, η dd-module σημαίνει ότι έχουμε αρχείο d.module στο φάκελο d.

Έτσι, έχοντας χρησιμοποιήσει την εισαγωγή ('./ a / a.module') και την εισαγωγή ('./ b / b.module') παίρνουμε

Δομή του προεπιλεγμένου ονόματος κομματιού

Ένα ακόμα πράγμα που αξίζει να αναφερθεί είναι ότι όταν το μήκος ενός κομματιού φτάσει τους 109 χαρακτήρες, το webpack το κόβει και προσθέτει κάποιες κατακερματισμούς στο τέλος.

Δομή του ονόματος μεγάλου κομματιού που μοιράζεται κώδικα σε πολλαπλές μονάδες τεμπέλης

Είμαστε έτοιμοι να γεμίσουμε το chunksInfoMap το οποίο ξέρει όλα για όλα τα πιθανά νέα κομμάτια και επίσης γνωρίζει ποιες ενότητες θα πρέπει να συνθέτουν και τα σχετικά κομμάτια όπου τα εν λόγω τμήματα βρίσκονται επί του παρόντος.

chunksInfoMap

Ήρθε η ώρα να φιλτράρετε πιθανά κομμάτια

SplitChunksPlugin βρόχους πάνω από τα στοιχεία chunksInfoMap για να βρείτε την καλύτερη καταχώρηση που ταιριάζει. Τι σημαίνει?

η προεπιλεγμένη ομάδα κρυφής μνήμης έχει προτεραιότητα 10, η οποία υπερβάλλει τα κοινά (που έχει μόνο 5). Αυτό σημαίνει ότι η προεπιλογή είναι η καλύτερη καταχώρηση που ταιριάζει και πρέπει πρώτα να γίνει επεξεργασία.

Μόλις ικανοποιηθούν όλες οι άλλες απαιτήσεις, το webpack αφαιρεί όλες τις μονάδες του τμήματος από άλλα πιθανά κομμάτια στο λεξικό chunksInfoMap. Εάν δεν υπάρχει κάποια υπομονάδα, η μονάδα διαγράφεται

Με αυτόν τον τρόπο η προεπιλεγμένη ~ aa-module ~ bb-module υπερισχύει έναντι του συνήθους κομματιού. Ο τελευταίος αφαιρείται αφού περιέχει τον ίδιο κατάλογο μονάδων.

Τελευταίο αλλά όχι λιγότερο σημαντικό βήμα είναι να κάνετε κάποιες βελτιστοποιήσεις (όπως την αφαίρεση επαναλήψεων) και να βεβαιωθείτε ότι πληρούνται όλες οι απαιτήσεις όπως το maxSize.

Ολόκληρος ο πηγαίος κώδικας του SplitChunksPlugin μπορεί να βρεθεί εδώ

Ανακαλύψαμε ότι το webpack δημιουργεί κομμάτια με τρεις διαφορετικούς τρόπους:

  • για κάθε είσοδο
  • για δυναμικά φορτισμένα στοιχεία
  • για κοινόχρηστο κώδικα με τη βοήθεια του SplitChunksPlugin
Γωνιακή έξοδος CLI ανά τύπο τεμαχίων

Τώρα ας επιστρέψουμε στις αμφιβολίες μας για το ποιος είναι ο καλύτερος τρόπος για να διατηρήσετε κοινόχρηστο κώδικα.

Πώς να μοιράζεστε εξαρτήματα μεταξύ των τεμπέλης μονάδων

Όπως έχουμε δει στην απλή γωνιακή εφαρμογή μας, η webpack δημιούργησε χωριστό κομμάτι για ab.module αλλά συμπεριέλαβε cd.module στο κύριο κομμάτι.

Ας συνοψίσουμε τις βασικές λήψεις από αυτήν την ανάρτηση:

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

συμπέρασμα

Ελπίζω τώρα ότι θα πρέπει να κατανοήσετε ξεκάθαρα την έξοδο του γωνιακού CLI και να διαχωρίσετε ανάμεσα στην είσοδο, τη δυναμική και τη διαίρεση χρησιμοποιώντας κομμάτια SplitChunksPlugin.

Ευτυχισμένη κωδικοποίηση!