Per iniziare
Questa pagina costruisce una shell con due micro app: una home page e una dashboard. Registrerai le app, lascerai che il router le monti e invierai nuove props a un'app montata.
Installazione
Installa il pacchetto ombrello. Contiene orchestratore, router, loader, event bus e sandbox:
pnpm add tuvix.jsAggiungi un adapter per ogni framework usato dalle tue micro app:
pnpm add @tuvix.js/react react react-dom
pnpm add @tuvix.js/vue vue
pnpm add @tuvix.js/svelte svelte
pnpm add @tuvix.js/angular @angular/core @angular/common @angular/platform-browserCrea lo scaffolding di un progetto
create-tuvix-app genera un progetto shell o micro app che dipende dai pacchetti 0.5:
npx create-tuvix-app my-shell --template shell
npx create-tuvix-app my-dashboard --template react-appTemplate: shell, react-app, vue-app, svelte-app, angular-app, vanilla-app. Passa --example <name> per copiare invece uno degli esempi del repository.
Costruisci una shell
La shell gestisce il layout della pagina e decide quale app va dove. Dalle un elemento container:
<nav>
<a href="/">Home</a>
<a href="/dashboard">Dashboard</a>
</nav>
<main id="app"></main>
<script type="module" src="/src/main.ts"></script>Poi crea l'orchestratore, registra le app e avvialo:
// src/main.ts
import { createOrchestrator, interceptLinks, matchPath } from 'tuvix.js';
const orchestrator = createOrchestrator({
router: { mode: 'history', routes: [] },
});
orchestrator.register({
name: 'home',
entry: () => import('./apps/home'),
container: '#app',
// '/' alone would match every path; `end: true` makes it exact.
activeWhen: (path) => matchPath('/', path, { end: true }).matched,
});
orchestrator.register({
name: 'dashboard',
entry: {
scripts: ['https://cdn.example.com/dashboard/main.js'],
styles: ['https://cdn.example.com/dashboard/main.css'],
type: 'module',
},
container: '#app',
activeWhen: '/dashboard/*',
props: { user: 'Guest' },
});
// Turn <a href> clicks into router navigations instead of page loads.
interceptLinks({ navigate: (path) => orchestrator.navigateTo(path) });
await orchestrator.start();Cosa succede:
start()legge il path corrente e monta ogni app il cuiactiveWhencorrisponde.- Un clic su
<a href="/dashboard">chiamanavigateTo('/dashboard'). Il router aggiorna l'URL, l'orchestratore smontahomee poi montadashboard. - Il primo mount di
dashboardcarica il suo script e il suo foglio di stile. I mount successivi riutilizzano il modulo già caricato.
Il router non intercetta mai i clic da solo. Senza interceptLinks, il clic su un link ricarica la pagina e l'orchestratore monta l'app giusta al nuovo caricamento della pagina.
Scrivi una micro app
Una micro app è un oggetto con mount e unmount. defineMicroApp verifica la forma e registra il modulo con il suo nome, così il loader può trovarlo dopo l'esecuzione dello script:
// apps/home.ts
import { defineMicroApp } from 'tuvix.js';
export default defineMicroApp({
name: 'home',
mount({ container, props }) {
const title = document.createElement('h1');
title.textContent = `Welcome, ${String(props?.user ?? 'Guest')}`;
container.replaceChildren(title);
},
update({ props, container }) {
const title = container?.querySelector('h1');
if (title) title.textContent = `Welcome, ${String(props.user ?? 'Guest')}`;
},
unmount({ container }) {
container.replaceChildren();
},
});Il name che passi a defineMicroApp deve essere uguale al nome che registri nella shell. Il loader cerca il modulo proprio con quel nome.
Usa un adapter per framework
Gli adapter creano lo stesso modulo a partire da un componente radice. Lo registrano, mantengono un'istanza del framework per container ed emettono gli eventi del ciclo di vita.
// dashboard/src/main.tsx
import { createReactMicroApp } from '@tuvix.js/react';
function Dashboard({ user }: { user: string }) {
return <h1>Dashboard for {user}</h1>;
}
export default createReactMicroApp({ name: 'dashboard', App: Dashboard });// settings/src/main.ts
import { createVueMicroApp } from '@tuvix.js/vue';
import Settings from './Settings.vue';
export default createVueMicroApp({ name: 'settings', App: Settings });Pagine dei framework: React, Vue, Svelte, Angular.
Fai la build di ogni micro app come bundle ES module e pubblicala. Una build in modalità libreria di Vite funziona:
// dashboard/vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: { entry: 'src/main.tsx', formats: ['es'], fileName: 'main' },
},
});L'output ES di Vite termina in .js, che il loader tratta come script classico a meno che la entry non indichi type: 'module'. Mantieni type: 'module' nella entry della shell, come sopra.
Aggiorna le props a runtime
import { createOrchestrator } from 'tuvix.js';
const orchestrator = createOrchestrator();
await orchestrator.updateAppProps('dashboard', { user: 'Yasin' });updateAppProps unisce le nuove props a quelle registrate. Se l'app è montata e ha update, l'orchestratore lo chiama con le props unite e l'app si renderizza di nuovo sul posto. Se l'app non è montata, l'orchestratore conserva le props per il mount successivo.
Cosa non fa la shell
- Non renderizza il tuo layout. È l'HTML della shell a contenere i container.
- Non condivide istanze di framework tra le app. Due app React ottengono due root React.
- Non mette in sandbox il JavaScript. Tutte le app girano nel
windowdella pagina.
Prossimi passi
- Architettura: il percorso dalla navigazione a
mount. - Routing: pattern di
activeWhen, il router integrato, le guardie. - Router esterno: segui TanStack Router o React Router.
- Island SSR: renderizza lato server le micro app dentro una pagina React.