当前文档对应 qiankun 3.0(RC),安装请使用 npm i qiankun@rc;2.x 文档见 v2 站点。
Skip to content

Vue <MicroApp> 组件(@qiankunjs/vue) ​

@qiankunjs/vue 提供 MicroApp 组件,用于以声明式方式加载、挂载、更新和卸载 qiankun 微应用。该组件封装了 loadMicroApp,并根据 Vue 组件的生命周期管理微应用实例。

组件基于 vue-demi 构建,同一份构建产物同时支持 Vue 2 和 Vue 3。

包中还提供用于主应用路由导航的 MicroAppLink,其用法见下文。

安装 ​

bash
npm install @qiankunjs/vue@rc qiankun@rc

主应用必须安装 vue,版本范围为 ^2.0.0 || >=3.0.0。Vue 2 项目还需要安装 @vue/composition-api,因为组件通过 vue-demi 使用组合式 API。

使用前提

MicroApp 组件直接调用 loadMicroApp,单独使用时无需调用 registerMicroApps 或 start。如果同一主应用还使用基于路由的注册方式,则仍需调用 start。挂载和更新操作与 single-spa 生命周期的对应关系参见微应用生命周期与 props。

MicroAppLink 用于 Vue 3 主应用的 registerMicroApps 路由模式。主应用完成注册并调用 start 后,点击链接即可通过 single-spa 的 navigateToUrl 切换 URL,由已注册的 activeRule 决定微应用的挂载与卸载。链接本身不加载微应用。

vue
<script setup lang="ts">
import { MicroAppLink, type MicroAppLinkProps } from '@qiankunjs/vue';

const appLink: MicroAppLinkProps = {
  to: '/app1',
  className: 'nav-link',
  activeClassName: 'is-active',
};
</script>

<template>
  <nav>
    <MicroAppLink v-bind="appLink">应用一</MicroAppLink>
    <MicroAppLink to="/app2/settings" replace>应用二设置</MicroAppLink>
  </nav>
</template>
属性类型说明
tostring必填。 目标 URL,作为链接的 href。
replaceboolean是否替换当前历史记录。默认值为 false,导航时新增一条历史记录。
classNamestring链接的 CSS 类名,模板中也可写为 class-name。
activeClassNamestring当前地址匹配目标地址前缀时追加的 CSS 类名,模板中也可写为 active-class-name。默认不追加。

默认插槽提供链接内容。MicroAppLinkProps 从包入口导出。除组件自身使用的属性外,class、target、rel、download、aria-*、data-* 和事件监听器等原生链接属性都会传递给 <a>;href 由 to 指定。

组件先执行传入的 @click 监听器,再判断是否接管导航。只有未被 preventDefault() 取消、未按下 Ctrl / Meta / Shift / Alt、目标为当前窗口(有效的 target 为空或为 _self,未设置时遵循页面的 <base target>)的同源 HTTP(S) 链接左键点击,才会阻止默认行为并在当前页面内导航。外链、下载链接、其他窗口目标及带修饰键的点击均保留浏览器行为。可通过 @click.prevent 取消组件导航。replace 为 true 时使用 history.replaceState 替换当前记录,并发送 popstate 通知路由更新。

activeClassName 将目标 URL 解析后,以它的 pathname + search + hash 为前缀匹配当前地址的对应部分。例如,to="/app1" 会匹配 /app1/settings,也会匹配 /app10;to="/" 会匹配所有路径。查询参数和哈希若包含在 to 中,也参与前缀匹配。这是字符串前缀匹配,不解析路由参数;需要精确匹配时,可由主应用自行设置 className 和 aria-current。

基本用法 ​

vue
<script setup>
import { MicroApp } from '@qiankunjs/vue';
</script>

<template>
  <micro-app name="app1" entry="http://localhost:8000" />
</template>

name 和 entry 是仅有的两个必填 prop。name 用于标识当前实例,entry 用于指定微应用的 HTML 入口 URL。缺少其中任意一项时,组件仅输出错误日志,不会加载微应用,也不会抛出异常。

组件会渲染一个 class 为 qiankun-micro-app-container 的 <div>,并将微应用内容流式写入该容器。只有启用加载状态或错误边界时,组件才会额外渲染一层包裹元素。详见加载与错误界面。

Props ​

Prop类型默认值说明
namestring—必填。 微应用实例的名称;值发生变化时会重新挂载。
entrystring—必填。 微应用的 HTML 入口 URL。
settingsAppConfiguration{ sandbox: true }传递给 loadMicroApp 的加载器和沙箱配置。参见 AppConfiguration。
lifeCyclesLifeCyclesundefined由主应用提供的生命周期钩子,包括 beforeLoad、beforeMount、afterMount、beforeUnmount 和 afterUnmount。每项可传入函数或函数数组。参见生命周期钩子。
autoSetLoadingbooleanfalse微应用加载期间渲染内置的加载指示器。
autoCaptureErrorbooleanfalse加载失败时渲染内置的错误边界。
wrapperClassNamestringundefined包裹元素上的额外 CSS 类名。仅在启用加载状态或错误边界时生效。
classNamestringundefined挂载容器元素上的额外 CSS 类名。
appPropsobjectundefined传递给微应用的 props。Vue 绑定仅通过该属性向微应用传递数据。

settings 的默认值与 React 绑定不同

Vue 绑定的 settings 默认值为 { sandbox: true },React 绑定则不设置默认值,不会替你填任何默认项。两者的 sandbox 在 qiankun 核心运行时中默认值都为 true,因此不额外传值时行为一致。

业务数据通过 appProps 传递

React 绑定会将 <MicroApp> 上的附加 prop 传递给微应用,Vue 绑定则不会传递任意附加属性。业务数据必须放入 appProps 对象,未声明的其他属性会被忽略。当前实现还会将 autoSetLoading、autoCaptureError 和 appProps 对象本身传入微应用;业务代码不应依赖这些组件控制字段。

settings(AppConfiguration) ​

settings 与 loadMicroApp 的第二个参数结构相同。完整定义参见 AppConfiguration,字段为 fetch、streamTransformer、nodeTransformer 和 sandbox(默认值为 true)。样式隔离、额外全局变量、宿主全局对象(incubatorContext)和隔离插件均位于 sandbox 对象内部。

vue
<template>
  <micro-app
    name="app1"
    entry="http://localhost:8000"
    :settings="{ sandbox: { styleIsolation: true } }"
  />
</template>

如需为特定微应用关闭 JavaScript 沙箱,可传入 :settings="{ sandbox: false }"。相关行为参见 JavaScript 隔离和样式隔离。

向微应用传递 props(appProps) ​

需要传递给微应用的数据应放入 appProps:

vue
<script setup>
import { reactive } from 'vue';
import { MicroApp } from '@qiankunjs/vue';

const appProps = reactive({ userId: 42, theme: 'dark' });
</script>

<template>
  <micro-app name="app1" entry="http://localhost:8000" :appProps="appProps" />
</template>

这些数据会作为 props 参数传给微应用的 mount、unmount 和可选的 update;bootstrap 只接收稳定的初始化上下文:

ts
// 微应用内部
export async function mount(props) {
  console.log(props.userId); // 42
}

组件会深度侦听 appProps。修改嵌套值(例如 appProps.theme = 'light')会尝试更新当前实例。执行更新前,微应用必须导出 update 生命周期、处于 MOUNTED 状态,并且尚未开始卸载。相关说明参见应用间共享状态与通信。

update 仅在挂载完成后执行

组件会等待 mountPromise 完成,再按顺序处理更新,并且仅在 Parcel 状态为 MOUNTED 时调用 update。挂载期间发生的中间状态变化不保证逐次触发更新。

加载与错误界面 ​

加载指示器和错误边界均需显式启用。如果未启用这两项功能,也未提供对应插槽,组件只渲染挂载容器 <div>。如果设置了 autoSetLoading、autoCaptureError、#loader 插槽或 #error-boundary 插槽中的任意一项,组件会额外渲染 class 为 qiankun-micro-app-wrapper 的包裹元素,用于容纳加载节点、错误节点和挂载容器。

自动加载与错误捕获 ​

通过以下两个布尔 prop 启用内置指示器:

vue
<script setup>
import { MicroApp } from '@qiankunjs/vue';
</script>

<template>
  <micro-app
    name="app1"
    entry="http://localhost:8000"
    autoSetLoading
    autoCaptureError
  />
</template>

内置界面仅提供基础占位内容:默认加载界面渲染文本 loading...,默认错误边界渲染包含 error.message 的 <div>。生产环境通常应使用下文介绍的插槽提供自定义界面。

加载状态的初始值

Vue 绑定将 loading 初始化为 false,React 绑定的初始值则为 true。微应用开始加载时,该状态会设为 true;启用 autoSetLoading 后,组件会在 mountPromise 完成时将其恢复为 false。未启用 autoSetLoading 时,组件不会渲染内置加载界面。

自定义 loader 插槽 ​

可通过 #loader 作用域插槽渲染自定义加载指示器。组件会将布尔值 loading 直接传给插槽:加载期间为 true,加载结束后为 false。

vue
<script setup>
import CustomLoader from '@/components/CustomLoader.vue';
import { MicroApp } from '@qiankunjs/vue';
</script>

<template>
  <micro-app name="app1" entry="http://localhost:8000" autoSetLoading>
    <template #loader="loading">
      <custom-loader :loading="loading" />
    </template>
  </micro-app>
</template>

#loader 插槽的优先级高于内置加载界面。提供该插槽后,组件不会渲染默认加载界面;仍需设置 autoSetLoading,组件才会在 mountPromise 完成后自动将 loading 设为 false。

自定义错误边界插槽 ​

可通过 #error-boundary 作用域插槽渲染自定义错误界面。组件会将 Error 实例直接传给插槽。该插槽仅在发生错误后渲染。

vue
<script setup>
import CustomErrorBoundary from '@/components/CustomErrorBoundary.vue';
import { MicroApp } from '@qiankunjs/vue';
</script>

<template>
  <micro-app name="app1" entry="http://localhost:8000">
    <template #error-boundary="error">
      <custom-error-boundary :error="error" />
    </template>
  </micro-app>
</template>

未捕获的错误会重新抛出 ​

如果既未启用 autoCaptureError,也未提供 #error-boundary 插槽,则 load、bootstrap 和 mount 阶段的错误会从异步加载流程中重新抛出。建议启用组件内置错误界面或提供自定义错误界面,避免产生未处理的 Promise 拒绝。

WARNING

启用 autoCaptureError 或提供 #error-boundary 插槽后,组件会通过错误界面呈现异常,不再重新抛出。同一微应用应选择一种错误处理方式。详见处理微应用错误。

重新挂载与实例句柄 ​

组件仅侦听 name 的变化来触发重新挂载。修改该 prop 会卸载当前微应用并创建新实例;仅修改 entry、settings 或 lifeCycles 不会创建新实例。组件销毁时,onBeforeUnmount 会自动卸载微应用;卸载操作会等待正在进行的 mountPromise 完成,以保持挂载和卸载的执行顺序。

可以通过组件实例的 microApp 和 microAppRef 两个属性访问当前微应用实例,两者均指向同一个 MicroApp Parcel 句柄。可通过模板 ref 访问该句柄:

vue
<script setup>
import { ref, onMounted } from 'vue';
import { MicroApp } from '@qiankunjs/vue';

const microAppComp = ref();

onMounted(() => {
  // MicroApp 句柄:getStatus()、mountPromise、unmount()、update() 等
  console.log(microAppComp.value?.microApp?.getStatus());
});
</script>

<template>
  <micro-app ref="microAppComp" name="app1" entry="http://localhost:8000" />
</template>

该句柄即 loadMicroApp 返回的 MicroApp:@qiankunjs/single-spa(qiankun 内置的 single-spa fork)的 Parcel,外加 unload()。getStatus() 返回 NOT_LOADED、LOADING_SOURCE_CODE、NOT_BOOTSTRAPPED、BOOTSTRAPPING、NOT_MOUNTED、MOUNTING、MOUNTED、UPDATING、UNMOUNTING、UNLOADING、SKIP_BECAUSE_BROKEN 或 LOAD_ERROR。完整类型参见类型参考。

由组件管理生命周期

应优先通过 prop(name、appProps)管理微应用,而不是直接调用句柄上的 unmount()、update() 或 unload()。组件会按顺序执行卸载,并协调并发更新;直接调用句柄方法可能与组件的内部状态发生冲突。

组件销毁时只调用 unmount(),已加载的实例会保留,再次渲染同名组件时直接复用。需要释放这些资源时,在组件销毁后调用 unloadMicroApp(name)。

CSS 钩子 ​

CSS 类名与 React 绑定一致。组件会添加两个稳定的类名,并在提供 wrapperClassName 或 className 时将自定义值添加到对应类名之前。

元素始终应用的类名prop 提供的额外类名
包裹元素(仅在启用加载状态或错误边界时存在)qiankun-micro-app-wrapperwrapperClassName
挂载容器qiankun-micro-app-containerclassName
css
/* 所有微应用的挂载容器 */
.qiankun-micro-app-container {
  min-height: 320px;
}

/* 承载加载界面和错误界面的包裹元素 */
.qiankun-micro-app-wrapper {
  position: relative;
}

包裹元素仅在启用加载状态或错误边界时存在。因此,如果 <micro-app> 未配置加载或错误界面,wrapperClassName 不会产生效果。

完整示例 ​

vue
<script setup>
import { reactive } from 'vue';
import { MicroApp } from '@qiankunjs/vue';
import Spinner from '@/components/Spinner.vue';
import ErrorPanel from '@/components/ErrorPanel.vue';

const appProps = reactive({ userId: 42 });
</script>

<template>
  <micro-app
    name="app1"
    entry="http://localhost:8000"
    :settings="{ sandbox: { styleIsolation: true } }"
    :appProps="appProps"
    autoSetLoading
    wrapperClassName="my-wrapper"
    className="my-container"
  >
    <template #loader="loading">
      <spinner v-if="loading" />
    </template>
    <template #error-boundary="error">
      <error-panel :message="error.message" />
    </template>
  </micro-app>
</template>

相关内容 ​

基于 MIT 协议发布