注册子应用
通常可以用容器实例的 registerApp 方法来注册一个子应用,也可以用 registerApps 来批量注册。
它们返回的是 AppAPI 对象,该对象提供了一系列的属性用于细粒度访问该子应用的状态、控制子应用的行为以及监听子应用的事件。
interface AppAPI<AdditionalOptions, CustomProps> {
get hooks(): AppHooks;
get state(): AppState;
get appElement(): HTMLElement | null;
get name(): string;
get latestDirective(): AppDirective;
get timeouts(): AppTimeouts;
readonly load: () => Promise<LifecycleFns<CustomProps>>;
readonly on: <T extends keyof AppEvent>(
event: T,
listener: (event: AppEvent[T]) => unknown,
context?: unknown,
) => this;
readonly once: <T extends keyof AppEvent>(
event: T,
listener: (event: AppEvent[T]) => unknown,
context?: unknown,
) => this;
readonly off: <T extends keyof AppEvent>(
event: T,
listener: (event: AppEvent[T]) => unknown,
context?: unknown,
) => this;
lifecycle: LifecycleAPI<CustomProps>;
options: AppOptions<CustomProps> & AdditionalOptions;
isUnloaded: boolean;
update(customProps: CustomProps): Promise<void>;
}
在 Haploid.js 中,支持以多种方式来声明一个子应用的入口(entry)。
入口类型
HTML entry
从 qiankun 开始,几乎所有微前端框架都支持 HTML 格式的 entry,即从 HTML 页面中解析 JS 和 CSS。只是 HTML 代码千变万化,不同元素包含的不同属性都可能影响 JS/CSS 的可用性、执行方法和执行顺序。
Haploid.js 对 HTML 元素的下列属性都有支持,尽可能地兼容了HTML:
- <style>
- type 属性,不存在或者取值是 “text/css” 都是合法的,其它不合法
- media 属性,需要保留
- <link>
- disabled 属性,存在则不合法;
- type 属性,不存在或者取值是 “text/css” 都是合法的,其它不合法;
- rel 属性,不存在或者取值是 “stylesheet” 都是合法的,其它不合法;
- href 属性,不存在或为空则不合法;
- media 属性,需要保留;
- crossorigin 属性,需要保留
- <script>
- src 属性,可能是相对路径,需要转换成绝对路径;
- text 属性,内联代码,只有当 src 属性不存在时才有意义;
- async 属性;
- defer 属性,只对外链有效;
- type 属性,不存在或者等于 module 或者等于 “text/javascript” 是合法的,其它不合法;
- entry 属性;
- crossorigin 属性;
- nomodule 属性
提示
如果 entry 以 .htm/.html/.xhtml/.shtml 结尾,或者请求 entry 返回的 Content-Type 包含 text/html 或 application/xhtml+xml,则该 entry 被认定为 HTML 格式。
JSON entry
JSON 支持两个版本的格式:
interface JSONEntryV1 {
version: 1;
module?: "esm" | "umd";
initial?: {
css?: string[];
js?: string[];
};
async?: {
css?: string[];
js?: string[];
};
}
interface JSONEntryV2 {
version: 2;
module?: "esm" | "umd";
css?: string[];
js?: string[];
}
支持 JSON 的原因是因为相比于 HTML,JSON 的语义化更强,更易解析,如果子应用没有复杂的逻辑,更建议用这种格式。不过这需要主动生成 JSON 文件。
目前暂时不支持定制 JSON 格式。
提示
如果 entry 以 .json 结尾,或者请求 entry 返回的 Content-Type 包含 application/json,则该 entry 被认定为 JSON 格式。
JS entry
Haploid.js 还支持 JS 格式的 entry,不过这样就缺少了对 CSS 的描述。
提示
对于如果 entry 以 .js 或者 .mjs 结尾,或者请求 entry 返回的 Content-Type 包含 text/javascript、application/javascript 或者 application/ecmascript,则该 entry 被认定为 JS 格式。
JS 分为 ESM 和 UMD 两种格式。由于这两种的执行方式不同,因此 Haploid.js 需要判断格式,以下列方法:
- 如果 entry 以 .js 结尾,则认为其为 UMD 格式;
- 如果 entry 以 .mjs 结尾,则认为其为 ESM 格式;
- 尝试以 Function(code) 的方式创建函数,如果抛出以下异常,则认为为 ESM 格式,否则为 UMD 格式
/Unexpected token 'export'//Cannot use import statement outside a module//Cannot use 'import.meta' outside a module/ /Unexpected keyword 'export'//Unexpected identifier .+ import call expects exactly one argument//import.meta is only valid inside modules/ /(import|export) declarations may only appear at top level of a module//import.meta may only appear in a module/
注意
上面的方法并不保险,为避免产生不必要的歧义和副作用,如果一定要使用 JS entry,请尽可能指定明确的后缀名。
Object entry
无论何种 entry 格式,最终的目标都是从中获取 JS 和 CSS,执行后得到子应用的生命周期方法。如果生命周期方法是现成的,那么就省去了访问 entry 的过程。
rc.registerApp({
name: "foo",
lifecycle: {
mount: () => {},
unmount: () => {},
bootstrap: () => {},
update: () => {},
},
activeWhen: "/foo",
});
这种形式通常用于主子应用在一个构建应用中的场景。
异步入口
无论使用何种格式的 entry,Haploid.js 都支持 函数变种、Promise变种 和 两者混合变种。
// 函数变种
rc.registerApp({
name: "foo",
entry: () => "https://foo.com/entry",
activeWhen: "/foo",
});
// Promise变种
rc.registerApp({
name: "foo",
entry: Promise.resolve("https://foo.com/entry"),
activeWhen: "/foo",
});
// 函数-Promise混合变种
rc.registerApp({
name: "foo",
entry: () => Promise.resolve("https://foo.com/entry"),
activeWhen: "/foo",
});