快速上手
本页构建一个包含两个微应用的 shell(宿主应用):一个首页和一个仪表盘。你将注册这些应用,让路由器挂载它们,并向已挂载的应用推送新的 props。
安装
安装聚合包。它包含编排器、路由器、加载器、事件总线和沙箱:
pnpm add tuvix.js为微应用使用的每个框架添加一个适配器:
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-browser搭建项目脚手架
create-tuvix-app 会生成一个依赖 0.5 版本包的 shell 项目或微应用项目:
npx create-tuvix-app my-shell --template shell
npx create-tuvix-app my-dashboard --template react-app模板:shell、react-app、vue-app、svelte-app、angular-app、vanilla-app。传入 --example <name> 则会复制仓库中的某个示例。
构建 shell
shell 掌管页面布局,并决定哪个应用放在哪里。给它一个容器元素:
<nav>
<a href="/">Home</a>
<a href="/dashboard">Dashboard</a>
</nav>
<main id="app"></main>
<script type="module" src="/src/main.ts"></script>然后创建编排器,注册应用并启动它:
// 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',
// 单独的 '/' 会匹配所有路径;`end: true` 让它精确匹配。
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' },
});
// 把 <a href> 点击转换为路由器导航,而不是页面加载。
interceptLinks({ navigate: (path) => orchestrator.navigateTo(path) });
await orchestrator.start();发生了什么:
start()读取当前路径,并挂载所有activeWhen匹配的应用。- 点击
<a href="/dashboard">会调用navigateTo('/dashboard')。路由器推入 URL,编排器先卸载home,再挂载dashboard。 dashboard首次挂载时会加载它的脚本和样式表。之后的挂载会复用已加载的模块。
路由器本身从不拦截点击。没有 interceptLinks 时,点击链接会重新加载页面,编排器会在新的页面加载中挂载正确的应用。
编写微应用
微应用是一个带有 mount 和 unmount 的对象。defineMicroApp 会检查其结构,并以其名称注册该模块,这样脚本运行后加载器就能找到它:
// 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();
},
});传给 defineMicroApp 的 name 必须与你在 shell 中注册的名称相同。加载器按这个名称查找模块。
使用框架适配器
适配器从根组件创建同样的模块。它们会注册该模块,为每个容器保留一个框架实例,并派发生命周期事件。
// 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 });框架页面:React、Vue、Svelte、Angular。
将每个微应用构建为 ES 模块 bundle 并上传。Vite 库模式构建即可:
// dashboard/vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: { entry: 'src/main.tsx', formats: ['es'], fileName: 'main' },
},
});Vite 的 ES 输出以 .js 结尾,除非入口声明了 type: 'module',否则加载器会把它当作经典脚本。如上所示,在 shell 的入口中保留 type: 'module'。
在运行时更新 props
import { createOrchestrator } from 'tuvix.js';
const orchestrator = createOrchestrator();
await orchestrator.updateAppProps('dashboard', { user: 'Yasin' });updateAppProps 会把新的 props 合并到已注册的 props 中。如果应用已挂载并且有 update,编排器会用合并后的 props 调用它,应用原地重新渲染。如果应用未挂载,编排器会保存这些 props,留给下一次挂载使用。
shell 不做什么
- 它不渲染你的布局。容器由 shell 自己的 HTML 提供。
- 它不在应用之间共享框架实例。两个 React 应用会得到两个 React root。
- 它不对 JavaScript 进行沙箱隔离。每个应用都运行在页面的
window中。
下一步
- 架构:从导航到
mount的路径。 - 路由:
activeWhen模式、内置路由器、守卫。 - 外部路由器:跟随 TanStack Router 或 React Router。
- SSR Islands:在 React 页面中服务端渲染微应用。