Nagtapos ang post 10 sa isang pangako: magiging federated singleton ang design system, isang UI package na shared sa runtime, para ang bawat remote ay magre-render ng parehong mga component nang hindi nagdadala ng sariling kopya. Tinutupad iyon ng post na ito.
Malinaw ang technical debt. Mula pa noong post 4, bawat screen ay nag-aayos ng sarili nitong itsura gamit ang StyleSheet.create at mga hex na nakasulat nang mano-mano, at umaabot na ang duplication sa tatlong app at sa view package: ang pare-parehong grey mula sa template ay nasa list app, sa party app at sa detail view, at ang parehong asul na accent ay lumalabas sa Refresh button ng host at sa Add button ng detail. Apat na codebase, apat na kopya ng parehong mga desisyon sa disenyo, at walang paraan para baguhin ang alinman sa iisang lugar.
Sa UI inilalapat ng solusyon ang dalawang pattern na ginamit na ng serye. Nag-share ang post 3 ng mga library bilang singleton para isang kopya ang magsilbi sa lahat ng app. Naglagay ang mga post 5 at 6 ng mga package sa seam para iisang definition ang ini-install ng bawat panig.
Ginagawa ng @pokedex/ui ang dalawa. Ibinabalot nito ang mga copy-in component ng gluestack-ui, ini-istilo ang mga ito gamit ang NativeWind 4.2.6, at pino-publish nito sa lokal na Verdaccio registry. Pagkatapos, ibinibigay ng Module Federation sa bawat remote ang kopya ng host sa runtime. Ang build na ito ay gumagamit ng @gluestack-ui/core sa ^3.0.25, @gluestack-ui/utils sa ^3.0.21 at Tailwind 3.4.19.
Tatlong package na ang nasa seam, at hindi pare-pareho ang pagtawid nila. Ang contracts at ang design system ay nagre-resolve sa iisang kopya sa runtime; sa diagram, ang host ang nagpo-provide sa kanila. Sadyang hindi nagre-resolve ang detail package sa iisang shared na kopya: bawat consumer ay may hawak na sariling version pin, at iyon na ang punto nito mula pa noong post 5. Ang kaibahang iyon ang puso ng post na ito.
Isang package para sa mga pixel
Magsimula sa tag ng post 10:
git clone https://github.com/warrendeleon/react-native-module-federation
cd react-native-module-federation
git checkout post-10-two-backends
May isang praktikal na limitasyon bago talakayin ang structure. Ang source ng design system ay mas malaki kaysa sa kayang dalhin ng isang post: labintatlong kinopyang gluestack component, mga token map para sa labingwalong type, labing-isang composed component. Kaya ang mga package ng post na ito ay kinukuha mula sa tapos nang tag sa halip na i-type, kasama na ang mga bagong bersyon ng contracts at detail, dahil ina-update ng post na ito ang parehong package (Tapos na ang bihis ng detail ang magsasabi kung paano). Kunin ang reference copy nang isang beses (gagamitin itong muli ng Patakbuhin para sa mga native asset) at kunin ang tatlong package:
npx degit@3.8.0 --force warrendeleon/react-native-module-federation#post-11-design-system /tmp/pokedex-ref
rm -rf packages/ui packages/contracts packages/detail
cp -R /tmp/pokedex-ref/packages/ui /tmp/pokedex-ref/packages/contracts /tmp/pokedex-ref/packages/detail packages/
Sinusunod ng packages/ui ang anatomy na itinakda ng mga post 5 at 6: nagbi-build ang react-native-builder-bob ng ESM na lib/, ang "react-native": "./src/index.ts" ay nagpapadala ng raw na source para ang kino-compile ng Re.Pack ay ang mismong mga file na hawak ng repo, nakaturo ang publishConfig sa lokal na Verdaccio, at ang files list ay may dagdag na dalawang entry na hindi kailanman kinailangan ng mga naunang package: tailwind.preset.js at global.css.
Kinokopya ng gluestack CLI ang source ng mga component papunta sa src/components/ui/. Sa copy-in, ikaw ang may-ari ng source: ang package mo ang nagpa-publish ng code na iyon, at hindi kusang dumarating ang mga update mula sa upstream. Kasama sa companion repo ang labintatlong primitive na ginagamit ng mga screen na ito, mula box hanggang vstack.
May tagong dependency ang isa sa mga kopya. Nag-i-import ang heading ng @expo/html-elements, na hindi dinedeklara ng na-generate na package. Idagdag mo ito sa dependencies; kung hindi, matatapos ang install nang walang reklamo at lilitaw ang pagkabigo mamaya, kapag narating na ng bundler ng consumer ang import na iyon at hindi nito makita ang module.
Sa ibabaw ng mga primitive ang mga token at ang mga composed component. Dala ng tailwind.preset.js ang mga brand at neutral na kulay, kasama ang type.<name> para sa lahat ng labingwalong type ng Pokémon. Ang parehong mga value ay nasa src/tokens/ para sa runtime code. Labing-isang component ang gumagamit sa kanila: ScreenContainer, PokemonCard, TypeBadge, LoadingState, ErrorState, InfoRow, StatBar, EmptySlot, BackPill, ThemeToggle at Toaster. Dahil sa primitives.ts barrel, iisang package surface lang ang ini-import ng consumers.
May dalawang desisyon na dapat nasa tabi ng mga token, at ang pag-iwan ng mga ito sa mga component ay nagdudulot ng tahimik na pagpalya.
Class lang mula sa literal na string ang ginagawa ng Tailwind. Ang binuong
`bg-type-${t}`ay hindi nakikita ng scanner ng Tailwind, at ang kulay, walang anumang babala, ay hindi kailanman aabot sa app. Ang token file ay nagsusulat ng bawat class sa mga literal na map (TYPE_BG_CLASSat mga kasama) at nag-e-export ng mga lookup helper, kaya ang buong palette ay nasa iisang lugar na madaling i-audit.
Ang pangalawa ay ang contrast ng text. Kailangang piliin ang text colour ayon sa nasukat na contrast, hindi sa hula ng mata. Ang puti sa asul ng water ay 3.48:1 ang sukat at sa pink ng psychic ay 3.41:1, mas mababa sa minimum ng WCAG na 4.5:1 para sa maliit na text. Lampas 6:1 ang itim sa pareho. Isang beses lang kinakalkula ng textOnTypeClass() ang kulay na may mas mataas na contrast, at ginagamit iyon ng bawat surface na may kulay ng type. Ang itim na text na naka-hard-code ay mag-iiwan sa hero ng fighting type sa 1.7:1.
May isang uri ng input na hinding-hindi talaga maita-type: ang mga imahe. Ang buwan at araw ng ThemeToggle at ang arrow ng BackPill ay mga PNG file sa src/assets, na sumama sa kopya, at isinasama sila ng files list sa na-publish na package.
I-publish ito gaya ng ginawa ng mga post 5 at 6, kasama ang install step:
( cd packages/ui && npm install && npm run build && npm publish )
Ang bersyong 1.0.2 ay ang binalak na 1.0.0 API kasama ang mga patch na idinagdag ng mga sumunod na hardening pass.
Tapos na ang bihis ng detail
Kailangan ng final detail ang data na wala sa dating contract. Nagdadagdag ang Contracts 3.2.2 ng heightM, weightKg, abilities, stats at opsyonal na flavourText sa PokemonDetail. Nagdadagdag din ito ng types sa PartyMember, para makulayan ng party ang mga card nito nang walang dagdag na request. Kino-convert ng parsePokemonDetail ang mga unit ng PokéAPI at nino-normalize ang flavour text nang isang beses sa boundary.
Parehong kinukuha na ngayon ng dalawang remote ang species payload kasabay ng Pokémon, at ipinapasa ng list ang type data kapag nagdadagdag ito ng member. Ang mga pagbabagong iyon ay dala ng app-file sweep sa Pag-configure ng styling runtime sa isang federated na build. Ang source ng mga bagong package ay kasama na sa reference copy, kaya i-publish ang contracts at detail bago sila i-install ng mga app:
( cd packages/contracts && npm install && npm run build && npm publish )
( cd packages/detail && npm install && npm run build && npm publish )
Muling itinatayo ng @pokedex/detail 4.0.2 ang view sa palibot ng design system: isang collapsing hero na may kulay ng type, isang sprite na may singsing, ang flavour text, mga Info row, mga Base Stats at ang Add button. Major ito dahil nagdadagdag ito ng required na peer dependencies: nagiging requirement sa bawat consumer ang @pokedex/ui, NativeWind at Reanimated.
Hindi nagbabago ang mga prop nito. Tinatanggap pa rin ng view ang { pokemon, loading, error, onRetry, onAddToParty, addDisabled, addLabel }, kaya lumilipat ang dalawang container sa bagong view sa pamamagitan lang ng pagpapalit ng version pin. Sapat na ang isang npm install para makuha ng dalawang consumer ang pinakamalaking visual na pagbabago ng serye.
Ang collapse ang tatak na galaw ng screen na ito, at sulit itong makita bago ang seksyon ng pag-configure, dahil bawat kulay dito ay isang token class na nire-resolve sa seam:
Pag-configure ng styling runtime sa isang federated na build
Metro ang saklaw ng mismong docs ng NativeWind; nasa pahina ng community-driven solutions ang Re.Pack. Mula sa kabilang panig, may sariling plugin ang Callstack, ang @callstack/repack-plugin-nativewind, na naka-lock ang bersyon sa naka-install na Re.Pack (5.2.5 dito). Kailangan ng Reanimated ang parehong pagtrato: natutukoy ng Re.Pack ang package at nagbabala kapag walang ReanimatedPlugin, kaya kasama nitong pumapasok ang @callstack/repack-plugin-reanimated sa parehong bersyon. Parehong tama ang dalawang source, magkaiba lang ang panig na sinasaklaw nila, at ang pag-configure na ito ang setup na documented ng plugin.
Ini-install sa lahat ng tatlong app ang styling dependencies. Ini-install din ng dalawang remote ang detail 4.0.2 at contracts 3.2.2. Bawat app ay direktang nagdedeklara ng gluestack core at utils dahil ini-import sila ng kinopyang source. Bawat app ay nagpi-pin din ng react at react-dom sa sariling bersyon ng React, 19.2.3, para hindi makapag-install ng pangalawang React ang peer resolution.
( cd apps/host && npm pkg set overrides.react=19.2.3 overrides.react-dom=19.2.3 && npm install nativewind@4.2.6 @gluestack-ui/core@3.0.25 @gluestack-ui/utils@3.0.21 react-native-reanimated@4.6.0 react-native-worklets@0.12.1 @pokedex/ui@1.0.2 @pokedex/contracts@3.2.2 && npm install -D tailwindcss@3.4.19 @callstack/repack-plugin-nativewind@5.2.5 @callstack/repack-plugin-reanimated@5.2.5 postcss@8.4.47 postcss-loader@8.1.1 autoprefixer@10.4.20 @module-federation/enhanced@2.9.0 @react-native-community/cli@20.2.0 @react-native-community/cli-platform-android@20.2.0 @react-native-community/cli-platform-ios@20.2.0 )
( cd apps/list && npm pkg set overrides.react=19.2.3 overrides.react-dom=19.2.3 && npm install nativewind@4.2.6 @gluestack-ui/core@3.0.25 @gluestack-ui/utils@3.0.21 react-native-reanimated@4.6.0 react-native-worklets@0.12.1 @pokedex/ui@1.0.2 @pokedex/contracts@3.2.2 @pokedex/detail@4.0.2 && npm install -D tailwindcss@3.4.19 @callstack/repack-plugin-nativewind@5.2.5 @callstack/repack-plugin-reanimated@5.2.5 postcss@8.4.47 postcss-loader@8.1.1 autoprefixer@10.4.20 @module-federation/enhanced@2.9.0 @react-native-community/cli@20.2.0 @react-native-community/cli-platform-android@20.2.0 @react-native-community/cli-platform-ios@20.2.0 )
( cd apps/party && npm pkg set overrides.react=19.2.3 overrides.react-dom=19.2.3 && npm install nativewind@4.2.6 @gluestack-ui/core@3.0.25 @gluestack-ui/utils@3.0.21 react-native-reanimated@4.6.0 react-native-worklets@0.12.1 @pokedex/ui@1.0.2 @pokedex/contracts@3.2.2 @pokedex/detail@4.0.2 && npm install -D tailwindcss@3.4.19 @callstack/repack-plugin-nativewind@5.2.5 @callstack/repack-plugin-reanimated@5.2.5 postcss@8.4.47 postcss-loader@8.1.1 autoprefixer@10.4.20 @module-federation/enhanced@2.9.0 @react-native-community/cli@20.2.0 @react-native-community/cli-platform-android@20.2.0 @react-native-community/cli-platform-ios@20.2.0 )
Ang
overridesblock ang dahilan kung bakit nagsisimula sanpm pkg setang mga install na iyon. Kinakaladkad ng gluestack angreact-native-webatreact-dombilang transitive peers, at nagbabanggaan ang mga react range nila sa naka-pin nareact@19.2.3ng app. Ang pag-pin sa dalawa saoverridesang umaayos nito. Ang pag-abot sa--legacy-peer-depsay nagtatago mismo ng mga version mismatch na kailangan mong makita ng isang shared singleton.
Ang huling apat na pin ay nag-a-update ng @module-federation/enhanced at ng React Native CLI trio (ang core package at ang dalawang platform package) sa mga kasalukuyang release, na naglilinis ng mga maaayos na npm audit advisory ng stack na ito; nakatala sa README ng repo ang natitira at kung bakit. May dalang native code ang react-native-reanimated at react-native-worklets, kaya kailangang i-refresh ng host ang mga pod nito bago ang build sa Patakbuhin: ( cd apps/host/ios && bundle install && bundle exec pod install ), ang parehong kumbensiyon na ginagamit ng README ng repo. Ipapaliwanag ng Sino ang nagse-share ng ano kung bakit narito ang react-native-reanimated at react-native-worklets. I-pin ang tailwindcss sa mas mababa sa 4. Ang styling runtime ng NativeWind 4, ang react-native-css-interop, ay nagdedeklara ng Tailwind 3 dependency, at ang latest na Tailwind sa npm ay isang 4.x na hindi gagana dito.
Ang config kada app, ang mga entry file at ang mga muling itinayong screen ay galing sa reference copy. Ang sweep na ito ang ginagawang kapareho ng tag ang tree mo:
for app in host list party; do
cp /tmp/pokedex-ref/apps/$app/babel.config.js \
/tmp/pokedex-ref/apps/$app/global.css \
/tmp/pokedex-ref/apps/$app/index.js \
/tmp/pokedex-ref/apps/$app/jest.config.js \
/tmp/pokedex-ref/apps/$app/rspack.config.mjs \
/tmp/pokedex-ref/apps/$app/tailwind.config.js \
/tmp/pokedex-ref/apps/$app/tsconfig.json apps/$app/
done
cp /tmp/pokedex-ref/apps/host/App.tsx /tmp/pokedex-ref/apps/host/mf-modules.d.ts apps/host/
cp /tmp/pokedex-ref/apps/host/__mocks__/react-native-reanimated.js /tmp/pokedex-ref/apps/host/__mocks__/partyApp-styles.js apps/host/__mocks__/
cp /tmp/pokedex-ref/apps/host/__tests__/RemoteBoundary.test.tsx apps/host/__tests__/
cp /tmp/pokedex-ref/apps/list/src/ListStack.tsx /tmp/pokedex-ref/apps/list/src/PokedexScreen.tsx /tmp/pokedex-ref/apps/list/src/detailApi.ts /tmp/pokedex-ref/apps/list/src/typesApi.ts apps/list/src/
mkdir -p apps/list/__mocks__
cp /tmp/pokedex-ref/apps/list/__mocks__/react-native-reanimated.js /tmp/pokedex-ref/apps/list/__mocks__/styleMock.js apps/list/__mocks__/
cp /tmp/pokedex-ref/apps/list/__tests__/addGate.test.tsx /tmp/pokedex-ref/apps/list/__tests__/doubleTapAdd.test.tsx /tmp/pokedex-ref/apps/list/__tests__/parsePokemonDetail.test.ts /tmp/pokedex-ref/apps/list/__tests__/parsePokemonTypes.test.ts apps/list/__tests__/
cp /tmp/pokedex-ref/apps/party/src/PartyScreen.tsx /tmp/pokedex-ref/apps/party/src/PartyStack.tsx /tmp/pokedex-ref/apps/party/src/detailApi.ts /tmp/pokedex-ref/apps/party/src/styles.ts apps/party/src/
cp /tmp/pokedex-ref/apps/party/__tests__/partyStateReady.test.ts /tmp/pokedex-ref/apps/party/__tests__/partySelfRecovery.test.tsx /tmp/pokedex-ref/apps/party/__tests__/removeAnimatesBeforeCommit.test.tsx apps/party/__tests__/
mkdir -p apps/party/__mocks__
cp /tmp/pokedex-ref/apps/party/__mocks__/react-native-reanimated.js apps/party/__mocks__/
cp -R /tmp/pokedex-ref/scripts .
cp /tmp/pokedex-ref/README.md .
Naka-configure ang Babel na gamitin ang JSX import source ng NativeWind, naka-configure ang Rspack na gamitin ang plugin, at isang tatlong-linyang global.css ang may hawak ng mga Tailwind directive. Ang interesanteng file ay ang tailwind.config.js:
module.exports = {
presets: [require('nativewind/preset'), require('@pokedex/ui/tailwind.preset.js')],
darkMode: 'class',
content: [
'./App.tsx',
'./src/**/*.{js,jsx,ts,tsx}',
path.join(path.dirname(require.resolve('@pokedex/ui/package.json')), 'src/**/*.{js,jsx,ts,tsx}'),
path.join(path.dirname(require.resolve('@pokedex/detail/package.json')), 'src/**/*.{js,jsx,ts,tsx}'),
],
theme: { extend: {} },
plugins: [],
};
May tatlong configuration warning sa file na iyon at sa mga katabi nito, at ang pagbalewala sa alinman sa mga ito ay nagdudulot ng tahimik na pagpalya.
I-scan ang source ng mga package. Nagge-generate lang ng class ang Tailwind kapag nakikita nito ang literal na string, at ang mga class ng design system ay nasa
node_modules. Kailangang isama ng mgacontentglob ang ipinadalang source ng@pokedex/uiat@pokedex/detail, na hinahanap gamit angrequire.resolve. Palampasin ito at mukhang hindi consistent ang problema: gumagana pa rin ang class na ginagamit din ng app, at ang mga class lang na eksklusibo sa library ang nawawala.
I-import ang
global.csssa bawat exposed module, hindi lang sa entry. Ang exposed module ang nilo-load ng federation, hindi kailanman ang sarilingindex.jsng remote, kaya angglobal.cssna naka-import lang sa entry ay gumagana sa standalone at, kapag federated, walang ginagawa at walang sinasabi. Nag-i-import nito angListStack.tsxsa itaas, eksakto dahil dito, at nilo-load ito ng party sa pamamagitan ng module na inilalarawan sa Nagpaparehistro ang mga style sa boot.
Required ang
darkMode: 'class'. Nagta-throw angsetColorSchemeng NativeWind kapag wala ito, at ang palit ng tema sa Isang toggle, tatlong bundle ay nakasalalay sa call na iyon.
Nagpaparehistro ang mga style sa boot
Inilipat ng party ang import nito nang isang hakbang na mas maaga, at sulit ang paliwanag kung bakit.
Ang pagrerehistro ng stylesheet ay nagsusulat sa styling runtime na binabasa ng bawat naka-mount na component. Kapag dumating ang module ng party tab habang lazy na nagre-render, ang pagsulat na iyon ay nangyayari habang nakadisplay pa ang mga screen ng Pokédex, at sinasabi ito ng React sa development: Can’t perform a React state update on a component that hasn’t mounted yet, na ang component stack ay tumuturo sa bundle ng list. Tatlo sila, sa unang pagbukas ng tab. Walang nasisira. Nag-render nang tama ang screen, development-only na log lang ang babala, at hindi ito nakikita ng sinumang hindi nagbubukas ng pangalawang tab.
Sulit pa rin itong ayusin, dahil tukoy na ngayon ang sanhi. Nag-ulat ang post 8 ng kaparehong warning sa boot at tahasang sinabing hindi pa ito na-trace. Ang isang ito, na-trace: ang import '../global.css' sa itaas ng isang exposed module ay tumatakbo habang isinasagawa ang render na naglo-load sa module na iyon. Kinumpirma ito ng bisect. I-comment out ang import at tahimik na bubukas ang tab.
Kaya naging sariling module ang mga style ng party:
// apps/party/src/styles.ts
// The remote's styles as a module the host can preload, for the same reason partySlice is one:
// registering them is a side effect the styling runtime shares with every app already mounted.
import '../global.css';
Sumasama ito sa exposes map katabi ng state module, at nag-i-import ang PartyStack.tsx ng ./styles sa halip na ang stylesheet mismo, para mairehistro pa rin ng standalone build ang mga style:
exposes: {
'./PartyStack': './src/PartyStack.tsx',
'./partySlice': './src/partySlice.ts',
'./styles': './src/styles.ts',
},
Nilo-load ito ng host sa parehong effect na naglo-load na ng state module, isang linya sa itaas nito:
import('partyApp/styles').catch(err => console.warn('party styles failed to load', err));
May idinadagdag itong ikalawang bahagi sa rule ng post 8. Ang kailangang irehistro ng remote bago ito mag-render, ang reducer at ang mga style nito, ay naglo-load sa boot; on demand pa rin naglo-load ang mga screen. Hindi kailangan ng Pokédex tab ang paglipat na ito, at sulit sabihin kung bakit: ito ang unang nire-render ng host, kaya nagpaparehistro ang mga style nito habang wala pang ibang naka-mount na maaapektuhan.
Sino ang nagse-share ng ano
Ang desisyon sa shared map ay ang identity test ng mga post 3 at 6, na tinanong nang tatlong beses at tatlong magkakaibang sagot.
Kabilang ang nativewind sa shared map dahil ang runtime nito ay may hawak na module-level na style registry at ng state ng color scheme. Sa dalawang kopya, nagrerehistro ang remote ng mga style sa isang registry na hindi kailanman binabasa ng host. Nakatala sa discussion #1032 ng Re.Pack ang parehong pagpalya ng registry. Ang tinanggap na sagot doon ay nagmumungkahi ng deep-sharing ng react-native-css-interop/, may mga sumunod na reply na nagsasabing buhay pa rin ang problema, at nagsara ang PR #1242 nang hindi nai-merge ang accumulation approach nito.
Ang host ang nagpo-provide ng iisang styling runtime na may state. Kapag shared ang NativeWind bilang eager singleton at naka-configure ang Re.Pack plugin sa bawat app, parehong nagrerehistro ang dalawang remote ng mga style sa registry na binabasa ng host. Vine-verify ng build na ito ang behavior na iyon sa dalawang remote at sa dalawang color scheme.
Host din ang nagpo-provide ng @pokedex/ui bilang eager singleton: isang provider, isang theme at isang set ng mga component instance. Ang mga build-time tool ay nananatiling labas sa runtime map. Hindi kailanman sumasama ang Tailwind at PostCSS sa bundle, at values lang ang dala ng Zod; wala itong process-wide identity. Ang apat na entry ng host:
'@pokedex/ui': {
singleton: true,
eager: true,
requiredVersion: pkg.dependencies['@pokedex/ui'],
},
nativewind: {
singleton: true,
eager: true,
requiredVersion: pkg.dependencies.nativewind,
},
'react-native-reanimated': {
singleton: true,
eager: true,
requiredVersion: pkg.dependencies['react-native-reanimated'],
},
'react-native-worklets': {
singleton: true,
eager: true,
requiredVersion: pkg.dependencies['react-native-worklets'],
},
Nagrerehistro ang Reanimated at Worklets ng mga native view at ng isang worklet runtime nang isang beses kada proseso. Noong nag-bundle ang list ng sariling kopya para sa collapse ng detail, nag-crash ang app habang naglo-load dahil dalawang beses nairehistro ang parehong pangalan ng native view. Ngayon, ang host ang nagpo-provide ng iisang kopya sa bawat consumer.
At sinadyang nasa labas ang @pokedex/detail. Ang per-consumer versioning ang feature nito: gumagana pa rin ang version ladder ng post 5 pagkatapos ng post na ito, at ang list na may detail 4.0.2 habang nasa 3.x pa ang party ay isang suportadong kalagayan, hindi bug. Parehong test, kabaligtarang sagot, parehong tama.
Naging isa ang mga grey
Hindi nagbabago ang balangkas ng mga screen. Nire-render ng list ang PokemonCard sa isang tatlong-column na FlatList, at ginagamit ng party ang parehong card katabi ng EmptySlot ng design system. Pinapalitan ng mga shared token ang mga literal na kulay sa antas ng app; ang mga token definition at ang mga translucent na overlay ng detail lang ang may hawak pa ng mga raw na value:
| Dati, kada app | Ngayon, mula sa seam |
|---|---|
#2a75bb sa mga button ng dalawang app | bg-pokemonGreen, isang token |
#6b7280, #9ca3af, #f3f4f6, ang mga template na grey | text-darkGrey, text-midGrey, bg-offGrey |
#eef2ff / #4338ca, ang mga kulay ng badge sa list | TYPE_BG_CLASS kada totoong type |
Mga screen na #fff | Mga variant ng ScreenContainer: bg-offWhite sa light, bg-navy sa dark |
May dalawang mas maliit na pagbabagong kasama ng conversion. Binubuksan na ang detail via push sa stack ng bawat tab, na may BackPill ng design system na lumulutang sa ibabaw ng hero, kaya hindi gumagalaw ang tab bar habang bukas ang isang detail:
At ang Refresh button sa header mula sa post 6 ay nagiging pull-to-refresh gesture sa list. Hindi nagbabago ang dispatch (isang na-invalidate na tag, na parehong pino-provide ng dalawang endpoint ng post 10), kaya sabay na nagre-refetch ang mga REST row at ang mga GraphQL badge mula sa isang gesture sa halip na button:
Isang toggle, tatlong bundle
Ang ThemeToggle ay isang component ng design system na walang prop: sa isang pindot, isinusulat nito ang color scheme sa shared na nativewind singleton, at bawat remote ang nagmo-mount nito sa kanang bahagi ng sarili nitong header. Ang interesante ay ang hindi nito ginagawa: hindi ito kailanman nakikipag-usap sa kahit anong remote. Ang scheme ay module-level na state sa loob ng shared styling runtime, bawat dark: class sa runtime ay muling nagre-resolve kapag naa-update ang scheme, at tatlong bundle na hiwalay na na-ship ay sabay na nagre-repaint. Walang docs na nangangako niyan; verified ito sa build na ito.
Nagbabago ang native na navigation sa parehong update. Lahat ay galing sa iisang observable: kinukuha ng host mula sa useColorScheme() ang theme ng NavigationContainer nito, ang parehong subscription na ginagamit ng mga class. May naunang bersyon ng build na ito na may hawak na hiwalay na host state bukod dito, at ang mga native na navigation bar ay nagpalit nang halos kalahating segundo bago ang styling pass ng content. Dahil iisa na ang source of truth, sabay-sabay nang nag-update ang lahat ng surface.
Parehong singleton ang gamit ng feedback
Ginagamit ng feedback ng party ang parehong shared package. Ang toast() ay isang ordinaryong function sa @pokedex/ui, at ang host ang nagmo-mount ng iisang Toaster ng runtime sa ibabaw ng tab bar. Pagkatapos mag-dispatch ng add, binabasa ng list ang store at ini-report kung ano ang naging resulta. Dalawang mabilis na tap sa limang member ay nagbibigay ng isang “joined your party” na mensahe at isang “Your party is full” na mensahe, dahil ang party reducer ang nagdedesisyon ng resulta.
Hindi kailanman mino-mount ng list ang component na gumuguhit ng kapsula. Tinatawag nito ang shared presentation API, at ang Toaster ng host ang nagre-render ng resulta kasama ang sprite ng Pokémon at ang kulay ng type nito.
Ganoon din ipinapadala ang feedback kapag nag-remove ng member sa party, na siyang panuntunan sa pagmamay-ari ng post 6 na dinala sa presentation: sa module mismo na nagbabago ng state nanggagaling ang kumpirmasyon. Ang toast() mismo ang nag-a-announce ng mensahe sa screen reader, kaya hindi puwedeng ipakita ng tumatawag ang visual na kumpirmasyon at laktawan ang accessible na bersyon.
Ngayon, sirain natin
May dahilan ang bawat entry sa map; patunayan ito sa pagtanggal ng isa. Sa apps/list/rspack.config.mjs, burahin ang nativewind entry mula sa shared, i-restart ang dev server ng list, at mag-reload.
Walang nagka-crash. Nag-boot ang app, nagre-render ang list, naglo-load ang mga sprite, at tahimik na mali ang layout: nawawalan ng lapad ang mga column wrapper ng grid, kaya sumusukat ang bawat card sa sarili nitong laman at hindi na naghahanay ang tatlong column sa magkakasunod na row. Nananatili ang mga kulay, pati na ang mga type badge at ang tipograpiya, at iyon ang dahilan kung bakit madaling hindi mapansin ang pagpalyang ito. Ang remote ay nagdadala na ngayon ng sarili nitong styling runtime. Kino-compile ng build nito ang mga class ng remote papunta sa isang registry na hindi kailanman binabasa ng mga shared component, at ang lapad ang nawawala sa screen. Hindi apektado ang mga component mula sa @pokedex/ui, dahil na-compile na ng scan ng host ang mga class nila papunta sa runtime ng host.
Maingay ang pagpalya ng post 6, tahimik ang sa post 8, at ang isang ito ay tahimik at parsyal: gumagana pa rin ang screen, pero ang grid nito ay hindi na ang grid ng design system. Ibalik ang entry, i-restart, at babalik sa ayos ang grid. Ang styling runtime ay shared state, at ang shared state sa ilalim ng federation ay singleton o bug.
Patakbuhin
Ang mga native asset ay kasama pa rin ng binary ng host. Kopyahin mula sa kasamang repo ang mga tab icon, ang app icon, ang bola ng launch screen, ang mga Nunito font at ang native na configuration. Dala ng kopya ng ios/Host ang icon catalogue, ang launch storyboard at ang app delegate. Kopyahin din ang project.pbxproj, dahil nire-render lang ng Xcode ang mga hiwalay na splash image kapag nakalista sila ng project bilang resources. Ang huling command ang nag-uugnay ng mga typeface:
cp -R /tmp/pokedex-ref/apps/host/src/assets apps/host/src/
cp -R /tmp/pokedex-ref/assets .
cp /tmp/pokedex-ref/apps/host/react-native.config.js apps/host/
cp /tmp/pokedex-ref/apps/list/react-native.config.js apps/list/
cp /tmp/pokedex-ref/apps/party/react-native.config.js apps/party/
cp -R /tmp/pokedex-ref/apps/host/ios/Host apps/host/ios/
cp /tmp/pokedex-ref/apps/host/ios/Host.xcodeproj/project.pbxproj apps/host/ios/Host.xcodeproj/
cp -R /tmp/pokedex-ref/apps/host/android/app/src/main/res apps/host/android/app/src/main/
( cd apps/host && npx react-native-asset@2.3.0 )
Tumatakbo pa ang Verdaccio mula sa mga publish step, at na-publish na ang tatlong package. Simulan ang tatlong dev server, bawat isa sa sariling terminal:
( cd apps/list && npm run start:remote )
( cd apps/party && npm run start:remote )
( cd apps/host && npm start )
Pagkatapos, i-build at i-launch ang host app nang isang beses, para ma-compile ang bagong app icon at ang asset catalogue:
( cd apps/host && npm run ios )
Buksan ang Pokédex tab para sa tatlong-column na card grid, i-tap ang isang Pokémon para sa tapos nang detail, idagdag ito sa party at panoorin ang pag-angat ng kumpirmasyon. Pagkatapos ay pindutin ang toggle sa header: sa isang pindot, nagpapalit ng scheme ang bawat surface, kasama ang dalawang remote at ang native na navigation. Ang buong ikot, mula splash hanggang walang lamang party, ay ganito:
Ang binuo mo, at ang susunod
Iisang design system at iisang styling runtime na ang nagsisilbi sa bawat remote. Ang mga remote ay nag-i-import ng @pokedex/ui bilang package, at ang federation ang nagre-resolve nito sa kopya ng host. Kaya ng isang shell release na palitan ang itsura nila nang hindi nire-rebuild ang bawat remote, basta’t compatible pa rin ang component API.
Nananatili ang native boundary. May dalang native module ang react-native-svg, at hindi makakarating ang native code sa federation. Sakop din ng parehong native boundary ang mga font, pero ibang dahilan ang umiiral: kailangang nasa binary ang bawat typeface at nakarehistro sa build time.
Makikita sa companion repo ang dalawang kaso. Ang mga tab icon ay simpleng tinted na imahe, ang mga Nunito face ay naka-link sa binary ng host, at ang bola ng launch screen ay isang storyboard asset.
Susunod: tatawid sa seam ang accessibility, isang shared testing package na magche-check ng mga touch target, contrast at focus order sa mga remote na sa runtime lang dini-discover ng host.
Mga Sanggunian
- gluestack-ui — ang copy-in na arkitektura ng mga component; “you copy, paste, and own your components”
- NativeWind — ang className runtime, at ang other-bundlers page na naglilista ng mga community solution
- @callstack/repack-plugin-nativewind — ang first-party na plugin na kino-configure ng post na ito
- @callstack/repack-plugin-reanimated — ang Reanimated transform na hinihingi ng Re.Pack sa pangalan
- Tailwind v3: content configuration — ang pag-scan sa source ng sarili mong component library
- callstack/repack discussion #1032 — ang mga style ng remote na ino-overwrite ang registry ng host; bukas pa rin sa upstream
- callstack/repack PR #1242 — ang naisarang accumulation attempt; ang resulta ng singleton sa build na ito ang nagsisilbing ebidensiya nito
- WCAG 2.2, contrast (minimum) — ang 4.5:1 na pamantayang pinagbabatayan ng mga type-colour token
- react-native-module-federation — ang kasamang repo, ang build sa tag na
post-11-design-system