@ticatec/uniface-micro-frame
v5.0.2
Published
A powerful micro-frontend framework built on Svelte 5 for developing modular web applications with iframe-based architecture
Readme
Uniface Micro Frame
A micro-frontend framework built on Svelte 5, designed for modular web applications with an iframe-based architecture. Runs inside an iframe and provides hash routing, a stack-based page manager, an optional multi-tab shell, and pre-built page layouts.
Requires Svelte
^5.0.0,@ticatec/uniface-element^5.0.0, and@ticatec/i18n^0.5.0(see Peer dependencies).
Overview
- Iframe-based runtime — each sub-app is isolated in its own frame
- Hash routing —
location.hash→ lazy-loaded Svelte component - Stack-based pages — modal-style push/pop via
AppModule.showPage/closeActivePage - Multi-tab shell — parallel sub-apps under a tab strip (each tab is its own iframe)
- Pre-built page shells —
CommonPage/CommonFormPagewrapping the lower-levelPage - Global UI singletons —
Dialog,MessageBoxandIndicatorare self-mounting: each controller lazily mounts its own panel intodocument.bodythe first time you callshow()/showInfo(), soHomePagedoesn't need to render a board for any of them.Tooltipis the one exception — it has no controller of its own (just a globalmouseoverlistener), soHomePagestill renders<Tooltip/>once - TypeScript-first — full type definitions for every public API
Installation
pnpm add @ticatec/uniface-micro-frameQuick Start
1. Define routes and (optional) module initialization
import HomePage from '@ticatec/uniface-micro-frame';
import type { ModuleInitialize } from '@ticatec/uniface-micro-frame';
const routes = {
'/': () => import('./components/Dashboard.svelte'),
'/users': () => import('./components/UserList.svelte'),
'/users/:id': () => import('./components/UserDetail.svelte'),
'/settings': () => import('./components/Settings.svelte'),
};
const initializeModule: ModuleInitialize = async () => {
// one-time setup: API clients, stores, etc.
};2. Mount HomePage in your iframe entry
<script lang="ts">
import HomePage from '@ticatec/uniface-micro-frame';
import { routes, initializeModule } from './app';
</script>
<HomePage {routes} {initializeModule} />HomePage gates on window.self !== window.top. If it's not inside an iframe, it refuses to mount and surfaces a MessageBox instead.
Architecture
Two operating modes
| Mode | Container | Use when |
| --- | --- | --- |
| Single module (stack) | HomePage → ModuleHome | One sub-app per iframe; navigation is push/pop overlays. |
| Multiple modules (tabs) | TabModules | Several sub-apps in parallel, each in its own iframe; switching via tabs. |
Both modes share the same page shells, global UI singletons, and routing primitives — they differ only in how the top-level navigation surface is rendered.
Layered architecture
┌─────────────────────────────────────────────────────────────────┐
│ HomePage.svelte │
│ • gates on window.self !== window.top │
│ • mounts ModuleHome + Tooltip (Dialog/MessageBox/Indicator │
│ self-mount on first use) │
└──────────────────────┬──────────────────────────────────────────┘
│ routes, initializeModule
▼
┌─────────────────────────────────────────────────────────────────┐
│ ModuleHome.svelte (src/lib/module/) │
│ • parses location.hash → { path, query } │
│ • resolves path against routes map → Component │
│ • renders the active page │
│ • renders the AppModule page stack on top (with fade) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Page components (consumer-supplied or pre-built) │
│ • CommonPage / CommonFormPage (src/lib/pages/) │
│ • AppModule.showPage(...) pushes overlays onto the stack │
└─────────────────────────────────────────────────────────────────┘For the tab-based variant, HomePage/ModuleHome are replaced (or augmented) by TabModules, which keeps every tab's iframe mounted and toggles display to preserve session state.
Directory map
| Path | Contents | Deep dive |
| --- | --- | --- |
| src/lib/HomePage.svelte | Root component. Gates on iframe context, mounts Tooltip, defers to ModuleHome. | — |
| src/lib/common/ | Shared types: PageAttrs, CloseConfirm, ModuleInitialize, PageInitialize. | — |
| src/lib/i18nRes/ | i18n resource proxy — exposes i18nRes.microFrame.* strings (btnClose, moduleError, …). | — |
| src/lib/module/ | Stack-based runtime: hash routing + AppModule singleton + ModuleHome container. | README |
| src/lib/multiple-modules/ | Tab-based runtime: TabModules with parallel iframes. | README |
| src/lib/pages/ | Pre-built page shells: CommonPage, CommonFormPage. | README |
| src/lib/index.ts | Public barrel — exports HomePage (default) + ModuleInitialize type. | — |
Core Features
Page management
The framework provides a stack-based page manager through the AppModule singleton:
import { AppModule } from '@ticatec/uniface-micro-frame/module';
AppModule.showPage(EditForm, { id: 42 }); // pushes; ModuleHome renders it as an overlay
AppModule.closeActivePage(); // pops the top entryAppModule is initialized once by ModuleHome.onMount; every push fires an onPagesChange callback that re-renders the stack with a fade transition. The active hash page stays mounted underneath.
Page components
CommonPage
General-purpose page shell with optional sidebar and header extension:
<script lang="ts">
import CommonPage from '@ticatec/uniface-micro-frame/CommonPage';
import type { PageAttrs } from '@ticatec/uniface-micro-frame/common';
const page$attrs: PageAttrs = {
title: 'My Page',
comment: 'Page description',
};
</script>
<CommonPage {page$attrs} canBeClosed round shadow>
{#snippet sidebar()}
<nav style="width: 240px; height: 100%">...</nav>
{/snippet}
{#snippet headerExt()}
<button onclick={reload}>Refresh</button>
{/snippet}
<!-- default snippet becomes the main content -->
<div>Page content goes here</div>
</CommonPage>CommonFormPage
Form-oriented shell with a built-in ActionBar and an auto-appended Close button:
<script lang="ts">
import CommonFormPage from '@ticatec/uniface-micro-frame/CommonFormPage';
import type { ButtonActions } from '@ticatec/uniface-element/ActionBar';
const actions: ButtonActions = [
{ label: 'Save', type: 'primary', handler: save },
{ label: 'Reset', type: 'secondary', handler: reset },
];
</script>
<CommonFormPage page$attrs={{ title: 'Edit Form' }} {actions} canBeClosed>
<form>...</form>
</CommonFormPage>Routing system
Hash-based with :param capture:
const routes = {
'/users/:id': () => import('./UserDetail.svelte'),
'/posts/:category/:slug': () => import('./PostView.svelte'),
};
// UserDetail receives `id` as a prop; PostView receives `category` and `slug`.
// Query string params (?key=value) are also spread onto the component as props.Query parameters are decoded exactly once: key=value pairs are split from the hash first, and only then is each key/value individually run through decodeURIComponent. Values are expected to be URL-encoded on the way in — encode any =, &, or % that's actually part of the value itself (e.g. %3D, %26, %25), rather than relying on the router to figure out delimiters vs. content. A key with no = (or with = but nothing after it) still comes through, with an empty-string value.
Module initialization
import type { ModuleInitialize } from '@ticatec/uniface-micro-frame';
const initializeModule: ModuleInitialize = async () => {
await setupApiClient();
initializeGlobalStores();
configureLibraries();
};Runs once before the first page renders.
Page lifecycle hooks
ModuleInitialize— async, runs once before the first page renders.PageInitialize— async, per-page hook (consumer-defined).CloseConfirm— async guard() => Promise<boolean>consulted byCommonPage.closePagebeforeAppModule.closeActivePagefires.
Internationalization
The framework ships with built-in English strings baked into src/lib/i18nRes/i18nRes.ts. The strings it owns:
| Key | Used by |
| --- | --- |
| uniface.microFrame.btnClose | Close button label in CommonFormPage |
| uniface.microFrame.moduleError | Error fallback page |
| uniface.microFrame.pageNotInFrame | MessageBox shown by HomePage when not in an iframe |
| uniface.microFrame.indicatorLoadModule | Loading indicator while the module boots |
To switch language, the consumer configures the shared @ticatec/i18n context before (or inside) HomePage mount:
1. Set the active language
import { i18n } from '@ticatec/i18n';
i18n.language = 'zh-CN';2. Load translation resources
import { i18nUtils } from '@ticatec/i18n';
await i18nUtils.loadResources('/assets/uniface_zh-CN.json');Multiple URLs are merged in order — pass additional files to layer your app's strings on top of the framework's.
3. JSON file shape
Framework strings live under the uniface.microFrame.* namespace. A typical localized file:
{
"uniface": {
"microFrame": {
"btnClose": "关闭",
"moduleError": "无法加载模块。",
"pageNotInFrame": "无法显示不在 iframe 中的页面。",
"indicatorLoadModule": "正在加载模块..."
}
}
}If a key is missing from the loaded resources, the framework falls back to the English defaults — so you only need to ship the keys you actually want to override.
4. Real-world wiring
Typically done in initializeModule so the resources are ready before the first page renders:
import { i18n, i18nUtils } from '@ticatec/i18n';
import type { ModuleInitialize } from '@ticatec/uniface-micro-frame';
const initializeModule: ModuleInitialize = async () => {
const lang = navigator.language; // e.g. "zh-CN"
i18n.language = lang;
await i18nUtils.loadResources(`/assets/uniface_${lang}.json`);
// ...other one-time setup
};5. Your own app strings
Create a parallel namespaced proxy for app-level text. Anything missing from loaded resources falls back to the default object:
import { i18nUtils } from '@ticatec/i18n';
const appRes = i18nUtils.createResourceProxy({
welcome: 'Hello {{name}}',
}, 'myApp');
appRes.welcome({ name: 'World' }); // "Hello World"Public API surface
// Root — main component + module init type
import HomePage from '@ticatec/uniface-micro-frame';
import type { ModuleInitialize } from '@ticatec/uniface-micro-frame';
// Stack runtime internals
import { AppModule } from '@ticatec/uniface-micro-frame/module';
import ModuleHome from '@ticatec/uniface-micro-frame/module/ModuleHome';
// Tab runtime
import TabModules from '@ticatec/uniface-micro-frame/multiple-modules';
// Pre-built page shells
import CommonPage from '@ticatec/uniface-micro-frame/CommonPage';
import CommonFormPage from '@ticatec/uniface-micro-frame/CommonFormPage';
// Shared types
import type { PageAttrs, CloseConfirm, PageInitialize } from '@ticatec/uniface-micro-frame/common';See package.json exports field for the authoritative list of subpaths.
Svelte 5 conventions
Every .svelte file in this package follows the same patterns:
- Props —
interface Props { ... }+let { ... }: Props = $props();. Noexport let. - State —
let x = $state(...)for anything read in the template and mutated from event handlers. - Derived —
let y = $derived(...)instead of$: y = .... - Slots —
Snippetprops +{#snippet name()}blocks. No<slot>/slot="...". - Dynamic components — capitalized alias inside
{#if}/{#each}({#if comp}{@const C = comp}<C/>{/if}), no<svelte:component>. - Refs — plain
let x: T;when only used in methods;let x: T | undefined = $state(undefined);when read in the template.
Styling
Import the framework's CSS for proper styling:
@import '@ticatec/uniface-micro-frame/uniface-micro-frame.css';The same file is also available via the ./styles subpath:
@import '@ticatec/uniface-micro-frame/styles';Peer dependencies
uniface-micro-frame 5.0.0 requires:
svelte^5.0.0@ticatec/uniface-element^5.0.0— UI boards,Page,ActionBar,Tabs,MessageBox,Indicator@ticatec/i18n^0.5.0—i18nUtils.createResourceProxy
These are declared as peerDependencies in package.json — install them alongside this package rather than relying on a transitive install.
Development
pnpm run dev # start the dev server
pnpm run build # build the package (CSS + svelte-package)
pnpm run check # svelte-check type checking
pnpm run test # run the vitest suite (tests/**/*.test.ts)
pnpm run package # full publish gate: check && test && svelte-package && copy:css && publintLicense
MIT
Author
Henry Feng
For deeper dives into each subsystem, see:
- Single-module runtime — routing, page stack, AppModule singleton.
- Multi-module runtime — tabbed iframe container.
- Page shells — CommonPage / CommonFormPage.
