@fulgurjs/federation 5.7.1 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -4
- package/DESIGN.md +3 -0
- package/README.en.md +350 -367
- package/README.md +293 -1179
- package/dist/cli.js +327 -29
- package/dist/index.cjs +72 -11
- package/dist/index.js +72 -11
- package/dist/runtime.js +1 -1
- package/docs/API.en.md +339 -0
- package/docs/API.md +914 -0
- package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +64 -37
- package/examples/README.md +3 -67
- package/examples/templates/README.md +88 -0
- package/examples/templates/react-host-vue-remote/README.md +18 -0
- package/examples/templates/react-host-vue-remote/package.json +11 -0
- package/examples/templates/react-host-vue-remote/pnpm-lock.yaml +1627 -0
- package/examples/templates/react-host-vue-remote/pnpm-workspace.yaml +10 -0
- package/examples/{bridge → templates/react-host-vue-remote}/react-host/README.md +8 -7
- package/examples/{bridge → templates/react-host-vue-remote}/react-host/package.json +1 -1
- package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d/_paths.d.ts +4 -0
- package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d/bridge.ts +3 -0
- package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d.ts +9 -0
- package/examples/templates/react-host-vue-remote/scripts/dev.config.json +6 -0
- package/examples/templates/react-host-vue-remote/scripts/dev.mjs +173 -0
- package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/README.md +10 -5
- package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/package.json +1 -1
- package/examples/{react → templates/react-react}/README.md +9 -13
- package/examples/{react → templates/react-react}/host/README.md +6 -8
- package/examples/{react → templates/react-react}/host/package.json +1 -1
- package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/ClickButton.ts +3 -0
- package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/_paths.d.ts +4 -0
- package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/pages/DetailPage.ts +3 -0
- package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/pages/HomePage.ts +3 -0
- package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/utils.ts +2 -0
- package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d.ts +26 -0
- package/examples/templates/react-react/package.json +11 -0
- package/examples/templates/react-react/pnpm-lock.yaml +1537 -0
- package/examples/templates/react-react/pnpm-workspace.yaml +10 -0
- package/examples/{react → templates/react-react}/remote/README.md +7 -6
- package/examples/{react → templates/react-react}/remote/package.json +1 -1
- package/examples/templates/react-react/scripts/dev.config.json +6 -0
- package/examples/templates/react-react/scripts/dev.mjs +173 -0
- package/examples/templates/showcase/README.md +109 -0
- package/examples/templates/showcase/package.json +13 -0
- package/examples/templates/showcase/pnpm-lock.yaml +1858 -0
- package/examples/templates/showcase/pnpm-workspace.yaml +12 -0
- package/examples/templates/showcase/react-host/fulgurjs.config.ts +21 -0
- package/examples/templates/showcase/react-host/index.html +13 -0
- package/examples/templates/showcase/react-host/package.json +26 -0
- package/examples/templates/showcase/react-host/src/GuardBanner.tsx +33 -0
- package/examples/templates/showcase/react-host/src/Layout.tsx +42 -0
- package/examples/templates/showcase/react-host/src/ObsPanel.tsx +44 -0
- package/examples/templates/showcase/react-host/src/demo-log.ts +74 -0
- package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d/_paths.d.ts +4 -0
- package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d/bridge.ts +3 -0
- package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d.ts +7 -0
- package/examples/templates/showcase/react-host/src/main.tsx +33 -0
- package/examples/templates/showcase/react-host/src/pages/AboutPage.tsx +18 -0
- package/examples/templates/showcase/react-host/src/pages/BridgeVuePage.tsx +77 -0
- package/examples/templates/showcase/react-host/src/pages/HomePage.tsx +14 -0
- package/examples/templates/showcase/react-host/src/routing.ts +30 -0
- package/examples/templates/showcase/react-host/tsconfig.json +15 -0
- package/examples/templates/showcase/react-host/vite.config.ts +8 -0
- package/examples/templates/showcase/react-remote/fulgurjs.config.ts +18 -0
- package/examples/templates/showcase/react-remote/index.html +13 -0
- package/examples/templates/showcase/react-remote/package.json +25 -0
- package/examples/templates/showcase/react-remote/src/ChildLayout.tsx +48 -0
- package/examples/templates/showcase/react-remote/src/bridge.tsx +62 -0
- package/examples/templates/showcase/react-remote/src/child-bus.ts +32 -0
- package/examples/templates/showcase/react-remote/src/main.tsx +18 -0
- package/examples/templates/showcase/react-remote/src/pages/Locked.tsx +23 -0
- package/examples/templates/showcase/react-remote/src/pages/OrderDetail.tsx +36 -0
- package/examples/templates/showcase/react-remote/src/pages/OrderList.tsx +110 -0
- package/examples/templates/showcase/react-remote/src/pages/Settings.tsx +50 -0
- package/examples/templates/showcase/react-remote/src/settings-store.ts +11 -0
- package/examples/templates/showcase/react-remote/tsconfig.json +14 -0
- package/examples/templates/showcase/react-remote/vite.config.ts +14 -0
- package/examples/templates/showcase/scripts/dev.config.json +8 -0
- package/examples/templates/showcase/scripts/dev.mjs +173 -0
- package/examples/templates/showcase/vue-host/fulgurjs.config.ts +21 -0
- package/examples/templates/showcase/vue-host/index.html +13 -0
- package/examples/templates/showcase/vue-host/package.json +25 -0
- package/examples/templates/showcase/vue-host/src/App.vue +50 -0
- package/examples/templates/showcase/vue-host/src/GuardBanner.vue +24 -0
- package/examples/templates/showcase/vue-host/src/ObsPanel.vue +60 -0
- package/examples/templates/showcase/vue-host/src/demo-log.ts +49 -0
- package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d/_paths.d.ts +4 -0
- package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d/bridge.ts +3 -0
- package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d.ts +7 -0
- package/examples/templates/showcase/vue-host/src/guard.ts +47 -0
- package/examples/templates/showcase/vue-host/src/main.ts +30 -0
- package/examples/templates/showcase/vue-host/src/pages/AboutPage.vue +17 -0
- package/examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue +74 -0
- package/examples/templates/showcase/vue-host/src/pages/HomePage.vue +13 -0
- package/examples/templates/showcase/vue-host/src/routing.ts +31 -0
- package/examples/templates/showcase/vue-host/tsconfig.json +14 -0
- package/examples/templates/showcase/vue-remote/fulgurjs.config.ts +17 -0
- package/examples/templates/showcase/vue-remote/index.html +13 -0
- package/examples/templates/showcase/vue-remote/package.json +23 -0
- package/examples/templates/showcase/vue-remote/src/ChildLayout.vue +54 -0
- package/examples/templates/showcase/vue-remote/src/bridge.ts +48 -0
- package/examples/templates/showcase/vue-remote/src/child-bus.ts +32 -0
- package/examples/templates/showcase/vue-remote/src/main.ts +16 -0
- package/examples/templates/showcase/vue-remote/src/pages/Locked.vue +28 -0
- package/examples/templates/showcase/vue-remote/src/pages/OrderDetail.vue +46 -0
- package/examples/templates/showcase/vue-remote/src/pages/OrderList.vue +116 -0
- package/examples/templates/showcase/vue-remote/src/pages/Settings.vue +34 -0
- package/examples/templates/showcase/vue-remote/src/settings-store.ts +13 -0
- package/examples/templates/showcase/vue-remote/tsconfig.json +14 -0
- package/examples/templates/showcase/vue-remote/vite.config.ts +14 -0
- package/examples/templates/vue-host-react-remote/README.md +18 -0
- package/examples/templates/vue-host-react-remote/package.json +11 -0
- package/examples/templates/vue-host-react-remote/pnpm-lock.yaml +1663 -0
- package/examples/templates/vue-host-react-remote/pnpm-workspace.yaml +10 -0
- package/examples/{bridge → templates/vue-host-react-remote}/react-remote/README.md +9 -4
- package/examples/{bridge → templates/vue-host-react-remote}/react-remote/package.json +1 -1
- package/examples/templates/vue-host-react-remote/scripts/dev.config.json +6 -0
- package/examples/templates/vue-host-react-remote/scripts/dev.mjs +173 -0
- package/examples/{bridge → templates/vue-host-react-remote}/vue-host/README.md +8 -7
- package/examples/{bridge → templates/vue-host-react-remote}/vue-host/package.json +1 -1
- package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/App.vue +1 -1
- package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d/_paths.d.ts +4 -0
- package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d/bridge.ts +3 -0
- package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d.ts +9 -0
- package/examples/{vue → templates/vue-vue}/README.md +9 -13
- package/examples/{vue → templates/vue-vue}/host/README.md +6 -8
- package/examples/{vue → templates/vue-vue}/host/package.json +1 -1
- package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/ClickButton.ts +4 -0
- package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/_paths.d.ts +4 -0
- package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/pages/DetailPage.ts +4 -0
- package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/pages/HomePage.ts +4 -0
- package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/utils.ts +2 -0
- package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d.ts +29 -0
- package/examples/templates/vue-vue/host/vite.config.ts +8 -0
- package/examples/templates/vue-vue/package.json +11 -0
- package/examples/templates/vue-vue/pnpm-lock.yaml +1199 -0
- package/examples/templates/vue-vue/pnpm-workspace.yaml +10 -0
- package/examples/{vue → templates/vue-vue}/remote/README.md +7 -6
- package/examples/{vue → templates/vue-vue}/remote/package.json +1 -1
- package/examples/{vue → templates/vue-vue}/remote/src/App.vue +1 -1
- package/examples/templates/vue-vue/scripts/dev.config.json +6 -0
- package/examples/templates/vue-vue/scripts/dev.mjs +173 -0
- package/package.json +8 -6
- package/examples/README.en.md +0 -67
- package/examples/bridge/README.md +0 -36
- package/examples/bridge/react-host/package-lock.json +0 -2466
- package/examples/bridge/react-remote/package-lock.json +0 -2391
- package/examples/bridge/vue-host/package-lock.json +0 -1894
- package/examples/bridge/vue-remote/package-lock.json +0 -1865
- package/examples/react/host/package-lock.json +0 -2391
- package/examples/react/remote/package-lock.json +0 -2333
- package/examples/vue/host/package-lock.json +0 -1865
- package/examples/vue/remote/package-lock.json +0 -1843
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/fulgurjs.config.ts +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/index.html +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/env.d.ts +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/host-session.ts +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/main.tsx +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/tsconfig.json +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/react-host/vite.config.ts +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/fulgurjs.config.ts +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/index.html +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/App.vue +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/bridge.ts +0 -0
- /package/examples/{bridge/react-remote → templates/react-host-vue-remote/vue-remote}/src/env.d.ts +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/main.ts +0 -0
- /package/examples/{bridge/vue-host → templates/react-host-vue-remote/vue-remote}/tsconfig.json +0 -0
- /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/vite.config.ts +0 -0
- /package/examples/{react → templates/react-react}/host/fulgurjs.config.ts +0 -0
- /package/examples/{react → templates/react-react}/host/index.html +0 -0
- /package/examples/{react → templates/react-react}/host/src/App.tsx +0 -0
- /package/examples/{react → templates/react-react}/host/src/main.tsx +0 -0
- /package/examples/{react → templates/react-react}/host/src/pages/UtilsDemo.tsx +0 -0
- /package/examples/{react → templates/react-react}/host/src/remotePages.tsx +0 -0
- /package/examples/{bridge/react-remote → templates/react-react/host}/tsconfig.json +0 -0
- /package/examples/{react → templates/react-react}/host/vite.config.ts +0 -0
- /package/examples/{react → templates/react-react}/remote/fulgurjs.config.ts +0 -0
- /package/examples/{react → templates/react-react}/remote/index.html +0 -0
- /package/examples/{react → templates/react-react}/remote/src/App.tsx +0 -0
- /package/examples/{react → templates/react-react}/remote/src/exposes/ClickButton.tsx +0 -0
- /package/examples/{react → templates/react-react}/remote/src/exposes/pages/DetailPage.tsx +0 -0
- /package/examples/{react → templates/react-react}/remote/src/exposes/pages/HomePage.tsx +0 -0
- /package/examples/{react → templates/react-react}/remote/src/exposes/utils.ts +0 -0
- /package/examples/{react → templates/react-react}/remote/src/main.tsx +0 -0
- /package/examples/{react/host → templates/react-react/remote}/tsconfig.json +0 -0
- /package/examples/{react → templates/react-react}/remote/vite.config.ts +0 -0
- /package/examples/{vue/host → templates/showcase/vue-host}/vite.config.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/fulgurjs.config.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/index.html +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/src/bridge.tsx +0 -0
- /package/examples/{bridge/vue-host → templates/vue-host-react-remote/react-remote}/src/env.d.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/src/main.tsx +0 -0
- /package/examples/{react/remote → templates/vue-host-react-remote/react-remote}/tsconfig.json +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/vite.config.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/fulgurjs.config.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/index.html +0 -0
- /package/examples/{bridge/vue-remote → templates/vue-host-react-remote/vue-host}/src/env.d.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/host-session.ts +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/main.ts +0 -0
- /package/examples/{bridge/vue-remote → templates/vue-host-react-remote/vue-host}/tsconfig.json +0 -0
- /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/vite.config.ts +0 -0
- /package/examples/{vue → templates/vue-vue}/host/fulgurjs.config.ts +0 -0
- /package/examples/{vue → templates/vue-vue}/host/index.html +0 -0
- /package/examples/{vue → templates/vue-vue}/host/src/App.vue +0 -0
- /package/examples/{vue → templates/vue-vue}/host/src/main.ts +0 -0
- /package/examples/{vue → templates/vue-vue}/host/src/pages/HomePage.vue +0 -0
- /package/examples/{vue → templates/vue-vue}/host/src/pages/UtilsDemo.vue +0 -0
- /package/examples/{vue → templates/vue-vue}/host/tsconfig.json +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/fulgurjs.config.ts +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/index.html +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/src/exposes/ClickButton.vue +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/src/exposes/pages/DetailPage.vue +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/src/exposes/pages/HomePage.vue +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/src/exposes/utils.ts +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/src/main.ts +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/tsconfig.json +0 -0
- /package/examples/{vue → templates/vue-vue}/remote/vite.config.ts +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-host/src/App.vue — 宿主根布局:顶部导航 + 守卫横幅 + RouterView。
|
|
4
|
+
* 「工单列表 / 设置」是直接跳子应用深链的菜单项:宿主 router.push 到 basePath 下的路径。
|
|
5
|
+
*/
|
|
6
|
+
import { RouterView, useRouter } from 'vue-router'
|
|
7
|
+
import GuardBanner from './GuardBanner.vue'
|
|
8
|
+
import { logNav } from './demo-log'
|
|
9
|
+
|
|
10
|
+
interface MenuItem {
|
|
11
|
+
label: string
|
|
12
|
+
path: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const router = useRouter()
|
|
16
|
+
|
|
17
|
+
const menus: MenuItem[] = [
|
|
18
|
+
{ label: '首页', path: '/' },
|
|
19
|
+
{ label: '关于', path: '/about' },
|
|
20
|
+
{ label: '桥接演示页', path: '/br-react' },
|
|
21
|
+
{ label: '工单列表', path: '/br-react/orders' },
|
|
22
|
+
{ label: '设置', path: '/br-react/settings' },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const handleMenu = (item: MenuItem): void => {
|
|
26
|
+
logNav('宿主菜单', `push ${item.path}`)
|
|
27
|
+
void router.push(item.path)
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div class="demo-shell">
|
|
33
|
+
<GuardBanner />
|
|
34
|
+
<nav class="demo-nav" aria-label="宿主导航">
|
|
35
|
+
<button v-for="item in menus" :key="item.path" @click="handleMenu(item)">{{ item.label }}</button>
|
|
36
|
+
</nav>
|
|
37
|
+
<RouterView />
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
/* 演示全局基础样式(非 scoped):宿主与子应用同页渲染,统一观感 */
|
|
43
|
+
body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; margin: 0; }
|
|
44
|
+
.demo-shell { padding: 12px; max-width: 960px; margin: 0 auto; }
|
|
45
|
+
.demo-nav { display: flex; gap: 8px; padding: 8px 0; border-bottom: 1px solid #ddd; margin-bottom: 12px; flex-wrap: wrap; }
|
|
46
|
+
.demo-nav button { cursor: pointer; }
|
|
47
|
+
.demo-row { display: flex; gap: 8px; align-items: center; margin: 8px 0; flex-wrap: wrap; }
|
|
48
|
+
.demo-muted { color: #666; font-size: 13px; }
|
|
49
|
+
code { background: #f4f4f5; padding: 1px 4px; border-radius: 3px; }
|
|
50
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-host/src/GuardBanner.vue — 宿主守卫确认横幅(异步 beforeEach 等待用户选择)。
|
|
4
|
+
* 「取消」= Vue Router 导航失败 → 通道收到 cancelled,URL/子应用位置保持不变。
|
|
5
|
+
*/
|
|
6
|
+
import { guardPending, resolveGuard } from './guard'
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div v-if="guardPending" class="demo-guard" role="alertdialog" aria-label="宿主守卫确认">
|
|
11
|
+
<span>宿主守卫:是否允许前往 <code>{{ guardPending.to }}</code>?</span>
|
|
12
|
+
<button @click="resolveGuard(true)">继续</button>
|
|
13
|
+
<button @click="resolveGuard(false)">取消</button>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<style scoped>
|
|
18
|
+
.demo-guard {
|
|
19
|
+
display: flex; gap: 12px; align-items: center;
|
|
20
|
+
background: #fff7e6; border: 1px solid #f0c36d;
|
|
21
|
+
padding: 8px 12px; margin-bottom: 12px; border-radius: 4px;
|
|
22
|
+
}
|
|
23
|
+
.demo-guard button { cursor: pointer; }
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-host/src/ObsPanel.vue — URL 同步观测台(实时更新)。
|
|
4
|
+
* 展示:宿主当前 URL(location.href)、子应用逻辑位置、history.length、
|
|
5
|
+
* 子应用挂载计数、最近 10 条导航事件(带来源标注)。
|
|
6
|
+
*/
|
|
7
|
+
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
8
|
+
import { useRoute } from 'vue-router'
|
|
9
|
+
import { navLog, mountCount } from './demo-log'
|
|
10
|
+
import { BRIDGE_BASE_PATH } from './routing'
|
|
11
|
+
|
|
12
|
+
const route = useRoute()
|
|
13
|
+
|
|
14
|
+
const hostHref = ref(window.location.href)
|
|
15
|
+
const historyLength = ref(window.history.length)
|
|
16
|
+
|
|
17
|
+
const childLocation = computed(() => {
|
|
18
|
+
const inPrefix = route.path === BRIDGE_BASE_PATH || route.path.startsWith(`${BRIDGE_BASE_PATH}/`)
|
|
19
|
+
if (!inPrefix) return route.fullPath
|
|
20
|
+
return route.fullPath.slice(BRIDGE_BASE_PATH.length) || '/'
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const refresh = (): void => {
|
|
24
|
+
hostHref.value = window.location.href
|
|
25
|
+
historyLength.value = window.history.length
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
watch(() => route.fullPath, refresh)
|
|
29
|
+
|
|
30
|
+
const handlePop = (): void => { refresh() }
|
|
31
|
+
onMounted(() => window.addEventListener('popstate', handlePop))
|
|
32
|
+
onUnmounted(() => window.removeEventListener('popstate', handlePop))
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<section class="demo-obs">
|
|
37
|
+
<h3 class="demo-obs-title">URL 同步观测台</h3>
|
|
38
|
+
<p>宿主当前 URL:<code data-demo-host-url>{{ hostHref }}</code></p>
|
|
39
|
+
<p>子应用逻辑位置:<code data-demo-child-location>{{ childLocation }}</code></p>
|
|
40
|
+
<p>
|
|
41
|
+
history.length:<code data-demo-history-length>{{ historyLength }}</code>
|
|
42
|
+
|子应用 mount 次数:<code data-demo-mount-count>{{ mountCount }}</code>(路由切换应恒为 1)
|
|
43
|
+
</p>
|
|
44
|
+
<h4>最近导航事件(≤10 条,新在下)</h4>
|
|
45
|
+
<ol class="demo-obs-log">
|
|
46
|
+
<li v-for="entry in navLog" :key="entry.id">
|
|
47
|
+
[{{ entry.time }}] {{ entry.source }} — {{ entry.detail }}
|
|
48
|
+
</li>
|
|
49
|
+
<li v-if="navLog.length === 0" class="demo-muted">(暂无事件)</li>
|
|
50
|
+
</ol>
|
|
51
|
+
</section>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<style scoped>
|
|
55
|
+
.demo-obs { border: 1px solid #d0e7ff; background: #f5faff; padding: 8px 12px; margin: 12px 0; border-radius: 4px; }
|
|
56
|
+
.demo-obs-title { margin: 6px 0; }
|
|
57
|
+
.demo-obs p { margin: 4px 0; }
|
|
58
|
+
h4 { margin: 6px 0 2px; }
|
|
59
|
+
.demo-obs-log { margin: 4px 0; padding-left: 20px; font-size: 13px; }
|
|
60
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-host/src/demo-log.ts — URL 同步观测台数据源(模块级单例,跨宿主页面存活)。
|
|
3
|
+
* 事件来源标注:宿主菜单 / 深链粘贴 / 子应用 Link / 子应用 push·replace / 子应用 go /
|
|
4
|
+
* 子应用初始化 / 浏览器前进后退 / 宿主守卫。最多保留 10 条。
|
|
5
|
+
*/
|
|
6
|
+
import { ref } from 'vue'
|
|
7
|
+
|
|
8
|
+
export interface NavLogEntry {
|
|
9
|
+
id: number
|
|
10
|
+
time: string
|
|
11
|
+
source: string
|
|
12
|
+
detail: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type DemoChildEvent =
|
|
16
|
+
| { type: 'mounted'; mountCount: number }
|
|
17
|
+
| { type: 'nav'; source: string; action: string; detail: string }
|
|
18
|
+
|
|
19
|
+
export const navLog = ref<NavLogEntry[]>([])
|
|
20
|
+
export const mountCount = ref(0)
|
|
21
|
+
|
|
22
|
+
let nextId = 1
|
|
23
|
+
|
|
24
|
+
export function logNav(source: string, detail: string): void {
|
|
25
|
+
const entry: NavLogEntry = { id: nextId, time: new Date().toLocaleTimeString('zh-CN', { hour12: false }), source, detail }
|
|
26
|
+
nextId += 1
|
|
27
|
+
navLog.value = [...navLog.value, entry].slice(-10)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** appProps 稳定回调:子应用经它上报挂载计数与导航事件 */
|
|
31
|
+
export function handleChildEvent(event: DemoChildEvent): void {
|
|
32
|
+
if (event.type === 'mounted') {
|
|
33
|
+
mountCount.value = event.mountCount
|
|
34
|
+
logNav('子应用', `mount #${event.mountCount}`)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
logNav(event.source, `${event.action} ${event.detail}`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let popInstalled = false
|
|
41
|
+
|
|
42
|
+
/** 浏览器前进后退(POP)在 main.ts 安装一次;push/replace 由各自来源显式标注 */
|
|
43
|
+
export function installPopStateLogger(): void {
|
|
44
|
+
if (popInstalled) return
|
|
45
|
+
popInstalled = true
|
|
46
|
+
window.addEventListener('popstate', () => {
|
|
47
|
+
logNav('浏览器前进后退', window.location.pathname + window.location.search)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-host/src/guard.ts — 宿主真实权限守卫(Vue Router 4)。
|
|
3
|
+
* /br-react/locked 需人工确认:异步 beforeEach 返回 Promise → Vue Router 等待用户选择;
|
|
4
|
+
* 返回 false = 真实取消(NavigationFailure)→ 桥接通道收到 cancelled,URL/历史/子应用位置
|
|
5
|
+
* 全部保持最后确认状态,插件绝不自动重试。
|
|
6
|
+
*/
|
|
7
|
+
import { ref } from 'vue'
|
|
8
|
+
import type { Router } from 'vue-router'
|
|
9
|
+
import { logNav } from './demo-log'
|
|
10
|
+
import { BRIDGE_BASE_PATH } from './routing'
|
|
11
|
+
|
|
12
|
+
export interface GuardPending {
|
|
13
|
+
to: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const guardPending = ref<GuardPending | null>(null)
|
|
17
|
+
let resolver: ((allow: boolean) => void) | undefined
|
|
18
|
+
|
|
19
|
+
/** 守卫横幅按钮回调:放行(true)或取消(false) */
|
|
20
|
+
export function resolveGuard(allow: boolean): void {
|
|
21
|
+
const current = resolver
|
|
22
|
+
resolver = undefined
|
|
23
|
+
current?.(allow)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 作废当前待确认导航(新导航开始时旧导航已被 vue-router 取消,确认横幅必须随之撤下) */
|
|
27
|
+
function invalidatePending(): void {
|
|
28
|
+
const current = resolver
|
|
29
|
+
if (!current) return
|
|
30
|
+
resolver = undefined
|
|
31
|
+
guardPending.value = null
|
|
32
|
+
current(false)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function installGuard(router: Router): void {
|
|
36
|
+
router.beforeEach(async (to) => {
|
|
37
|
+
// 任何新导航开始都作废旧待确认(含非 locked 目标,如守卫等待期间的 go(1))
|
|
38
|
+
invalidatePending()
|
|
39
|
+
if (!to.path.startsWith(`${BRIDGE_BASE_PATH}/locked`)) return true
|
|
40
|
+
guardPending.value = { to: to.fullPath }
|
|
41
|
+
logNav('宿主守卫', `拦截 → ${to.fullPath}(等待确认)`)
|
|
42
|
+
const allow = await new Promise<boolean>((resolve) => { resolver = resolve })
|
|
43
|
+
guardPending.value = null
|
|
44
|
+
logNav('宿主守卫', allow ? `继续 → ${to.fullPath}` : `取消 → ${to.fullPath}(URL/历史/子应用位置保持不变)`)
|
|
45
|
+
return allow
|
|
46
|
+
})
|
|
47
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-host/src/main.ts — 宿主入口。
|
|
3
|
+
* 宿主 Router 是浏览器历史唯一写入方;catch-all 路由(/br-react/* 单条记录渲染同一组件)
|
|
4
|
+
* 保证子应用内部导航不重挂宿主组件。守卫与 POP 日志在此一次性安装。
|
|
5
|
+
*/
|
|
6
|
+
import { createApp } from 'vue'
|
|
7
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
8
|
+
import App from './App.vue'
|
|
9
|
+
import HomePage from './pages/HomePage.vue'
|
|
10
|
+
import AboutPage from './pages/AboutPage.vue'
|
|
11
|
+
import BridgeReactPage from './pages/BridgeReactPage.vue'
|
|
12
|
+
import { initBridgeRouting } from './routing'
|
|
13
|
+
import { installGuard } from './guard'
|
|
14
|
+
import { installPopStateLogger } from './demo-log'
|
|
15
|
+
|
|
16
|
+
const router = createRouter({
|
|
17
|
+
history: createWebHistory(),
|
|
18
|
+
routes: [
|
|
19
|
+
{ path: '/', component: HomePage },
|
|
20
|
+
{ path: '/about', component: AboutPage },
|
|
21
|
+
// catch-all:子应用挂载在 /br-react 前缀下,内部导航全部命中同一条路由记录
|
|
22
|
+
{ path: '/br-react/:pathMatch(.*)*', component: BridgeReactPage },
|
|
23
|
+
],
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
initBridgeRouting(router)
|
|
27
|
+
installGuard(router)
|
|
28
|
+
installPopStateLogger()
|
|
29
|
+
|
|
30
|
+
createApp(App).use(router).mount('#app')
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-host/src/pages/AboutPage.vue — 关于页:场景与工程说明。
|
|
4
|
+
*/
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<section>
|
|
9
|
+
<h2>关于</h2>
|
|
10
|
+
<p>examples/templates/showcase:一个 pnpm 模板中的 4 个应用 —— react-remote(5333)、vue-host(5334)、vue-remote(5335)、react-host(5336)。</p>
|
|
11
|
+
<ul>
|
|
12
|
+
<li>vue-host(:5334)套 react-remote(:5333),桥接前缀 basePath=/br-react</li>
|
|
13
|
+
<li>react-host(:5336)套 vue-remote(:5335),桥接前缀 basePath=/br-vue</li>
|
|
14
|
+
</ul>
|
|
15
|
+
<p>API 与源码对照、演示操作清单见 examples/templates/showcase/README.md。</p>
|
|
16
|
+
</section>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue — 桥接演示页(Vue 宿主 × React 子应用)。
|
|
4
|
+
* createVueBridgeApp + routing prop(basePath=/br-react + createVueBridgeNavigation 端口);
|
|
5
|
+
* catch-all 路由保证子应用内部导航不重挂本组件;appProps 携带稳定回调供子应用上报事件。
|
|
6
|
+
*/
|
|
7
|
+
import { ref } from 'vue'
|
|
8
|
+
import { useRouter } from 'vue-router'
|
|
9
|
+
import { createVueBridgeApp } from '@fulgurjs/federation/bridge/vue'
|
|
10
|
+
import { BRIDGE_BASE_PATH, getBridgeRouting } from '../routing'
|
|
11
|
+
import { handleChildEvent, logNav } from '../demo-log'
|
|
12
|
+
import ObsPanel from '../ObsPanel.vue'
|
|
13
|
+
|
|
14
|
+
const RemoteReactBridge = createVueBridgeApp('react-remote/bridge', { retries: 1 })
|
|
15
|
+
const routing = getBridgeRouting()
|
|
16
|
+
const appProps = { onChildEvent: handleChildEvent }
|
|
17
|
+
|
|
18
|
+
const router = useRouter()
|
|
19
|
+
const deepLink = ref(`${window.location.origin}${BRIDGE_BASE_PATH}/orders?q=网络&page=2`)
|
|
20
|
+
const deepLinkError = ref('')
|
|
21
|
+
|
|
22
|
+
const handleDeepLink = (): void => {
|
|
23
|
+
const raw = deepLink.value.trim()
|
|
24
|
+
let path = raw
|
|
25
|
+
if (/^https?:\/\//i.test(raw)) {
|
|
26
|
+
try {
|
|
27
|
+
const parsed = new URL(raw)
|
|
28
|
+
path = parsed.pathname + parsed.search
|
|
29
|
+
} catch {
|
|
30
|
+
deepLinkError.value = 'URL 无法解析'
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (path !== BRIDGE_BASE_PATH && !path.startsWith(`${BRIDGE_BASE_PATH}/`)) {
|
|
35
|
+
deepLinkError.value = `路径必须位于 ${BRIDGE_BASE_PATH} 前缀下`
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
deepLinkError.value = ''
|
|
39
|
+
logNav('深链粘贴', `push ${path}`)
|
|
40
|
+
void router.push(path)
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<section>
|
|
46
|
+
<h2>桥接演示:Vue 宿主 × React 子应用(URL 同步)</h2>
|
|
47
|
+
<p class="demo-muted">
|
|
48
|
+
子应用挂载在宿主 <code>{{ BRIDGE_BASE_PATH }}</code> 前缀下;观测台实时展示宿主 URL、
|
|
49
|
+
子应用逻辑位置、history.length 与导航事件来源。
|
|
50
|
+
</p>
|
|
51
|
+
<ObsPanel />
|
|
52
|
+
<section class="demo-deeplink">
|
|
53
|
+
<h3>深链粘贴框(push 侧「刷新直达」复现)</h3>
|
|
54
|
+
<div class="demo-row">
|
|
55
|
+
<input v-model="deepLink" class="demo-deeplink-input" aria-label="深链粘贴框" />
|
|
56
|
+
<button @click="handleDeepLink">push 复现直达</button>
|
|
57
|
+
</div>
|
|
58
|
+
<p v-if="deepLinkError !== ''" class="demo-deeplink-error">{{ deepLinkError }}</p>
|
|
59
|
+
<p class="demo-muted">粘贴带中文 query 的完整 URL 后点击按钮 = 复制地址栏在新窗口打开前的 push 侧动作。</p>
|
|
60
|
+
</section>
|
|
61
|
+
<section class="demo-bridge-area" data-demo-bridge-area>
|
|
62
|
+
<RemoteReactBridge :routing="routing" :app-props="appProps" />
|
|
63
|
+
</section>
|
|
64
|
+
</section>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<style scoped>
|
|
68
|
+
.demo-deeplink { border: 1px dashed #bbb; padding: 8px 12px; margin: 12px 0; border-radius: 4px; }
|
|
69
|
+
.demo-deeplink h3 { margin: 6px 0; }
|
|
70
|
+
.demo-deeplink-input { flex: 1 1 320px; }
|
|
71
|
+
.demo-deeplink button { cursor: pointer; }
|
|
72
|
+
.demo-deeplink-error { color: #c0392b; margin: 4px 0; }
|
|
73
|
+
.demo-bridge-area { border: 2px solid #61dafb; padding: 8px 12px; margin: 12px 0; border-radius: 4px; }
|
|
74
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-host/src/pages/HomePage.vue — 宿主首页。
|
|
4
|
+
*/
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<section>
|
|
9
|
+
<h2>首页</h2>
|
|
10
|
+
<p>双向桥接 URL 同步演示:宿主页级路由(首页 / 关于 / 桥接演示页)+ 子应用受控 memory 路由。</p>
|
|
11
|
+
<p>顶部菜单「桥接演示页 / 工单列表 / 设置」经宿主 router.push 直达子应用深链。</p>
|
|
12
|
+
</section>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-host/src/routing.ts — 桥接宿主导航端口(模块级单例)。
|
|
3
|
+
* main.ts 组装宿主 router 后初始化一次:createVueBridgeNavigation(router)——
|
|
4
|
+
* Vue Router fullPath 已剥离 history base,逻辑路径无需再处理部署前缀;
|
|
5
|
+
* 端口引用保持稳定 → routing 键不变,宿主不重挂、不重复订阅。
|
|
6
|
+
*/
|
|
7
|
+
import type { Router } from 'vue-router'
|
|
8
|
+
import { createVueBridgeNavigation } from '@fulgurjs/federation/bridge/router/vue'
|
|
9
|
+
|
|
10
|
+
export const BRIDGE_BASE_PATH = '/br-react'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 宿主 routing prop 结构(BridgeHostRouting)。
|
|
14
|
+
* 注:5.4.1 发布包未从 /bridge/router/vue 导出 BridgeHostRouting 类型(README §8.3 示例
|
|
15
|
+
* 与发布包类型面不一致,已知问题),这里用端口函数返回值结构化等价替代。
|
|
16
|
+
*/
|
|
17
|
+
interface BridgeHostRoutingLike {
|
|
18
|
+
basePath: string
|
|
19
|
+
navigation: ReturnType<typeof createVueBridgeNavigation>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let routing: BridgeHostRoutingLike | undefined
|
|
23
|
+
|
|
24
|
+
export function initBridgeRouting(router: Router): void {
|
|
25
|
+
routing = { basePath: BRIDGE_BASE_PATH, navigation: createVueBridgeNavigation(router) }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getBridgeRouting(): BridgeHostRoutingLike {
|
|
29
|
+
if (!routing) throw new Error('桥接路由端口未初始化:请先在 main.ts 调用 initBridgeRouting(router)')
|
|
30
|
+
return routing
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"jsx": "preserve",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"types": ["vite/client"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src", "fulgurjs.config.ts", "vite.config.ts"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FederationOptions } from '@fulgurjs/federation'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vue 子应用联邦配置(examples/templates/showcase/vue-remote,端口 5335)。
|
|
5
|
+
* 只暴露 ./bridge 桥接契约;宿主(react-host)启用 URL 同步后经它加载。
|
|
6
|
+
* vue-router 是子应用自己的受控路由库(不进 shared),由消费方自装。
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
name: 'vue-remote',
|
|
10
|
+
filename: 'fulgurjs-remoteEntry.js',
|
|
11
|
+
exposes: {
|
|
12
|
+
'./bridge': './src/bridge.ts',
|
|
13
|
+
},
|
|
14
|
+
shared: {
|
|
15
|
+
vue: { singleton: true },
|
|
16
|
+
},
|
|
17
|
+
} satisfies FederationOptions
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="icon" href="data:," />
|
|
7
|
+
<title>vue-remote(桥接 URL 同步子应用,5335)</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fulgurjs-demo-bridge-vue-remote",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --port 5335 --strictPort",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview --port 5335 --strictPort",
|
|
10
|
+
"typecheck": "vue-tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@fulgurjs/federation": "5.8.0",
|
|
14
|
+
"vue": "^3.5.13",
|
|
15
|
+
"vue-router": "^4.5.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
19
|
+
"typescript": "^5.8.0",
|
|
20
|
+
"vite": "^6.3.5",
|
|
21
|
+
"vue-tsc": "^2.2.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* examples/templates/showcase/vue-remote/src/ChildLayout.vue — 子应用根布局:导航演示按钮 + RouterView。
|
|
4
|
+
* push/replace 走被 connectVueBridgeRouter 接管的 router.push/replace(经通道同步宿主 URL);
|
|
5
|
+
* go 委托宿主浏览器历史,不创建第二条独立历史。
|
|
6
|
+
*/
|
|
7
|
+
import { RouterView, useRouter } from 'vue-router'
|
|
8
|
+
import { getMountCount, reportNav } from './child-bus'
|
|
9
|
+
|
|
10
|
+
const router = useRouter()
|
|
11
|
+
|
|
12
|
+
const handlePushDemo = (): void => {
|
|
13
|
+
const target = router.resolve('/orders?q=演示&page=2').fullPath
|
|
14
|
+
reportNav('子应用 push/replace', 'push', target)
|
|
15
|
+
void router.push(target)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const handleReplaceDemo = (): void => {
|
|
19
|
+
reportNav('子应用 push/replace', 'replace', '/settings')
|
|
20
|
+
void router.replace('/settings')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handleGoBack = (): void => {
|
|
24
|
+
reportNav('子应用 go', 'go', '-1')
|
|
25
|
+
router.go(-1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const handleGoForward = (): void => {
|
|
29
|
+
reportNav('子应用 go', 'go', '1')
|
|
30
|
+
router.go(1)
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<section class="demo-child">
|
|
36
|
+
<p class="demo-child-title">
|
|
37
|
+
<strong>Vue 子应用</strong>(受控 memory 路由)· 本会话挂载次数:{{ getMountCount() }}
|
|
38
|
+
</p>
|
|
39
|
+
<div class="demo-row">
|
|
40
|
+
<button @click="handlePushDemo">子应用 push /orders?q=演示&page=2</button>
|
|
41
|
+
<button @click="handleReplaceDemo">子应用 replace /settings</button>
|
|
42
|
+
<button @click="handleGoBack">子应用 go(-1)</button>
|
|
43
|
+
<button @click="handleGoForward">子应用 go(1)</button>
|
|
44
|
+
</div>
|
|
45
|
+
<RouterView />
|
|
46
|
+
</section>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<style scoped>
|
|
50
|
+
.demo-child { border-top: 1px dashed #ccc; margin-top: 8px; padding-top: 8px; }
|
|
51
|
+
.demo-child-title { margin: 4px 0; }
|
|
52
|
+
.demo-row { display: flex; gap: 8px; align-items: center; margin: 8px 0; flex-wrap: wrap; }
|
|
53
|
+
.demo-row button { cursor: pointer; }
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-remote/src/bridge.ts — Vue 子应用桥接契约(exposes './bridge')。
|
|
3
|
+
* defineBridgeApp(factory, { routing: true }) 声明路由协议(缺失时宿主启用同步即 MFU-031 占位)。
|
|
4
|
+
* 受控路由 = createMemoryHistory + connectVueBridgeRouter;工厂为异步形态——
|
|
5
|
+
* 必须 await conn.ready 后再 app.use(router)(初始 push 落定后再 install)。
|
|
6
|
+
*/
|
|
7
|
+
import { createApp } from 'vue'
|
|
8
|
+
import { createMemoryHistory, createRouter, type RouteRecordRaw } from 'vue-router'
|
|
9
|
+
import { defineBridgeApp } from '@fulgurjs/federation/runtime'
|
|
10
|
+
import { connectVueBridgeRouter } from '@fulgurjs/federation/bridge/router/vue'
|
|
11
|
+
import ChildLayout from './ChildLayout.vue'
|
|
12
|
+
import OrderList from './pages/OrderList.vue'
|
|
13
|
+
import OrderDetail from './pages/OrderDetail.vue'
|
|
14
|
+
import Settings from './pages/Settings.vue'
|
|
15
|
+
import Locked from './pages/Locked.vue'
|
|
16
|
+
import { bindReporter, reportNav } from './child-bus'
|
|
17
|
+
|
|
18
|
+
const routes: RouteRecordRaw[] = [
|
|
19
|
+
// 根路径以 replace 规范化到 /orders(不产生额外历史条目)
|
|
20
|
+
{ path: '/', redirect: '/orders' },
|
|
21
|
+
{ path: '/orders', component: OrderList },
|
|
22
|
+
{ path: '/orders/:id', component: OrderDetail },
|
|
23
|
+
{ path: '/settings', component: Settings },
|
|
24
|
+
{ path: '/locked', component: Locked },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
export default defineBridgeApp(async (props, ctx) => {
|
|
28
|
+
if (!ctx?.routing) {
|
|
29
|
+
throw new Error('本契约需要宿主启用 URL 同步(mount 第三参数 routing)')
|
|
30
|
+
}
|
|
31
|
+
bindReporter(props)
|
|
32
|
+
const init = ctx.routing.getLocation()
|
|
33
|
+
const initPath = init.pathname + init.search + init.hash
|
|
34
|
+
const router = createRouter({ history: createMemoryHistory(), routes })
|
|
35
|
+
const conn = connectVueBridgeRouter(ctx.routing, router, { signal: ctx.signal })
|
|
36
|
+
await conn.ready
|
|
37
|
+
const currentPath = router.currentRoute.value.fullPath
|
|
38
|
+
reportNav(
|
|
39
|
+
'子应用初始化',
|
|
40
|
+
'init',
|
|
41
|
+
currentPath !== initPath
|
|
42
|
+
? `根路径规范化 replace → ${currentPath}(无新增历史条目)`
|
|
43
|
+
: `初始位置 → ${currentPath}(与宿主深链一致)`,
|
|
44
|
+
)
|
|
45
|
+
const app = createApp(ChildLayout, props)
|
|
46
|
+
app.use(router)
|
|
47
|
+
return app
|
|
48
|
+
}, { routing: true })
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-remote/src/child-bus.ts — 子应用 → 宿主观测台上报总线。
|
|
3
|
+
* 宿主经 appProps 传入稳定回调 onChildEvent;本模块持有该引用并维护挂载计数。
|
|
4
|
+
* 单页单实例假设:同一宿主页同时只挂一个本子应用实例(路由同步前缀互斥的常规形态)。
|
|
5
|
+
*/
|
|
6
|
+
export type DemoChildNavAction = 'push' | 'replace' | 'go' | 'init'
|
|
7
|
+
|
|
8
|
+
export type DemoChildEvent =
|
|
9
|
+
| { type: 'mounted'; mountCount: number }
|
|
10
|
+
| { type: 'nav'; source: string; action: DemoChildNavAction; detail: string }
|
|
11
|
+
|
|
12
|
+
export type DemoChildReporter = (event: DemoChildEvent) => void
|
|
13
|
+
|
|
14
|
+
const bus: { report: DemoChildReporter | undefined } = { report: undefined }
|
|
15
|
+
let mountCount = 0
|
|
16
|
+
|
|
17
|
+
/** 桥接工厂内调用:从 props 快照中取出宿主回调,并上报本次挂载计数 */
|
|
18
|
+
export function bindReporter(props: Record<string, unknown> | undefined): void {
|
|
19
|
+
const candidate = (props as { onChildEvent?: DemoChildReporter } | undefined)?.onChildEvent
|
|
20
|
+
bus.report = typeof candidate === 'function' ? candidate : undefined
|
|
21
|
+
mountCount += 1
|
|
22
|
+
bus.report?.({ type: 'mounted', mountCount })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 导航来源上报(子应用 Link / 子应用 push·replace / 子应用 go / 子应用初始化) */
|
|
26
|
+
export function reportNav(source: string, action: DemoChildNavAction, detail: string): void {
|
|
27
|
+
bus.report?.({ type: 'nav', source, action, detail })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getMountCount(): number {
|
|
31
|
+
return mountCount
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* examples/templates/showcase/vue-remote/src/main.ts — 独立运行入口(仅 dev 占位)。
|
|
3
|
+
* 子应用真实能力经 exposes './bridge' 被宿主加载;直开本页仅作 dev server 健康检查。
|
|
4
|
+
*/
|
|
5
|
+
import { createApp, h } from 'vue'
|
|
6
|
+
|
|
7
|
+
createApp({
|
|
8
|
+
render: () =>
|
|
9
|
+
h('div', { style: "font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; padding: 24px" }, [
|
|
10
|
+
h('h1', null, 'vue-remote(联邦子应用,5335)'),
|
|
11
|
+
h('p', null, '本应用通过 expose ./bridge(defineBridgeApp + routing 协议)被宿主加载。'),
|
|
12
|
+
h('p', null, [
|
|
13
|
+
h('a', { href: 'http://localhost:5336/br-vue/orders' }, '打开 React 宿主桥接页(5336)'),
|
|
14
|
+
]),
|
|
15
|
+
]),
|
|
16
|
+
}).mount('#app')
|