2024-07-08
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Pääprosessin lisäksi selitämme yksityiskohtaisesti useita epämääräisiä käsitteitä:
edessä Init, Make, Seal"Vuorossa ", olemme esittäneet, että Webpackin taustalla oleva rakennuslogiikka voidaan karkeasti jakaa: "Alustus, rakentaminen, pakkaus"kolme vaihetta:
sisään,"Rakentaa"Vaihe on vastuussa moduulien välisten riippuvuuksien analysoinnista ja niiden määrittämisestä Riippuvuuskaavio(ModuleGraph);kapselointi”-vaiheessa riippuvuusgraafin perusteella moduulit kapseloidaan erikseen useiksi Chunk-objekteiksi ja Chunkien väliset ylä-lapsi-riippuvuussuhteet lajitellaan ChunkGraph- ja useisiin ChunkGroup-objekteihin.
"Kapselointi"-vaiheen tärkein tavoite on rakentaa ChunkGraph-suhdekaavio "koonti"-vaiheessa kerätyn ModuleGraph-suhdegraafin perusteella. Tämän prosessin logiikka on suhteellisen monimutkainen:
Analysoidaan tässä lyhyesti useiden tärkeiden vaiheiden toteutuslogiikkaa.
Ensimmäinen askel on erittäin kriittinen: siirtääseal()
Tee toiminnon jälkeen poikkientry
Määritys, luo tyhjä jokaiselle merkinnälleChunk
jaSisääntulopiste esine (erityinenChunkGroup
) ja määritä ensin perusasetukset ChunkGraph
Rakennesuhde, valmistaudu seuraavaan vaiheeseen, avainkoodi:
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);
// 触发各种优化钩子
// ...
}
}
Kun suoritus on valmis, muodostetaan seuraava tietorakenne:
Toiseksi, jos se on määritetty tällä hetkellä entry.runtime
, Webpack tarjoaa myös ajonaikaisen koodin tässä vaiheessa luoda Vastaava pala ja suoraanjakaa Antaaentry
vastaavaChunkGroup
esine.Soitetaan kun kaikki on valmistabuildChunkGraph toiminto, siirry seuraavaan vaiheeseen.
Vaihe kaksi: olla olemassabuildChunkGraph
toiminnon sisälläsiirtää visitModules
Funktio, kulje ModuleGraph ja osoita kaikki moduulit eri moduuleille niiden riippuvuuksien mukaan.Chunk
Objekti, jos se havaitaan tämän prosessin aikanaasync-moduuli, sitten moduuli luodaUusi ChunkGroup
jaChunk
Objekti, joka lopulta muodostaa seuraavan tietorakenteen:
kolmas vaihe: olla olemassabuildChunkGraph
toiminnassasiirtää connectChunkGroups
menetelmä, perustaaChunkGroup
välillä,Chunk
toistensa välisiä riippuvuuksia luodakseen täydellisenChunkGraph
Objekti, joka lopulta muodostaa seuraavan tietorakenteen:
neljäs vaihe: olla olemassabuildChunkGraph
toiminnassasiirtää cleanupUnconnectedGroups
Menetelmä, puhdistus on virheellinenChunkGroup
, vaikuttaa pääasiassa suorituskyvyn optimointiin.
Kun olet käynyt nämä neljä vaihetta ylhäältä alas,ModuleGraph
Kohteeseen tallennetut moduulit määritetään kolmelle eri Chunk-objektille: Entry, Async ja Runtime moduulin luonteen mukaan, ja Chunks-riippuvuudet tallennetaan ChunkGraph- ja ChunkGroup-kokoelmiin. Voit jatkaa näiden objektien perusteella muuttaa myöhemmin alihankintakäytäntöjä (esim.SplitChunksPlugin
), toteuttaa alihankintaoptimoinnin uudelleenorganisoimalla ja jakamalla Moduuli- ja Chunk-objektien omistajuutta.
Yllä oleva rakennusprosessi sisältää kolme avainobjektia: Chunk, ChunkGroup ja ChunkGraph. Tehdään ensin yhteenveto niiden käsitteistä ja toiminnoista ymmärryksemme syventämiseksi:
Chunk
: Moduulia käytetään lukemaan moduulien sisältöä, tallentamaan moduulien välisiä riippuvuuksia jne., kun taas Chunk yhdistää useita moduuleja moduuliriippuvuuksien perusteella ja tulostaa ne omaisuustiedostoiksi (tuotteiden yhdistämisen ja tulostamisen logiikka selitetään seuraavassa luvussa):ChunkGroup
:yksi ChunkGroup
sisältää yhden tai useammanChunk
esine;ChunkGroup
jaChunkGroup
Vanhemman ja lapsen riippuvuussuhde muodostuu:ChunkGraph
: Lopuksi Webpack tallentaa Chunks- ja ChunkGroupsien väliset riippuvuudet sisään compilation.chunkGraph
Objektissa muodostetaan seuraavat tyyppisuhteet:Ylempi ChunkGraph
Rakennusprosessi järjestää lopulta moduulin kolmeen eri tyyppiin:
entry
Alemmalla kosketuksella saavutetut moduulit on järjestetty osaksi;entry.runtime
Kun ajonaikainen moduuli ei ole tyhjä, se järjestetään osaksi.Tämä on sisäänrakennettu Webpackiin, jos sitä ei käytetä splitChunks
Muiden lisäosien tapauksessa oletussäännöt moduulin syötteen ja tulosteen yhdistämiselle ovat yksi Webpackin tärkeimmistä perusperiaatteista, joten on tarpeen ottaa käyttöön kunkin Chunkin erityiset säännöt.
Sisäänpääsyosa:
Alkaen Entry Chunkista, Webpack ensin entry
luodaChunk
Objektoi esimerkiksi seuraava kokoonpano:
module.exports = {
entry: {
main: "./src/main",
home: "./src/home",
}
};
Traverse entry
objektin ominaisuudet ja luochunk[main]
、chunk[home]
Kaksi esinettä, jotka tällä hetkellä sisältävät vastaavasti kaksi kappalettamain
、home
Moduuli:
Kun alustus on valmis, Webpack tekee sen ModuleGraph
riippuvuustiedot, testamenttientry
Kaikki moduulit, joihin seuraavat koskettavat, on täytetty Chunkiin (estyyvisitModules menetelmä), esimerkiksi seuraaville tiedostoriippuvuuksille:
main.js
Jos neljään tiedostoon a/b/c/d viitataan suoraan tai epäsuorasti synkronisesti, Webpackmain.js
Moduuli luo Chunk- ja EntryPoint-objekteja ja lisää sitten vähitellen a/b/c/d-moduulejachunk[main]
, lopulta muodostuu:
Asynkroninen osa:
Toiseksi Webpack kääntää jokaisen asynkronisen tuontilausekkeen (import(xxx)
jarequire.ensure
) käsitellään erillisenä Chunk-objektina, ja kaikki sen alimoduulit lisätään tähän Chunkiin - kutsumme sitä Async Chunkiksi. Esimerkiksi seuraavaa esimerkkiä varten:
// index.js
import './sync-a.js'
import './sync-b.js'
import('./async-a.js')
// async-a.js
import './sync-c.js'
Sisääntulomoduulissa index.js
Synkron-a ja sync-b otetaan käyttöön synkronisesti, samalla kun async-a-moduuli otetaan käyttöön synkronisesti;sync-c
moduuli, muodostaen seuraavan moduuliriippuvuuskaavion:
Tässä vaiheessa Webpack on sisääntulopiste index.js
, asynkroninen moduuli async-a.js
Luo alipaketit erikseen muodostaaksesi seuraavan palarakenteen:
ja chunk[index]
jachunk[async-a]
Niiden välille muodostuu yksisuuntainen riippuvuus, ja Webpack tallentaa tämän riippuvuudenChunkGroup._parents
、ChunkGroup._children
kiinteistöissä.
Ajonaikainen osa:
Lopuksi paitsi entry
, Asynkronisten moduulien lisäksi Webpack5 tukee myös ajonaikaisen koodin purkamista erikseen paloiksi. Tässä mainittu ajonaikainen koodi viittaa joukkoon Webpackin syöttämiä peruskehyskoodeja varmistaakseen, että pakattu tuote voi toimia normaalisti. Esimerkiksi yleinen Webpack-pakatun tuotteen rakenne on seuraava:
Yllä olevan kuvan punaisessa laatikossa ympyröity suuri koodiosa on Webpackin dynaamisesti luoma ajonaikainen koodiDependency
alaluokka), esimerkiksi:
__webpack_require__.f
、__webpack_require__.r
ja muut toiminnot modulaarisen vähimmäistuen saavuttamiseksi;__webpack_require__.e
toiminto;__webpack_require__.o
toiminto;Vaikka jokainen ajonaikaisen koodin osa voi olla pieni, ominaisuuksien lisääntyessä lopputuloksesta tulee suurempi ja suurempi Varsinkin usean merkinnän sovelluksissa on vähän turhaa pakata toistuvasti samanlainen ajonaika jokaiselle merkinnälle entry.runtime
Määrityskohteita käytetään määrittämään, kuinka ajonaikainen koodi on pakattu.Käyttöä varten vain käytäentry
Lisää merkkijonomuoto kohteeseenruntime
arvo, esimerkiksi:
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
}
};
olla olemassa compilation.seal
Toiminnossa Webpack ensin onentry
luodaEntryPoint
, sitten tuomitse entry
Sisältääkö kokoonpanoruntime
Jos sellaisia on, luo ne käyttämälläruntime
Arvo on kappaleen nimi. Siksi yllä oleva esimerkkikokoonpano luo kaksi kappaletta:chunk[index.js]
、chunk[solid-runtime]
, ja tämän perusteella tulostetaan lopulta kaksi tiedostoa:
index.js
asiakirja;solid-runtime.js
asiakirja.monessa entry
Skenaariossa vain jokaiselleentry
Kaikki asetettu samaanruntime
arvoa, Webpack-ajonaikainen koodi yhdistetään ja kirjoitetaan samaan Runtime Chunkiin, mikä lopulta saavuttaa tuotteen suorituskyvyn optimoinnin. Esimerkiksi seuraavalle kokoonpanolle:
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
home: { import: "./src/home", runtime: "solid-runtime" },
}
};
Sisäänkäynti index
、home
jakaa samaaruntime
arvoa ja luo lopuksi kolme kappaletta:
Sisäänkäynti tähän aikaan chunk[index]
、chunk[home]
suoritusajan kanssachunk[solid-runtime]
Muodostuu myös vanhemman ja lapsen huoltosuhde.
Suurin ongelma oletusalipakkaussääntöjen kanssa on, että se ei pysty ratkaisemaan moduulien päällekkäisyyttä. Jos useat osat sisältävät saman moduulin samanaikaisesti, tämä moduuli pakataan toistuvasti näihin osiin ilman rajoituksia. Oletetaan esimerkiksi, että meillä on kaksi merkintää pää/indeksi, jotka molemmat riippuvat samasta moduulista:
Oletuksena Webpack ei käsittele tätä lisäkäsittelyä, vaan yksinkertaisesti pakkaa c-moduulin pää-/indeksiosaan samanaikaisesti kahteen osaan, jolloin lopulta muodostuu:
voidaan nähdä chunk
on eristetty toisistaan, moduuli c pakataan toistuvasti, mikä voi aiheuttaa tarpeettomia suorituskyvyn menetyksiä lopputuotteelle!
Tämän ongelman ratkaisemiseksi esiteltiin Webpack 3 CommonChunkPlugin
Plugin yrittää purkaa yhteiset riippuvuudet merkintöjen välillä erillisiksichunk
,mutta CommonChunkPlugin
Se toteutetaan pääosin yksinkertaisen vanhemman ja lapsen välisen suhdeketjun perusteella. On vaikea päätellä, että purettua kolmatta pakettia tulisi käyttääentry
isächunk
Vielä lapsichunk
,CommonChunkPlugin
yhtenäinen käsittely vanhempinachunk
, joissakin tapauksissa sillä on huomattava negatiivinen vaikutus suorituskykyyn.
Tästä syystä Webpack 4:n jälkeen otettiin käyttöön monimutkaisempia tietorakenteita. ChunkGroup
Erikoistunut suhdeketjun hallinnan ja yhteistyön toteuttamiseenSplitChunksPlugin
voidaan toteuttaa tehokkaammin ja älykkäämminHeuristinen alihankinta.
Yhteenvetona voidaan todeta, että "Build"-vaihe vastaa ModuleGraphin rakentamisesta moduulin viitesuhteen perusteella. "Encapsulation"-vaihe on vastuussa ModuleGraphiin perustuvien Chunk-objektien sarjan rakentamisesta ja osien välisten riippuvuuksien järjestämisestä (asynkroniset viittaukset, ajonaika); ) ChunkGraph - Chunk Dependency -graafiobjektiin. Samoin kuin ModuleGraph, ChunkGraph-rakenteen käyttöönotto voi myös irrottaa Chunksien välisten riippuvuuksien hallintalogiikan, mikä tekee kokonaisarkkitehtuurilogiikasta järkevämmän ja helpommin laajennettavan.
Kuitenkin, vaikka se näyttää hyvin monimutkaiselta, "pakkaus"-vaiheen tärkein tavoite on silti määrittää kuinka monta kappaletta on ja mitkä moduulit sisältyvät jokaiseen palaan - nämä ovat avaintekijöitä, jotka todella vaikuttavat lopulliseen pakkaustulokseen.
Tätä varten meidän on ymmärrettävä Webpack5:n kolme sisäänrakennettua alihankintasääntöä: Entry Chunk, Async Chunk ja Runtime Chunk Nämä ovat alkuperäisin alihankintalogiikka Muut laajennukset (kuten splitChunksPlugin) perustuvat kaikki tähän, avulla buildChunkGraph
Erilaiset koukut, jotka laukesivat myöhemmin edelleen jakavat, yhdistävät ja optimoivat Chunk-rakennetta laajentaakseen alihankintavaikutuksia.
ajatella Chunk
Tuotetaanko vain yksi tuotetiedosto? Miksi?mini-css-extract-plugin
、file-loader
Miten tämän tyyppinen komponentti, joka voi kirjoittaa lisätiedostoja, on toteutettu alareunassa?