Skip to content
📦
@tuvix.js/loader
ローダー
スクリプトURLまたはローダー関数からマイクロアプリのバンドルを名前で読み込みます。リトライ、タイムアウト、共有モジュールレジストリ、ライフサイクルイベントを備えています。

インストール ​

bash
npm install @tuvix.js/loader
# or pnpm add @tuvix.js/loader

使いどころ ​

@tuvix.js/loaderは、エントリー(スクリプトURL、スクリプトとスタイルのリスト、または() => import('./app'))を、mountとunmountを持つマイクロアプリモジュールに変換します。<script>タグと<link>タグを追加し、失敗した試行をリトライし、タイムアウト後に諦め、結果をアプリ名でキャッシュします。

@tuvix.js/coreはModuleLoaderを自動で作成し、createOrchestratorのloaderオプションをそれに渡します。そのため、ほとんどのシェルはこのパッケージをimportしません。次の場合にimportします。

  • registerModuleで自身を公開する、またはemitLifecycleでtuvix:mountedを通知するマイクロアプリやアダプターを書く
  • 別のアプリがいつマウントされたかを知る必要がある(whenMounted、onLifecycle)
  • オーケストレーターなしでマイクロアプリを読み込む(たとえば独自のルーターから)
  • 単一のスクリプトまたはスタイルシートを重複排除しながら読み込む(loadScript、loadStyle)

バンドラーがビルド時にアプリをimportでき、別のオリジンからコードを読み込むことがない場合は、通常のimport()で十分です。

クイックスタート ​

マイクロアプリは名前を付けてモジュールを公開します。このファイルはhttps://cdn.example.com/dashboard/main.jsにビルドされます。

ts
// dashboard/src/main.ts
import { registerModule } from '@tuvix.js/loader';

registerModule('dashboard', {
  mount({ container, props }) {
    container.innerHTML = `<h2>Dashboard for user ${String(props?.userId)}</h2>`;
  },
  unmount({ container }) {
    container.replaceChildren();
  },
});

シェルは名前でそれを読み込み、マウントします。

ts
// shell/src/main.ts
import { createLoader } from '@tuvix.js/loader';

const loader = createLoader({ timeout: 15000, retries: 1 });
const container = document.getElementById('dashboard')!;

const result = await loader.load('dashboard', {
  scripts: ['https://cdn.example.com/dashboard/main.js'],
  styles: ['https://cdn.example.com/dashboard/main.css'],
  type: 'module',
});

if (result.module) {
  await result.module.mount({ container, props: { userId: 42 } });
} else {
  container.textContent = 'The dashboard is unavailable.';
  console.error(result.error);
}

load()はスローしません。失敗した読み込みは、status: 'error'、module: null、最後のエラーとともに返ります。

エントリーの形式 ​

Entryは、アプリのコードがどこにあるかをローダーに伝えます。3つの形式があります。

URL文字列。 ローダーは'https://cdn.example.com/app.js'を{ scripts: ['https://cdn.example.com/app.js'] }として扱います。

EntryConfigオブジェクト:

オプション型デフォルト意味
scriptsstring[]必須スクリプトURL。ローダーはこの順序で1つずつ実行します。空の配列の場合は何も読み込まず、レジストリからモジュールを解決します。
stylesstring[][]スタイルシートURL。スクリプトと並列で読み込まれます。読み込みは両方を待ちます。
type'module' | 'classic'URLごとに推定scripts内のすべてのURLのスクリプトタイプ。指定しない場合、.mjs、.mts、.ts、.tsx、.jsxで終わるURLはtype="module"として読み込まれ、それ以外のURLはクラシックスクリプトとして読み込まれます。.jsで終わるViteやRollupのES出力には'module'を設定してください。
integrityRecord<string, string>なしURLをキーとするサブリソース完全性(SRI)のハッシュ。ローダーはこれを<script>、<link>、プリフェッチのタグに書き込みます。

ローダー関数(EntryLoader)。 ローダーはこの関数を{ signal }とともに呼び出し、次の3種類の結果を受け付けます。この順序で確認します。

  1. mount関数を持つモジュール
  2. defaultエクスポートがそのようなモジュールであるオブジェクト(ESモジュールの名前空間)
  3. 使用できるものがない場合。このときローダーは、importがregisterModule(name, ...)で登録したモジュールを探します
ts
import { createOrchestrator } from '@tuvix.js/core';

const orchestrator = createOrchestrator();

orchestrator.register({
  name: 'dashboard',
  entry: () => import('./dashboard'),
  container: '#main',
  activeWhen: '/dashboard/*',
});

await orchestrator.start();

バンドラーは./dashboardを独自のチャンクにコード分割し、初回のマウント時に取得します。@tuvix.js/module-federationがフェデレーションされたリモートをオーケストレーターに組み込む方法も、ローダー関数です。

signalは試行がタイムアウトすると中断されます。途中でfetchするものすべてに渡してください。

ts
import type { EntryLoader } from '@tuvix.js/loader';

const entry: EntryLoader = async ({ signal }) => {
  const response = await fetch('/api/manifest/dashboard.json', { signal });
  const { url } = (await response.json()) as { url: string };
  return import(/* @vite-ignore */ url);
};

API ​

ModuleLoader ​

ts
class ModuleLoader {
  constructor(options?: LoaderOptions);
  load(name: string, entry: Entry): Promise<LoadResult>;
  prefetch(entry: Entry): void;
  removeStyles(name: string): void;
  unload(name: string): void;
  clearCache(): void;
  isLoaded(name: string): boolean;
  getModule(name: string): MicroAppModule | null;
  getStatus(name: string): LoadStatus;
  getCachedNames(): string[];
}

各ModuleLoaderは、解決済みモジュールの独自のキャッシュをアプリ名ごとに保持します。<script>タグと<link>タグはページ全体で共通です。同じURLを読み込む2つのローダーは1つのタグを共有します。

LoaderOptions:

オプション型デフォルト意味
timeoutnumber100001回の試行の制限時間(ミリ秒)。エントリーのすべてのスクリプトとスタイルシートが対象です。0で無効になります。
retriesnumber2最初の試行が失敗した後の追加の試行回数。2は最大3回の試行を意味します。
retryDelaynumber1000試行間の待機時間(ミリ秒)。
globalsRecord<string, unknown>{}URLエントリーの各試行の前にローダーがwindowに代入するプロパティ。window.Reactや設定オブジェクトを前提とするUMDバンドル向けです。ローダー関数のエントリーでは無視されます。

load(name, entry) ​

LoadResultに解決されます。

フィールド型意味
status'loaded' | 'error'読み込みの結果。
moduleMicroAppModule | null解決されたモジュール。エラー時はnullです。
errorError | null最後の試行のエラー。
durationnumber呼び出しから結果までのミリ秒。performance.now()で計測します。

URLエントリーの場合、1回の試行は次のように進みます。

  1. options.globalsをwindowに代入する
  2. スクリプトを順番に追加し(それぞれ前のスクリプトを待ちます)、スタイルシートを並列で追加する
  3. レジストリからモジュールを読み取る(getRegisteredModule(name))
  4. UMDグローバルにフォールバックする:この読み込み中に現れた、mount関数を持つwindowのプロパティ

その後ローダーは、モジュールがmount関数とunmount関数を持つことを確認し、nameでキャッシュします。同じ名前での以降の呼び出しは、キャッシュされたモジュールを返し、entryを無視します。最初の呼び出しがまだ読み込み中のときの2回目の呼び出しは、同じPromiseを返します。異なる名前の読み込みは並列で実行されます。

prefetch(entry) ​

エントリーのファイルを実行せずにダウンロードするようブラウザに依頼します。モジュールスクリプトには<link rel="modulepreload">、クラシックスクリプトとスタイルシートには<link rel="prefetch">が使われます。ローダー関数のエントリーはスキップされます。関数を呼び出すとモジュールが実行されてしまうためです。オーケストレーターはprefetch設定に応じてこれを呼び出します。

ts
import { createLoader } from '@tuvix.js/loader';

const loader = createLoader();
loader.prefetch({ scripts: ['/apps/settings/main.js'], type: 'module' });

removeStyles(name) ​

読み込み済みアプリのスタイルシートを削除し、アンマウント中はそのCSSが適用されないようにします。スタイルを削除していない別のキャッシュ済みアプリが同じURLを持つ場合、そのスタイルシートは残ります。次のload(name)はスタイルシートを再び追加し、解決する前にそれらを待ちます。オーケストレーターはアンマウントのたびにremoveStylesを呼び出します。

ts
import { createLoader } from '@tuvix.js/loader';

const loader = createLoader();
const container = document.getElementById('cart')!;
const { module } = await loader.load('cart', { scripts: ['/cart.js'], styles: ['/cart.css'] });

await module?.mount({ container });
await module?.unmount({ container });
loader.removeStyles('cart');

unload(name) ​

アプリを破棄します。スタイルシートとscriptタグを削除し、キャッシュから外し、登録されているモジュールがこのローダーが解決したものであれば共有レジストリから登録解除します。次のload(name)はエントリーを再び取得して実行します。オーケストレーターはunregister(name)からこれを呼び出します。

clearCache() ​

キャッシュされたすべてのアプリに対してunloadを呼び出します。オーケストレーターはdestroy()からこれを呼び出します。

isLoaded(name), getModule(name), getStatus(name), getCachedNames() ​

キャッシュを読み取ります。getStatusは、読み込みの実行中は'loading'、モジュールがキャッシュされると'loaded'、それ以外は'idle'を返します。読み込みに失敗した場合、ステータスは'idle'のままです。

createLoader ​

ts
function createLoader(options?: LoaderOptions): ModuleLoader;

new ModuleLoader(options)と同じです。上の表のLoaderOptionsを受け取ります。

ts
import { createLoader } from '@tuvix.js/loader';

const loader = createLoader({ timeout: 8000, retries: 0, globals: { APP_ENV: 'production' } });

オーケストレーターにこれらのオプションを設定するには、loaderとして渡します。

ts
import { createOrchestrator } from '@tuvix.js/core';

const orchestrator = createOrchestrator({ loader: { timeout: 15000, retries: 1, retryDelay: 500 } });

normalizeEntry ​

ts
function normalizeEntry(entry: string | EntryConfig): EntryConfig;

URLを{ scripts: [url] }に変換し、EntryConfigはそのまま返します。ローダー関数は受け付けません。先にtypeof entry === 'function'を確認してください。

ts
import { normalizeEntry } from '@tuvix.js/loader';

normalizeEntry('https://cdn.example.com/app.js'); // { scripts: ['https://cdn.example.com/app.js'] }

loadScript ​

ts
function loadScript(url: string, options?: LoadScriptOptions | string): Promise<void>;
オプション型デフォルト意味
type'module' | 'classic'拡張子から推定スクリプトタイプ。EntryConfig.typeと同じルールです。
integritystringなしintegrity属性用のSRIハッシュ。
signalAbortSignalなし中断されると待機をやめます。Promiseはsignalの理由でrejectされます。

<script crossorigin="anonymous">をdocument.headに追加し、そのloadイベントで解決します。同じURLに対する同時呼び出しは1つのタグを共有し、すでに読み込まれたURLに対する呼び出しはすぐに解決します。読み込みに失敗するとタグを削除し、[Tuvix Loader] Failed to load script: <url>でrejectするため、次の呼び出しは新しいタグで始まります。中断された呼び出しは、他の呼び出し元がまだそのタグを待っていない場合にのみタグを削除します。loadScriptには独自のタイムアウトがありません。signalを渡してください。

2番目の引数に文字列を渡すと、0.4と同様にintegrityハッシュとして読み取られます。

ts
import { loadScript } from '@tuvix.js/loader';

await loadScript('https://cdn.example.com/charts.js', { type: 'classic', signal: AbortSignal.timeout(5000) });

loadStyle ​

ts
function loadStyle(url: string, options?: LoadStyleOptions | number): Promise<void>;
オプション型デフォルト意味
timeoutnumber10000指定したミリ秒が経過すると、[Tuvix Loader] Style load timed out: <url>で読み込みを失敗させます。0で無効になります。
integritystringなしintegrity属性用のSRIハッシュ。
signalAbortSignalなし中断されると待機をやめます。

<link rel="stylesheet" crossorigin="anonymous">をdocument.headに追加します。共有、失敗、中断のルールはloadScriptと同じです。2番目の引数に数値を渡すと、0.4と同様にタイムアウトとして読み取られます。

ts
import { loadStyle } from '@tuvix.js/loader';

await loadStyle('https://cdn.example.com/app.css', { timeout: 5000 });

removeScript ​

ts
function removeScript(url: string): void;

loadScriptがurlのために追加したタグと、src属性がurlと等しいすべての<script>を削除します。そのURLでまだ進行中の読み込みは[Tuvix Loader] Removed before load: <url>でrejectされます。次のloadScript(url)は新しいタグを追加します。スクリプトがすでに実行したコードはメモリに残ります。

removeStyle ​

ts
function removeStyle(url: string): void;

<link rel="stylesheet">タグに対するremoveScriptと同じです。

prefetchResource ​

ts
function prefetchResource(url: string, options?: PrefetchOptions): void;
オプション型デフォルト意味
as'script' | 'style'.cssのURLは'style'、それ以外は'script'リソースの種類。
type'module' | 'classic'拡張子から推定モジュールスクリプトにはrel="modulepreload"が使われます。
integritystringなしリンク用のSRIハッシュ。

URLとページごとに<link>を1つ追加します。loadScriptまたはloadStyleがすでに扱っているURLや、以前にプリフェッチされたURLはスキップされます。

ts
import { prefetchResource } from '@tuvix.js/loader';

prefetchResource('https://cdn.example.com/app.js', { type: 'module' });

registerModule ​

ts
function registerModule(name: string, module: MicroAppModule): void;

アプリのスクリプトが実行された後にローダーが見つけられるよう、モジュールを名前で公開します。同じ名前で2回目に呼び出すと、最初のモジュールが置き換えられます。React、Vue、Svelte、AngularのアダプターとcoreのdefineMicroAppは、これを自動で呼び出します。モジュールを手書きする場合は自分で呼び出してください。

レジストリとマウント済みアプリの状態はwindow.__TUVIX_RUNTIME__に保持され、モジュールマップはwindow.__TUVIX_MODULES__からもアクセスできます。それぞれが@tuvix.js/loaderの独自のコピーをバンドルしたマイクロアプリも、同じレジストリを読み書きします。windowのないサーバーでは、状態はモジュール内に保持されます。

アダプターの規約に従う手書きのマイクロアプリ:

ts
import { emitLifecycle, hasServerMarkup, registerModule } from '@tuvix.js/loader';

registerModule('clock', {
  mount({ container }) {
    const hydrated = hasServerMarkup(container);
    if (!hydrated) container.textContent = new Date().toLocaleTimeString();
    emitLifecycle('mounted', { name: 'clock', container, hydrated });
  },
  unmount({ container }) {
    container.replaceChildren();
    emitLifecycle('unmounted', { name: 'clock', container, hydrated: false });
  },
});

unregisterModule ​

ts
function unregisterModule(name: string): void;

レジストリからモジュールを削除します。ModuleLoader.unloadは、自身が解決したモジュールに対してこれを呼び出します。

getRegisteredModule ​

ts
function getRegisteredModule(name: string): MicroAppModule | undefined;

登録されたモジュール、またはundefinedを返します。

emitLifecycle ​

ts
function emitLifecycle(type: 'mounted' | 'unmounted' | 'error', detail: LifecycleDetail): void;

ライフサイクルの変化を記録し、tuvix:mounted、tuvix:unmounted、またはtuvix:errorという名前のCustomEventをディスパッチします。イベントはバブリングし、composedであるため、document上のリスナーはShadow Root内のコンテナからのイベントも受け取れます。detail.containerがDOM内にある場合、イベントはコンテナで発火し、documentへ一度だけバブリングします。コンテナが切り離されている(isConnectedがfalse)場合、たとえばアダプターがunmountedを通知する前にReactがアイランドを削除した場合は、イベントはdocument自体で発火します。切り離された要素上のイベントはdocumentに届かないためです。どちらの場合も、detail.containerはその要素を指したままです。event.detailはLifecycleDetailです。

フィールド型意味
namestringマイクロアプリ名。
containerHTMLElementアプリが描画された要素。
hydratedbooleanアダプターが新規に描画せず、サーバーレンダリングされたマークアップを引き継いだ場合はtrue。
errorunknownスローされた値。errorイベントで設定されます。

mountedは、detail.nameに対してコンテナをマウント済みとして記録し、保留中のwhenMounted(name)のPromiseを解決します。unmountedはそのコンテナを記録から削除します。errorはイベントをディスパッチするだけです。React、Vue、Svelte、AngularのアダプターとcoreのdefineMicroAppは、マウントとアンマウントの完了後、およびどちらかがスローしたときにこれを呼び出します。

isMounted ​

ts
function isMounted(name: string): boolean;

nameのコンテナが少なくとも1つマウントされている間はtrueです。ランタイムはマウント済みのコンテナをアプリごとに追跡します。そのため、1つのモジュールが2つのコンテナにマウントされ、そのうち1つをアンマウントした場合、2つ目がアンマウントされるまでisMounted(name)はtrueのままです。

whenMounted ​

ts
function whenMounted(name: string, options?: { signal?: AbortSignal }): Promise<LifecycleDetail>;

最初のマウントのLifecycleDetailで解決します。アプリがすでにマウントされている場合は、まだマウントされている最も古いコンテナのdetailですぐに解決します。待機をやめるにはsignalを中断します。その場合、Promiseはsignalの理由でrejectされます。

ts
import { whenMounted } from '@tuvix.js/loader';

const { container, hydrated } = await whenMounted('header', { signal: AbortSignal.timeout(5000) });
console.log(`header mounted into #${container.id}, hydrated: ${hydrated}`);

onLifecycle ​

ts
function onLifecycle(type: LifecycleType, handler: (detail: LifecycleDetail) => void, target?: EventTarget): () => void;

target(デフォルトはdocument)でtuvix:<type>をリッスンし、リスナーを削除する関数を返します。デフォルトのtargetでは、接続されたコンテナでも切り離されたコンテナでも、ハンドラーはイベントごとに一度実行されます。コンテナ要素上のリスナーは、そのコンテナが切り離されている間にディスパッチされたイベントを受け取りません。

ts
import { onLifecycle } from '@tuvix.js/loader';

const stop = onLifecycle('error', ({ name, error }) => {
  console.error(`Micro app "${name}" failed`, error);
});

// later
stop();

このパッケージを使わない場合は、DOMイベントをリッスンします。

ts
import type { LifecycleEvent } from '@tuvix.js/loader';

document.addEventListener('tuvix:mounted', (event) => {
  const { name, hydrated } = (event as LifecycleEvent).detail;
  console.log(name, hydrated);
});

hasServerMarkup ​

ts
function hasServerMarkup(container: Element): boolean;

要素が子要素、または空白以外のテキストを持つテキストノードを持つ場合はtrueです。コメントと空白は対象外です。アダプターはこれを使って、ハイドレーションと新規描画のどちらを行うかを選びます。

型 ​

マイクロアプリモジュールが実装するインターフェース:

ts
interface MicroAppModule {
  bootstrap?: () => void | Promise<void>;
  mount: (context: MountContext) => void | Promise<void>;
  unmount: (context: UnmountContext) => void | Promise<void>;
  update?: (context: UpdateContext) => void | Promise<void>;
  [key: string]: unknown;
}

interface MountContext {
  container: HTMLElement;
  props?: Record<string, unknown>;
  /** App name the orchestrator registered this module under */
  name?: string;
  /** The orchestrator's event bus */
  bus?: MicroAppBus;
}

interface UnmountContext {
  container: HTMLElement;
}

interface UpdateContext {
  props: Record<string, unknown>;
  /** Container to update; without it, adapters update every mounted instance */
  container?: HTMLElement;
}

interface MicroAppBus {
  on<T = unknown>(event: string, handler: (data: T) => void): () => void;
  once<T = unknown>(event: string, handler: (data: T) => void): () => void;
  emit<T = unknown>(event: string, data?: T): void;
  off<T = unknown>(event: string, handler: (data: T) => void): void;
}

オーケストレーターは、マウントのたびにnameとbusを渡します。busはオーケストレーターの@tuvix.js/event-busインスタンスです。MicroAppBusはそれを構造的に記述するため、ローダーはイベントバスのパッケージに依存しません。

型意味
Entrystring | EntryConfig | EntryLoader。
EntryConfig{ scripts, styles?, type?, integrity? }。エントリーの形式を参照してください。
EntryLoader(context: EntryLoaderContext) => Promise<MicroAppModule | { default: MicroAppModule }>。
EntryLoaderContext{ signal: AbortSignal }。
ScriptType'module' | 'classic'。
MicroAppModuleマイクロアプリがエクスポートするライフサイクルオブジェクト。
MountContext, UnmountContext, UpdateContextmount、unmount、updateの引数。
MicroAppBus{ on, once, emit, off }。IEventBusはこれを満たします。
LoaderOptions{ timeout?, retries?, retryDelay?, globals? }。
LoadResult{ status, module, error, duration }。
LoadStatus'idle' | 'loading' | 'loaded' | 'error'。
CacheEntry{ module, loadedAt, entry }。キャッシュされた1つのモジュールです。
PrefetchStrategy'immediate' | 'idle' | 'hover' | 'none'。ローダーはこれを読み取りません。
ResourceOptions{ integrity?, signal? }。loadScriptとloadStyleで共通です。
LoadScriptOptionsResourceOptionsにtype?を加えたもの。
LoadStyleOptionsResourceOptionsにtimeout?を加えたもの。
PrefetchOptions{ as?, type?, integrity? }。
LifecycleType'mounted' | 'unmounted' | 'error'。
LifecycleDetail{ name, container, hydrated, error? }。
LifecycleEventCustomEvent<LifecycleDetail>。

エッジケース ​

  • タイムアウト。 timeoutは1回の試行全体を対象にします。発火すると、ローダーは試行のsignalを中断し、他の読み込みが待っていないその試行のタグを削除し、retryDelay待ってから再試行します。エラーは[Tuvix Loader] Loading "<name>" timed out after <timeout>msです。signalを無視するローダー関数はバックグラウンドで実行を続けます。ローダーはそれを待つのをやめます。
  • リトライ。 失敗した<script>は次の試行の前に削除されるため、リトライでは新しいタグが1つ追加されます。最後の試行の後、load()は最後のエラーとともに解決します。
  • 同じURLを複数のアプリで使う場合。 同じベンダースクリプトを列挙する2つのエントリーは、1つのタグと1回のダウンロードを共有します。
  • キャッシュされたエントリー。 load(name, otherEntry)はnameでキャッシュされたモジュールを返し、otherEntryを無視します。エントリーを切り替えるには、先にunload(name)を呼び出してください。
  • モジュールスクリプトはURLごとに一度だけ実行されます。 unload(name)の後、type: 'module'のスクリプトでregisterModuleを呼び出していたアプリは、同じURLからは再び読み込まれません。ブラウザはモジュールスクリプトを二度実行しないためです。読み込みはno exports foundで失敗します。ローダー関数のエントリーか、新しいURL(コンテンツハッシュや?v=2)を使ってください。
  • unloadは共有のscriptタグを削除します。 エントリーのすべてのスクリプトURLを削除します。別のアプリも列挙しているベンダースクリプトも含まれます。すでに実行されたコードは残ります。そのURLを列挙する以降の読み込みはタグを再び追加し、クラシックスクリプトは再実行されます。
  • CORS。 ローダーはすべてのタグにcrossorigin="anonymous"を設定します。別のオリジンのスクリプトやスタイルシートにはAccess-Control-Allow-Originヘッダーが必要です。ない場合はブラウザがブロックし、読み込みは失敗します。
  • UMDグローバル。 2つのUMDバンドルが同時に読み込まれると、ローダーは別の読み込みがすでに取得したグローバルをスキップし、残りを大文字と小文字や記号を無視してアプリ名と照合します(my-appはMyAppに一致します)。複数の候補が残った場合、読み込みはseveral globals with mount() appeared while it loaded (<names>)で失敗します。registerModuleを使えば推測は不要です。
  • ライフサイクルの欠落。 mountまたはunmountのないモジュールは[Tuvix Loader] Module "<name>" is missing required "unmount" function.で失敗します。
  • 何も見つからない場合。 スクリプトがモジュールを登録せず、UMDグローバルも定義しないURLエントリーは、Module "<name>" loaded but no exports foundで失敗します。
  • 複数のコンテナ。 ランタイムはマウント状態をコンテナごとに追跡します。2つのコンテナのうち1つをアンマウントしてもisMounted(name)はtrueのままです。whenMounted(name)は、まだマウントされている最も古いコンテナを返します。
  • 切り離されたコンテナ。 アダプターがunmountedまたはerrorを通知する前にホストフレームワークがコンテナを削除した場合、emitLifecycleはイベントをdocumentにディスパッチします。すべてのイベントを受け取るにはdocumentでリッスンしてください(onLifecycleのデフォルト)。
  • whenMountedはエラーを無視します。 tuvix:errorイベントでrejectされることはありません。signalを渡すか、onLifecycle('error', ...)でリッスンしてください。
  • サーバー。 ランタイム関数はwindowなしで動作します。CustomEventが未定義の場合、emitLifecycleはDOMイベントをスキップします。ModuleLoader、loadScript、loadStyle、prefetchResourceにはdocumentが必要です。

対象外の機能 ​

  • HTMLページを取得してスクリプトを抽出することはしません。スクリプトとスタイルのURLを列挙するか、ローダー関数を使ってください。
  • JavaScriptを隔離しません。スクリプトはページのグローバルスコープで実行されます。CSSの隔離には、オーケストレーターのsandboxオプションまたは@tuvix.js/sandboxを使ってください。
  • アプリをマウントしません。module.mountを呼び出すのは開発者またはオーケストレーターです。
  • ページの読み込みをまたいでキャッシュを保持しません。それはブラウザのHTTPキャッシュが行います。
  • PrefetchStrategyを読み取りません。prefetchを呼び出すタイミングは、オーケストレーターのprefetch設定が決めます。

0.4からの移行 ​

  • LoaderOptions.fetchとEntryConfig.htmlは削除されました。ローダーはこれらを一度も読み取っていませんでした。設定から削除してください。

  • loadScript(url, integrity)とloadStyle(url, timeoutMs)は引き続き動作します。オプション形式ではtypeとsignalが追加されます。

    ts
    import { loadScript } from '@tuvix.js/loader';
    
    // 0.4
    await loadScript('/vendor.js', 'sha384-abc');
    // 0.5
    await loadScript('/vendor.js', { integrity: 'sha384-abc', type: 'classic' });
  • .jsで終わるViteのES出力は、0.4ではクラシックスクリプトとして読み込まれ、最初のimport文で失敗していました。エントリーにtype: 'module'を設定してください。

  • type: 'module'のエントリーは、unloadの後に同じURLから再び読み込まれなくなりました。ローダー関数か新しいURLを使ってください。

  • キャッシュされたモジュールに対するload()は、0ではなく実際のdurationを報告します。

  • このページの0.4版では、loader.load(url)、result.app、onLoadStart、cache、loader.clear()が示されていました。これらはいずれも存在しませんでした。load(name, entry)、result.module、unload(name)、clearCache()を使ってください。