ライフサイクルフック
オーケストレーターは、登録された各アプリを決まった順序のフックとステータスに沿って進めます。このページでは、モジュールが実装するフック、シェルが読み取れるステータス、エラー時や素早いナビゲーション時の動作を説明します。アプリがページに表示されたタイミングを外部から知るには、ライフサイクルイベントを使ってください。
フックの順序
register()
└─ first activation: load entry → bootstrap() → mount()
├─ updateAppProps() while mounted → update()
└─ deactivation → unmount()
└─ next activation: mount() (module cached, no bootstrap)オーケストレーターは各フックを待機します。マウント中のアプリに対してmountを呼び出すことはなく、mountの実行中にunmountを呼び出すこともありません。アンマウントはマウントの完了を待ちます。
bootstrap
読み込まれたモジュールごとに1回、エントリーの読み込み後、最初のmountの前に実行されます。設定の読み込みなど、再マウントをまたいで保持すべき処理に使います。
import { defineMicroApp } from '@tuvix.js/core';
let config: { apiUrl: string } | undefined;
export default defineMicroApp({
name: 'reports',
async bootstrap() {
const response = await fetch('/reports/config.json');
config = (await response.json()) as { apiUrl: string };
},
mount({ container }) {
container.textContent = `API: ${config?.apiUrl ?? 'unknown'}`;
},
unmount({ container }) {
container.replaceChildren();
},
});bootstrapがエラーをスローすると、アプリはerrorステータスになり、マウントは失敗します。次のマウント試行ではbootstrapが再度呼び出されます。一度成功すると、以降の再マウントではスキップされます。
mount
アプリがアクティブになるたび、またはmountApp(name)を呼び出したときに実行されます。コンテキストには次のものが含まれます:
| フィールド | 値 |
|---|---|
container | レンダリング先の要素。sandbox: trueのアプリでは、Shadow Root内の要素です。 |
props | 登録済みのプロパティに、すべてのupdateAppProps呼び出しをマージしたもの。 |
name | 登録されたアプリ名。 |
bus | オーケストレーターのイベントバス。 |
mountApp(name)はmountの完了後に解決し、読み込みまたはマウントのエラーでリジェクトします。
update
アプリがマウントされている間にシェルがupdateAppProps(name, props)を呼び出したときに実行されます:
import { createOrchestrator } from '@tuvix.js/core';
const orchestrator = createOrchestrator();
await orchestrator.updateAppProps('reports', { range: '7d' });コンテキストには、マージ後のpropsとcontainerが含まれます。updateがない場合やアプリがマウントされていない場合、オーケストレーターはプロパティを保存し、次のmountに渡します。プロパティが変わったことを理由にアプリを再マウントすることはありません。
updateAppPropsは保留中のマウントを先に待つため、更新は新しいインスタンスに届きます。更新中に届いたunmountは、更新の完了を待ちます。
unmount
アプリが非アクティブになったとき、およびunmountApp(name)、unregister(name)、destroy()の呼び出し時に実行されます。フレームワークのルート、リスナー、タイマー、バスのサブスクリプションなど、mountが作成したものをすべて削除してください。
unmountの後、オーケストレーターはアプリのエントリーのスタイルシートをdocument.headから削除します。次のマウントでそれらを再度追加し、読み込みを待ちます。
アプリのステータス
import { createOrchestrator, type AppStatus } from '@tuvix.js/core';
const orchestrator = createOrchestrator();
const status: AppStatus | null = orchestrator.getAppStatus('reports');
// 'registered' | 'bootstrapping' | 'bootstrapped' | 'mounting' | 'mounted'
// | 'updating' | 'unmounting' | 'unmounted' | 'error' | null (not registered)| メソッド | 戻り値 |
|---|---|
getAppStatus(name) | ステータス。不明な名前の場合はnull。 |
getMountedApps() | ステータスがmountedの名前。 |
getRegisteredApps() | 登録済みのすべての名前。 |
エラーとフォールバック
読み込み、bootstrap、mountのいずれかが失敗すると、オーケストレーターは次の処理を行います:
fallbackを設定している場合、アプリのfallbackのHTMLをコンテナに書き込みます。- ステータスを
errorに設定します。 - バス上で
{ name, error, phase }とともにOrchestratorEvent.APP_ERROR(tuvix:app:error)を発行します。 onError(error, name)を呼び出します。mountAppをリジェクトします。ルート変更中のマウント失敗は、代わりにconsole.errorでログに記録され、他のアプリは引き続きマウントされます。
updateまたはunmountが失敗した場合は、手順2から4を実行し、updateAppPropsまたはunmountAppをリジェクトします。フォールバックのHTMLは書き込みません。phaseは'load'(コンテナの解決を含む)、'bootstrap'、'mount'、'update'、'unmount'のいずれかです。
import { createOrchestrator, OrchestratorEvent, type AppErrorEvent } from '@tuvix.js/core';
const orchestrator = createOrchestrator();
orchestrator.getEventBus().on<AppErrorEvent>(OrchestratorEvent.APP_ERROR, ({ name, phase, error }) => {
console.error(`${name} failed during ${phase}`, error);
});errorステータスのアプリをアクティブにする次のルート変更で、そのアプリのマウントが再試行されます。
素早いナビゲーション
ルート変更は一度に1つずつリコンサイルされます。非アクティブなアプリはアクティブなアプリのマウント前にアンマウントされます。アプリのマウントより速くパスが届いた場合:
- 次に実行されるのは、キューにある最新のパスのみです。
- エントリーの読み込み中に非アクティブになったアプリは、マウントをスキップします。
mountの実行中に非アクティブになったアプリは、mountの完了後にアンマウントされます。
そのため、A → B → Aと素早く続けて移動すると、最終的にマウントされているのはAだけになり、別のアプリがまだ保持しているコンテナにアプリがレンダリングすることはありません。
シェルレベルのフック
import { createOrchestrator, OrchestratorEvent } from '@tuvix.js/core';
const orchestrator = createOrchestrator({
onError: (error, name) => console.error(`[${name}]`, error),
onStatusChange: (name, status) => console.debug(`[${name}] ${status}`),
});
const bus = orchestrator.getEventBus();
bus.on<{ name: string }>(OrchestratorEvent.APP_MOUNT, ({ name }) => {
console.log('mounted', name);
});
bus.on<{ name: string }>(OrchestratorEvent.APP_UNMOUNT, ({ name }) => {
console.log('unmounted', name);
});
bus.on<{ to: string }>(OrchestratorEvent.ROUTE_CHANGE, ({ to }) => {
console.log('route', to);
});| イベント | ペイロード | 発行タイミング |
|---|---|---|
tuvix:app:registered | { name } | register() |
tuvix:app:bootstrap | { name } | bootstrapの後 |
tuvix:app:mount | { name } | mountの後 |
tuvix:app:unmount | { name } | unmountの後 |
tuvix:app:error | { name, error, phase } | 読み込み、ブートストラップ、マウント、更新、アンマウントのいずれかが失敗したとき |
tuvix:app:status-change | { name, status } | ステータスが変わるたび |
tuvix:route:change | { from, to, fromRoute, toRoute } | 組み込みのルーターによるナビゲーションのみ |
tuvix:started | {} | start()の終了時 |
tuvix:destroyed | {} | destroy()の終了時 |
これらのバスイベントはオーケストレーターから発行されます。ライフサイクルイベントのtuvix:mountedなどのDOMイベントはアダプターとdefineMicroAppから発行され、オーケストレーターなしでマウントされたアイランドでも発生します。
破棄
import { createOrchestrator } from '@tuvix.js/core';
const orchestrator = createOrchestrator();
await orchestrator.destroy();destroy()は実行中のリコンサイルを待ち、すべてのアプリをアンマウントし、ルーター、connectRouterのサブスクリプション、プリフェッチ、ビューポートオブザーバーを停止し、ローダーのキャッシュをクリアします。プライベートなイベントバスは破棄し、グローバルバスは残します。2回呼び出しても安全です。呼び出し後は、register、start、mountApp、unmountApp、unregister、reconcile、connectRouterがエラーをスローします。代わりに新しいオーケストレーターを作成してください。