Appearance
API 索引与排错验收
本页用于查找入口与定位问题,详细示例见相应章节。类型以 packages/fin-micro/src 和构建后的 dist/types 为准;业务参数的 TypeScript 检查不等于运行时校验。
1. 入口与关键 API
| 导入路径 | 主要导出 | 使用位置 |
|---|---|---|
@axewo/fin-micro/sdk | getMicroBridge、HostCallError、NavigationError、类型与 createNotificationBus | 各框架子应用/共享契约 |
@axewo/fin-micro/core | createMicroHost、MicroHostOptions、PreparedNavigation、HostCapabilityRegistry | 基座平台层 |
@axewo/fin-micro/runtime | createWujieRuntime、ObservableMicroRuntime、MicroRuntimeError、MicroRuntimeResources、WujieRuntimeOptions | 基座工作区 |
@axewo/fin-micro/vue | MicroAppContainer、MicroTabCache、MicroRouteView、useMicroHost、useMicroTab、useMicroPage、connectMicroRouter | Vue setup/组件树 |
@axewo/fin-micro/browser | connectMicroBrowser、encodeMicroLocation、decodeMicroLocation | 认证就绪后的基座初始化 |
@axewo/fin-micro/iframe | createIframeRpc、IframeRpcError、exposeHostCapabilities | 需要跨窗口请求的双端 |
宿主创建选项
createMicroHost(apps, options) 的 options 支持:
| 字段 | 缺省与要求 |
|---|---|
capabilities | 不配置则没有公开宿主能力;每个能力独立 handler 和 apps 白名单 |
callTimeoutMs | 30000 毫秒;正有限数且 ≤ 2147483647 |
guardTimeoutMs | 60000 毫秒;单个离页守卫上限 |
navigationTimeoutMs | 120000 毫秒;包含排队、全部守卫与外部路由确认 |
maxPendingNavigations | 100;包含执行中的事务,超限以 QUEUE_FULL 拒绝 |
session | 初始公开会话,不包含凭据 |
canOpen | 可选同步准入;配置后只有严格返回 true 放行 |
canNavigate | navigateTo/back(destination) 的显式跨应用目标默认拒绝,只有严格同步 true 放行 |
crossAppBack | 默认 false;true 时记录跨应用来源并支持无参 back 返回,模板已开启 |
onError | 监听器/清理错误处理,不能借此把原始敏感异常透传子应用 |
宿主的同步 openTab/activateTab/closeTab 是底层状态原语。需要守卫和原子批量确认的交互统一走 operate();不要在菜单或按钮里绕过事务层。
prepare/coordinateNavigation 是外部 Router 适配底层接口;Vue 项目使用 connectMicroHostRouter 即可。资源预算、独立桥撤销、卸载上限及完整错误码见运行时治理与故障恢复。
页面 SDK 快查
| 方法 | 结果与作用 |
|---|---|
openTab(input) | Promise<MicroTab>,本应用打开;跨应用拒绝 |
navigateTo(input, { signal }?) | Promise<NavigationResult>,可申请宿主授权的跨应用打开 |
refreshCurrent() | 原位刷新,成功生成新 key,旧 SDK 失效 |
closeCurrent() | Promise<boolean>,只关闭原 key 并按宿主邻页回落;dispose 后仍可尝试关闭原 key |
back(destination?) | 关闭功能页;有参数优先去指定目标,无参数返回来源,跨应用来源须 crossAppBack |
operate(operation) | 本应用范围的导航事务;全局批量仅宿主可执行 |
beforeLeave(guard) | 返回取消函数;页面 SDK dispose 时自动注销 |
setUnsavedChanges(formId, state / null) | 登记/清理本页未保存表单元数据,归属固定 key;Vue 页面优先使用 Hook |
callHost/callHostWithOptions | 类型化请求响应,异常为公开 HostCallError |
emit/on | 无响应业务事件;on 第二参数是可信来源元信息 |
dispose() | 释放 SDK、订阅和守卫,不关闭页签 |
AppBridge 还提供 protocol/negotiate/getSession/subscribeSession/getState/subscribe/page/bindPage/ready。应用级 callHost 没有 context.tab;页级调用才绑定固定页面生命周期。
会话失效与权限契约
| 入口 / API | 契约与边界 |
|---|---|
/sdk:AuthCapabilities、AuthRequestTicket | 在 Token 能力基础上定义 beginAuthRequest() 与 reportAuthFailure(ticket);不向子应用暴露私有版本 |
/sdk:createAuthFailureCapabilities({ apps, getRevision, invalidate }) | apps 为可信白名单;getRevision 每次身份/凭据变动递增;invalidate 必须同步撤销身份并推进版本,不等待退出守卫 |
/sdk:hasPermission/hasAny/hasAll | 普通标识完全匹配,无命名格式要求;已认证且包含精确标识 *:*:* 时放行全部有效权限;未登录、空权限名与空集合拒绝,不推断角色或其他通配符 |
/vue:installPermissionDirectives(app, identityRefOrGetter) | 挂载前注册 v-hasPermi,字符串判断单项,非空合法数组判断任意一项;登录/退出/撤权后自动更新并支持恢复显示,模板已注册 |
/vue:useUnsavedChanges(getValues, options?) | setup 调用一次,返回 { dirty, setBaseline };自动比较快照,第二参数支持 enabled/formId/label/message |
/vue:installMicroConfirmation(app) / useMicroConfirmation() | App 内统一确认状态,由项目 ConfirmationDialog.vue 使用 fin-ui FDialog 展示 |
/vue:connectUnsavedChangesRouter(router, confirmation, authenticated) | 独立运行的路由保护;返回清理函数,嵌入时由基座处理 |
/vue:createHasPermiDirective(identityRefOrGetter) | 返回同一指令实现,可用于局部注册;每个 App 使用自己的响应式公开会话 |
/vue:usePermission(sessionRefOrGetter?) | 默认读取已注入的 MicroSession;返回 hasPermission/hasAny/hasAll 与 can;can 返回 computed,其他函数在模板/computed 中读取最新会话 |
指令注册、完整页面与表格操作列、v-if 和全部满足的示例见按钮权限指令。
失效票据是同窗口原对象能力关联,不含 Token:伪造、复制、跨应用、重用及旧版本票据不会撤销当前身份;不支持序列化或 iframe 转发。可信白名单不能隔离同窗口 XSS。并发首次有效失效后其余票据过期;旧 401 不得退出新登录。服务端注销失败不能恢复本地身份。正常主动退出使用一次系统确认,确认后清理工作区,不逐页运行未保存确认。没有自动刷新、重发或写请求重放。
表单加载、保存快照、动态文案与多页签完整示例见未保存表单与统一确认。
生成模板配置、错误分类、启动兜底、独立 demo 身份策略与权限完整示例见仓库 packages/fin-micro/README.md。模板的 utils/appConfig.ts 是唯一公开配置校验入口;utils/errorUtils.ts 只反馈安全固定文案。部署配置解析失败也经动态启动入口捕获;更正环境后须重新构建/刷新,普通重试不保证清除浏览器 ESM 失败缓存。
ts
// setup 中使用订阅得到的 SessionSnapshot ref;权限不是一次性复制的数组。
const permission = usePermission(identity)
const canRead = permission.can('demo:read')
const canManage = computed(() => permission.hasAll(['demo:read', 'demo:write']))
const canUse = computed(() => permission.hasAny(['demo:read', 'demo:write']))独立 Vue 应用通过 createMicroSession({ auth }) 管理真实后端身份;启动使用 installMicroSession 注入,组件通过无参 usePermission() 消费。模板统一要求先登录,列表不额外要求功能权限,详情要求 demo:read;未使用的基础计数示例与 /demo 入口已移除。开发 Token 验证、正式登录、路由与菜单接线见接入说明。
2. 导航状态与异常要分开处理
| 状态 | 含义 | 建议 |
|---|---|---|
committed | 导航状态完成提交 | 不等于目标资源加载完成 |
cancelled | 离页守卫返回 false | 留在当前页,不提示系统故障 |
noop | 没有状态变化 | 不重复创建页签/请求 |
stale | 意图过期、未提交取消或状态版本已变化 | 不用旧结果覆盖当前 UI |
公开导航异常码包括 INVALID_OPERATION、FORBIDDEN、PAGE_RELEASED、HOST_DISPOSED、GUARD_FAILED、GUARD_TIMEOUT、NAVIGATION_TIMEOUT、QUEUE_FULL、CANCELLED、STALE。不要据此假设每个方法都会抛出每个码;例如 navigateTo 的未提交 signal 取消表现为 status: 'stale'。按具体方法结果和 code 分支,不匹配中文 message。
3. 常见问题
3.1 新增 API 在应用里提示不存在
先构建 fin-micro,再重启应用类型服务。核对应用实际解析的是工作区包还是外部旧版本;确认 dist/types 已更新。不要增加不存在的 API 类型声明来绕过错误。
3.2 两个详情页参数相同或草稿串页
依次检查:
- tabId 是否误用了同一个固定
detail?建议使用order:detail:${id}。 - 是否把全局当前 route 直接注入所有缓存页?需要每个 tab 的固定 route 上下文。
useMicroPage是否在 setup 绑定固定 tab.key,而非随 activeTabId 变化重绑?- 缓存是否以 key 分隔,而非仅按路由组件名缓存?
3.3 跨应用 openTab 报 FORBIDDEN
这是原有隔离契约,不是故障。保留来源页使用 page.navigateTo(target),关闭本页并指定目标使用 page.back(target);宿主同时配置 canNavigate 和 canOpen。跨应用授权不能只检查目标 appId,还应约束来源、目标 route、参数格式和 tabId 一致性。
旧宿主降级可先检查 bridge.protocol().features.includes('navigate-to'),并核对 typeof page.navigateTo === 'function';缺失时禁用按钮并给出说明,不绕过门禁。
3.4 明明有菜单却没有选中项
activeMenuId 是应用内标识,必须和 appId 联合定位。检查显式 menuId 是否是叶菜单;隐藏详情需配置 menuRoutes,前缀按路径段边界最长匹配。归属规则不是权限规则,不能靠高亮来授权。
使用 Element Plus 菜单时,组件可能先乐观更新选中项。守卫拒绝后应按宿主真实状态恢复高亮,参考基座 MenuTree,而不是把失败的点击结果写回宿主。
3.5 导航一直等待
检查 beforeLeave 是否 await 了同一宿主的 openTab/operate/navigateTo/closeCurrent。导航队列串行,这会造成自等待。守卫只做确认/保存并返回布尔;后续导航应在当前事务结束后的业务流程触发。
3.6 Token 更新后子应用还用旧值
不要把首次读取 Token 存成应用级永久变量。请求前按需调用宿主认证能力;续期由宿主认证模块负责。本库不自动实现登录、续期或请求拦截器。退出时先禁止新交互、发布未认证公开会话,再清理应用和宿主。
3.7 缓存 max 设大仍然丢草稿
页面内存不是持久存储:浏览器刷新、LRU 淘汰、应用卸载都会销毁实例。beforeLeave 不拦截这些所有场景。关键草稿必须业务持久化,并使用用户/应用/业务记录隔离键。
3.8 关闭重开后旧异步操作关闭了新页
检查业务是否按“此刻活动页”找目标。必须保留原页面 SDK 并调用它的 closeCurrent,或保留 {tabId,key}。同 tabId 的新生命周期不能继承旧异步任务身份。
3.9 navigateTo 已 committed,界面却加载失败
导航状态与运行时挂载是两层。检查子应用入口、资源路径、CORS/CSP、网络错误和运行时超时。使用容器显式重试,不重新提交保存接口,不把它误报为导航事务未提交。
3.10 iframe 能调用业务函数却不能直接执行所有 SDK 方法
iframe RPC 不是 AppBridge 透明代理。exposeHostCapabilities 只转发显式白名单能力;路由守卫函数、Vue 缓存对象、AbortSignal 等不能当普通业务 payload 结构化克隆。需要更多方法时先设计独立窄契约,而非暴露整个宿主。
4. 接入验收清单
页面、菜单与权限
- 同 appId 开多个页签只挂载一个应用实例。
- 详情 1001/1002 参数、草稿、守卫彼此独立。
- 列表与隐藏详情的菜单高亮及祖先展开正确。
- canOpen 拒绝时不提交目标;动态撤权后当前策略不继续使用旧权限快照。
- 跨应用默认拒绝;放行合法订单/库存目标,拒绝非法参数和未登记应用。
导航与生命周期
- 切换、关闭、刷新、批量关闭全部遵守 guard 结果。
- 批量任一页面拒绝时不发生半关闭。
- 页签刷新生成新 key;旧 SDK 和迟到响应不能写新页面。
- closeCurrent 的异步完成只针对来源页,不能关闭后来激活的页。
- LRU 淘汰确实卸载,重新激活时业务能安全重建。
通信、安全与恢复
- UNKNOWN_CAPABILITY/FORBIDDEN/TIMEOUT/CANCELLED 有明确 UI 分支。
- handler 验证运行时参数,使用 context 身份而非 payload 自报 appId。
- handler 支持 signal;取消后不盲目重试非幂等写操作。
- 公开会话、URL、sessionStorage、日志不含 Token 或其他凭据。
- 浏览器恢复在认证 ready 后连接,非法存储与旧 history 安全回落。
- 应用加载失败可显式重试;退出中禁重试,释放 browser → runtime → host。
- 独立预览、生产部署入口与真实浏览器的前进/后退/整页刷新都单独验证。
5. 本仓库验证命令与覆盖边界
sh
pnpm --filter @axewo/fin-micro typecheck
pnpm check:production
pnpm --filter @axewo/fin-ui lint:check测试用例和历史临时验收工程已按生产迭代要求移除。当前命令检查生产范围内五个包的类型、构建、公开入口、声明引用、模板运行能力及发布清单,同时构建文档站和 playground。新增项目仍应按以上清单执行真实后台与浏览器验收;静态及产物检查通过不代表所有部署环境兼容。
6. 当前不支持的能力
- 同一 appId 多个独立运行实例;同应用多页签应由内部页级缓存实现。
- 任意恶意同窗口子应用的强安全隔离。
- 认证服务、Token 自动续期、后端鉴权、业务数据自动持久化。
- 浏览器刷新后恢复跨应用 back 来源链;当前来源关系只存在宿主内存。
- iframe 自动代理整个 AppBridge、守卫函数或 Vue 组件。
- 取消/超时后回滚已完成的业务副作用,以及非幂等写操作自动重试。
需要扩展时先定义契约与异常边界,再扩公共层;不要为解决一个页面问题复制一套宿主状态机。