Webpack 5 module federacija

Kas ta Module Federation ir kodėl ji svarbi?

Kai Webpack 5 pristatė Module Federation funkciją, daugelis developerių net nesuprato, kokį revoliucinį įrankį gavo į rankas. Paprasčiausiai tariant, tai galimybė dalintis JavaScript moduliais tarp skirtingų aplikacijų runtime metu – ne per npm paketus, ne per monorepo, o tiesiogiai veikiančioje aplinkoje. Skamba kaip magija? Iš dalies taip ir yra.

Įsivaizduokite situaciją: turite kelias frontend aplikacijas – gal mikroservisų architektūrą, gal kelias atskiras sistemas, kurios turi veikti kartu. Tradiciškai kiekviena aplikacija būtų atskirai sukompiliuota, turėtų savo dependencies, ir jei norėtumėte pasidalinti kokiu nors komponentu – turėtumėte jį iškelti į npm paketą, publikuoti, versijuoti. Module Federation šį procesą apverčia aukštyn kojomis.

Kaip tai veikia praktiškai?

Module Federation leidžia vienai aplikacijai (vadinamai „host”) dinamiškai importuoti modulius iš kitos aplikacijos (vadinamai „remote”). Svarbiausia čia – tai vyksta runtime, ne build time. Tai reiškia, kad jūsų aplikacija gali užkrauti komponentą iš kitos aplikacijos tik tada, kai jo reikia, ir tas komponentas bus visada naujausios versijos.

Konfigūracija atrodo gana paprasta. Pirmiausia, turite sukonfigūruoti „remote” aplikaciją – tą, kuri eksportuos modulius:


const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "remoteApp",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/components/Button",
"./Header": "./src/components/Header"
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true }
}
})
]
};

Čia svarbiausios dalys: name – unikalus jūsų remote aplikacijos vardas, filename – failas, kuris bus entry point į jūsų eksportuojamus modulius, exposes – kas bus prieinama kitiems, ir shared – bendros priklausomybės.

Host aplikacijoje konfigūracija panašiai paprasta:


new ModuleFederationPlugin({
name: "hostApp",
remotes: {
remoteApp: "remoteApp@http://localhost:3001/remoteEntry.js"
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true }
}
})

Shared dependencies – čia slypi paslaptys

Viena didžiausių Module Federation galių yra shared dependencies valdymas. Kai turite kelias aplikacijas, kurios naudoja React, nebūtų protinga, kad kiekviena užkrautų savo React kopiją. Module Federation tai supranta ir leidžia dalintis priklausomybėmis.

Tačiau čia reikia būti atsargiam. singleton: true parametras užtikrina, kad bus naudojama tik viena bibliotekos versija. Tai kritiškai svarbu tokioms bibliotekoms kaip React, kur kelios versijos tiesiog neveiks kartu. Bet kas nutinka, jei viena aplikacija naudoja React 17, o kita React 18?

Čia įsiterpia requiredVersion ir strictVersion parametrai:


shared: {
react: {
singleton: true,
requiredVersion: "^17.0.0",
strictVersion: false
}
}

Jei strictVersion nustatytas į false, Webpack bandys naudoti bet kurią versiją, net jei ji neatitinka reikalavimų. Jei true – aplikacija tiesiog neveiks. Praktikoje dažniausiai naudojama false su įspėjimais konsolėje, nes visiškas griežtumas gali sukelti daugiau problemų nei naudos.

Dinaminiai remote’ai ir runtime konfigūracija

Vienas įdomiausių Module Federation aspektų – galimybė dinamiškai nustatyti remote aplikacijų URL. Tai ypač naudinga, kai turite skirtingas aplinkas (dev, staging, production) arba kai remote aplikacijų adresai nėra žinomi build metu.

Galite naudoti specialų init metodą:


const loadRemote = (url, scope, module) => {
return async () => {
await __webpack_init_sharing__("default");
const container = window[scope];
await container.init(__webpack_share_scopes__.default);
const factory = await container.get(module);
return factory();
};
};

Arba dar paprasčiau – naudoti promise-based remote:


remotes: {
remoteApp: `promise new Promise(resolve => {
const remoteUrl = getRemoteUrl(); // jūsų funkcija
const script = document.createElement('script');
script.src = remoteUrl;
script.onload = () => {
resolve({
get: (request) => window.remoteApp.get(request),
init: (arg) => window.remoteApp.init(arg)
});
};
document.head.appendChild(script);
})`
}

Taip, atrodo šiek tiek baisoka, bet veikia puikiai ir suteikia maksimalią lankstumą.

Tipinės problemos ir kaip jų išvengti

Dirbant su Module Federation, susidursite su keletu klasikinių problemų. Pirmoji – CORS. Jei jūsų remote aplikacija yra kitame domene, privalote tinkamai sukonfigūruoti CORS headers. Tai atrodo akivaizdu, bet stebėtinai dažnai apie tai pamirštama.

Antroji problema – versijų konfliktai. Jei turite sudėtingą priklausomybių medį, gali atsitikti, kad skirtingos aplikacijos reikalauja nesuderinamų bibliotekų versijų. Čia padeda keletas strategijų:

  • Naudokite peerDependencies savo shared moduliuose
  • Reguliariai atnaujinkite visas aplikacijas į tas pačias major versijas
  • Stebėkite bundle analyzer output – pamatysite, kas iš tikrųjų yra įtraukiama
  • Naudokite eager: true kritinėms priklausomybėms, kurias norite užkrauti iš karto

Trečioji problema – TypeScript tipai. Kai importuojate komponentą iš remote aplikacijos, TypeScript neturi pojūčio, kas ten yra. Sprendimas – eksportuoti tipus atskirai ir dalintis jais per npm arba naudoti type generation įrankius.

Performance optimizacijos

Module Federation gali būti neįtikėtinai greitas arba skausmingai lėtas – priklausomai nuo to, kaip jį naudojate. Štai keletas praktinių patarimų:

Lazy loading viskas. Nenaudokite statinių importų remote moduliams. Visada naudokite React.lazy() arba dynamic import():


const RemoteButton = React.lazy(() => import("remoteApp/Button"));

Prefetch strategijos. Jei žinote, kad vartotojas greičiausiai naudos tam tikrą funkciją, galite prefetch’inti remote modulį:


const link = document.createElement("link");
link.rel = "prefetch";
link.href = "http://localhost:3001/remoteEntry.js";
document.head.appendChild(link);

Cache strategijos. Naudokite content hash’us failų varduose ir ilgus cache header’ius. Module Federation puikiai veikia su CDN ir browser cache.

Chunk splitting. Neeksportuokite visko viename bundle. Skaidykite į mažesnius chunks, kad vartotojas užkrautų tik tai, ko reikia:


exposes: {
"./Button": "./src/components/Button",
"./ComplexFeature": "./src/features/ComplexFeature",
"./utils": "./src/utils"
}

Realūs panaudojimo scenarijai

Teorija teorija, bet kur tai naudoti praktiškai? Štai keletas scenarijų, kur Module Federation tikrai spindi:

Mikrofront-endai. Tai akivaizdžiausias use case. Turite skirtingas komandas, kurios kuria skirtingas sistemos dalis. Kiekviena komanda palaiko savo aplikaciją, bet jos visos gali dalintis komponentais ir funkcionalumu.

Design system. Vietoj to, kad publikuotumėte design system komponentus kaip npm paketą ir lauktumėte, kol visos aplikacijos atsinaujins, galite juos eksportuoti per Module Federation. Atnaujinimai tampa instant.

A/B testing. Galite turėti kelias feature versijas skirtinguose remote’uose ir dinamiškai pasirinkti, kurią užkrauti pagal vartotojo segmentą.

White-label sprendimai. Jei kuriate platformą, kurią klientai gali customizuoti, jie gali pateikti savo remote modulius su custom funkcionalumu.

Vienas įdomus pavyzdys iš praktikos – e-commerce platforma, kur pagrindinis shell yra host, o kiekviena produkto kategorija (elektronika, drabužiai, maistas) yra atskiras remote. Tai leidžia skirtingoms komandoms nepriklausomai vystyti savo sritis, bet vartotojui viskas atrodo kaip viena seamless aplikacija.

Kai viskas susirenka į vieną vietą

Module Federation nėra silver bullet, bet tai vienas galingiausių įrankių šiuolaikinio frontend architektūros arsenale. Jis iššaukia tradicinį mąstymą apie tai, kaip kuriame ir deployiname aplikacijas.

Pradedant naudoti, rekomenduoju pradėti nuo mažo – gal vieno ar dviejų shared komponentų tarp dviejų aplikacijų. Išbandykite, kaip veikia jūsų deployment pipeline, kaip elgiasi versijų valdymas, kaip jaučiasi performance. Tik tada plėskite į sudėtingesnius scenarijus.

Svarbu suprasti, kad Module Federation reikalauja kitokio mąstymo apie aplikacijų architektūrą. Jūs nebegalvojate apie vieną monolitinį bundle, bet apie distributed sistemą, kur skirtingos dalys gali būti deployinamos nepriklausomai. Tai suteikia laisvės, bet ir reikalauja disciplinos.

Dokumentacija ir community support auga, bet dar nėra tokio lygio kaip tradicinėms Webpack features. Būkite pasirengę kartais ieškoti sprendimų GitHub issues ar eksperimentuoti patys. Ir tikrai naudokite webpack-bundle-analyzer – jis taps jūsų geriausiu draugu bandant suprasti, kas vyksta su jūsų bundles.

Galiausiai, Module Federation atidaro duris į tikrai įdomią ateitį, kur aplikacijos gali būti kuriamos kaip sudedamosios dalys, kurias galima keisti ir atnaujinti nepriklausomai. Tai ypač aktualu dabar, kai komandos dirba remote, kai sistemos tampa vis sudėtingesnės, ir kai greitis rinkoje tampa kritiškai svarbus. Verta išbandyti ir pažiūrėti, ar tai tinka jūsų projektui.

Daugiau

Python FastAPI ir Django: našumo testas