@hhfenpm/micro-app 1.0.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/README.md +211 -0
- package/dist/index.esm.js +416 -0
- package/dist/index.js +424 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# @hhfenpm/micro-app
|
|
2
|
+
|
|
3
|
+
微前端通信桥接和状态同步工具,支持父子应用之间的通信、状态同步和生命周期管理。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hhfenpm/micro-app
|
|
9
|
+
# 或
|
|
10
|
+
yarn add @hhfenpm/micro-app
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 功能特性
|
|
14
|
+
|
|
15
|
+
- 🔗 **通信桥接**:支持父子应用之间的方法调用和通信
|
|
16
|
+
- 🔄 **状态同步**:自动同步 Vuex store 状态
|
|
17
|
+
- 🎯 **生命周期管理**:支持微前端生命周期(mount、update、unmount)
|
|
18
|
+
- 🛣️ **路由同步**:自动同步子应用路由到父应用
|
|
19
|
+
- ⚙️ **可配置**:支持自定义处理器、状态映射等
|
|
20
|
+
|
|
21
|
+
## 使用方式
|
|
22
|
+
|
|
23
|
+
### 1. 初始化通信桥接
|
|
24
|
+
|
|
25
|
+
#### 父应用
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import { initBridge } from '@hhfenpm/micro-app'
|
|
29
|
+
import Vue from 'vue'
|
|
30
|
+
|
|
31
|
+
const vm = new Vue({ /* ... */ })
|
|
32
|
+
|
|
33
|
+
// 创建处理器(项目特定)
|
|
34
|
+
const handlers = {
|
|
35
|
+
elementUI: {
|
|
36
|
+
$message: (...args) => vm.$message(...args),
|
|
37
|
+
// ... 其他 Element UI 方法
|
|
38
|
+
},
|
|
39
|
+
electron: {
|
|
40
|
+
// ... Electron 方法
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 初始化桥接
|
|
45
|
+
initBridge({
|
|
46
|
+
isParent: true,
|
|
47
|
+
vm,
|
|
48
|
+
handlers,
|
|
49
|
+
iframeSelector: '#microApp', // 可选,默认 '#microApp'
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### 子应用
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
import { initBridge } from '@hhfenpm/micro-app'
|
|
57
|
+
import Vue from 'vue'
|
|
58
|
+
|
|
59
|
+
const vm = new Vue({ /* ... */ })
|
|
60
|
+
|
|
61
|
+
// 初始化桥接
|
|
62
|
+
initBridge({
|
|
63
|
+
isParent: false,
|
|
64
|
+
vm,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// 使用 $base 调用父应用方法
|
|
68
|
+
vm.$base.elementUI.$message('Hello from child app')
|
|
69
|
+
vm.$base.electron.show()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. 使用核心功能
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
import { createMicroAppCore } from '@hhfenpm/micro-app'
|
|
76
|
+
|
|
77
|
+
// 定义模块路由映射
|
|
78
|
+
const ModelMap = () => ({
|
|
79
|
+
'module1': ['/module1', '/m1'],
|
|
80
|
+
'module2': ['/module2'],
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// 创建核心功能实例
|
|
84
|
+
const core = createMicroAppCore({
|
|
85
|
+
modelMap: ModelMap,
|
|
86
|
+
enabledModules: () => window.GLOBAL_CONFIG?.microApp || null,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// 使用
|
|
90
|
+
const module = core.microAppModule('/module1')
|
|
91
|
+
const src = core.microAppSrc('/module1')
|
|
92
|
+
const deployed = await core.microAppDeployed('module1')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. 使用状态同步插件
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import Vuex from 'vuex'
|
|
99
|
+
import { baseSyncPlugin } from '@hhfenpm/micro-app'
|
|
100
|
+
|
|
101
|
+
// 创建 Vuex Store
|
|
102
|
+
const store = new Vuex.Store({
|
|
103
|
+
modules: {
|
|
104
|
+
base: {
|
|
105
|
+
namespaced: true,
|
|
106
|
+
state: {
|
|
107
|
+
patient_id: null,
|
|
108
|
+
},
|
|
109
|
+
mutations: {
|
|
110
|
+
SYNC_STATE(state, payload) {
|
|
111
|
+
Object.assign(state, payload)
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
plugins: [
|
|
117
|
+
baseSyncPlugin({
|
|
118
|
+
isParent: false, // 子应用设为 false
|
|
119
|
+
iframeSelector: '#microApp',
|
|
120
|
+
}),
|
|
121
|
+
],
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// 子应用附加路由同步
|
|
125
|
+
import router from './router'
|
|
126
|
+
store.attachRouterSync(router)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. 生命周期管理
|
|
130
|
+
|
|
131
|
+
#### 子应用
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// 注册生命周期钩子
|
|
135
|
+
window.__MICRO_APP_LIFECYCLE__ = {
|
|
136
|
+
mount(payload) {
|
|
137
|
+
console.log('微前端已挂载', payload)
|
|
138
|
+
},
|
|
139
|
+
update(payload) {
|
|
140
|
+
console.log('微前端已更新', payload)
|
|
141
|
+
},
|
|
142
|
+
unmount(payload) {
|
|
143
|
+
console.log('微前端已卸载', payload)
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### 父应用
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
// 调用生命周期
|
|
152
|
+
store.callMicroAppLifeCycle('mount', { /* payload */ })
|
|
153
|
+
store.callMicroAppLifeCycle('update', { /* payload */ })
|
|
154
|
+
store.callMicroAppLifeCycle('unmount', { /* payload */ })
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## API 文档
|
|
158
|
+
|
|
159
|
+
### Bridge
|
|
160
|
+
|
|
161
|
+
#### `initBridge(options)`
|
|
162
|
+
|
|
163
|
+
初始化通信桥接。
|
|
164
|
+
|
|
165
|
+
**参数:**
|
|
166
|
+
- `options.isParent` (boolean): 是否为父应用
|
|
167
|
+
- `options.vm` (Object): Vue 实例
|
|
168
|
+
- `options.iframeSelector` (string): iframe 选择器,默认 `'#microApp'`
|
|
169
|
+
- `options.handlers` (Object): 处理器对象(父应用需要)
|
|
170
|
+
|
|
171
|
+
### Core
|
|
172
|
+
|
|
173
|
+
#### `createMicroAppCore(options)`
|
|
174
|
+
|
|
175
|
+
创建微前端核心功能实例。
|
|
176
|
+
|
|
177
|
+
**参数:**
|
|
178
|
+
- `options.modelMap` (Function): 模块路由映射函数,返回 `{ [moduleName]: string[] }`
|
|
179
|
+
- `options.enabledModules` (Array|Function): 启用的模块列表或获取模块列表的函数
|
|
180
|
+
|
|
181
|
+
**返回:**
|
|
182
|
+
- `microAppModule(path)`: 根据路径获取模块名
|
|
183
|
+
- `microAppSrc(path)`: 生成完整 URL
|
|
184
|
+
- `microAppDeployed(module)`: 检测模块是否已部署
|
|
185
|
+
|
|
186
|
+
### Store
|
|
187
|
+
|
|
188
|
+
#### `baseSyncPlugin(options)`
|
|
189
|
+
|
|
190
|
+
创建状态同步插件。
|
|
191
|
+
|
|
192
|
+
**参数:**
|
|
193
|
+
- `options.isParent` (boolean): 是否为父应用
|
|
194
|
+
- `options.iframeSelector` (string): iframe 选择器
|
|
195
|
+
|
|
196
|
+
## 浏览器兼容性
|
|
197
|
+
|
|
198
|
+
- 现代浏览器(Chrome, Firefox, Safari, Edge)
|
|
199
|
+
- 需要支持 `postMessage` API
|
|
200
|
+
- 需要支持 `Proxy` API(IE 11 不支持)
|
|
201
|
+
|
|
202
|
+
## 注意事项
|
|
203
|
+
|
|
204
|
+
1. **安全性**:消息通信会检查 `origin`,确保只在同源窗口间通信
|
|
205
|
+
2. **状态同步**:状态同步使用深拷贝,避免引用污染
|
|
206
|
+
3. **生命周期**:子应用需要注册 `window.__MICRO_APP_LIFECYCLE__` 对象
|
|
207
|
+
4. **路由同步**:子应用需要调用 `store.attachRouterSync(router)` 附加路由同步
|
|
208
|
+
|
|
209
|
+
## 许可证
|
|
210
|
+
|
|
211
|
+
MIT
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 微前端通信桥接
|
|
3
|
+
* 支持父子应用之间的方法调用和通信
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const BRIDGE$1 = {
|
|
7
|
+
INVOKE: 'bridge-invoke',
|
|
8
|
+
CALLBACK: 'bridge-callback',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const post = (target, message) =>
|
|
12
|
+
target?.postMessage(message, window.location.origin);
|
|
13
|
+
|
|
14
|
+
const getIframeWindow = selector =>
|
|
15
|
+
document.querySelector(selector)?.contentWindow;
|
|
16
|
+
|
|
17
|
+
const genId = () => `${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
18
|
+
|
|
19
|
+
const safeClone = value => {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(JSON.stringify(value))
|
|
22
|
+
} catch {
|
|
23
|
+
return String(value)
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 初始化通信桥接
|
|
29
|
+
* @param {Object} options - 配置选项
|
|
30
|
+
* @param {boolean} options.isParent - 是否为父应用,默认 false
|
|
31
|
+
* @param {Object} options.vm - Vue 实例(可选)
|
|
32
|
+
* @param {string} options.iframeSelector - iframe 选择器,默认 '#microApp'
|
|
33
|
+
* @param {Object} options.handlers - 处理器对象(父应用需要提供)
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*/
|
|
36
|
+
const initBridge = ({
|
|
37
|
+
isParent = false,
|
|
38
|
+
vm,
|
|
39
|
+
iframeSelector = '#microApp',
|
|
40
|
+
handlers = {},
|
|
41
|
+
} = {}) => {
|
|
42
|
+
// 父应用使用传入的 handlers,子应用为空对象
|
|
43
|
+
const finalHandlers = isParent ? handlers : Object.create(null);
|
|
44
|
+
|
|
45
|
+
const pending = Object.create(null);
|
|
46
|
+
|
|
47
|
+
// 子应用:创建 $base 代理对象,用于调用父应用方法
|
|
48
|
+
if (!isParent && vm) {
|
|
49
|
+
vm.$base = new Proxy(
|
|
50
|
+
{},
|
|
51
|
+
{
|
|
52
|
+
get(_, namespace) {
|
|
53
|
+
return new Proxy(
|
|
54
|
+
{},
|
|
55
|
+
{
|
|
56
|
+
get(_, method) {
|
|
57
|
+
return params =>
|
|
58
|
+
new Promise(resolve => {
|
|
59
|
+
const callbackId = genId();
|
|
60
|
+
pending[callbackId] = resolve;
|
|
61
|
+
|
|
62
|
+
post(window.parent, {
|
|
63
|
+
type: BRIDGE$1.INVOKE,
|
|
64
|
+
action: `${namespace}.${method}`,
|
|
65
|
+
params,
|
|
66
|
+
callbackId,
|
|
67
|
+
});
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
vm.$baseReady = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 处理来自子应用的调用请求(父应用)
|
|
80
|
+
const handleInvoke = e => {
|
|
81
|
+
const { action, params, callbackId } = e.data || {};
|
|
82
|
+
if (!action || !callbackId) return
|
|
83
|
+
|
|
84
|
+
const [namespace, method] = action.split('.');
|
|
85
|
+
const fn = finalHandlers?.[namespace]?.[method];
|
|
86
|
+
if (typeof fn !== 'function') return
|
|
87
|
+
|
|
88
|
+
const iframeWindow = getIframeWindow(iframeSelector);
|
|
89
|
+
if (!iframeWindow) return
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const result = fn(params);
|
|
93
|
+
const done = payload =>
|
|
94
|
+
post(iframeWindow, {
|
|
95
|
+
type: BRIDGE$1.CALLBACK,
|
|
96
|
+
callbackId,
|
|
97
|
+
result: safeClone(payload),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
result?.then ? result.then(done).catch(done) : done(result);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
post(iframeWindow, {
|
|
103
|
+
type: BRIDGE$1.CALLBACK,
|
|
104
|
+
callbackId,
|
|
105
|
+
result: safeClone(err?.message || err),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// 处理来自父应用的响应(子应用)
|
|
111
|
+
const handleCallback = e => {
|
|
112
|
+
const { callbackId, result } = e.data || {};
|
|
113
|
+
if (!callbackId || !pending[callbackId]) return
|
|
114
|
+
|
|
115
|
+
pending[callbackId](result);
|
|
116
|
+
delete pending[callbackId];
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// 监听消息
|
|
120
|
+
window.addEventListener('message', e => {
|
|
121
|
+
if (e.origin !== window.location.origin) return
|
|
122
|
+
|
|
123
|
+
const { type } = e.data || {};
|
|
124
|
+
if (isParent && type === BRIDGE$1.INVOKE) handleInvoke(e);
|
|
125
|
+
if (!isParent && type === BRIDGE$1.CALLBACK) handleCallback(e);
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 微前端核心功能
|
|
131
|
+
* 提供模块路由映射、URL 生成、部署检测等功能
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 创建微前端核心功能
|
|
136
|
+
* @param {Object} options - 配置选项
|
|
137
|
+
* @param {Function} options.modelMap - 模块路由映射函数,返回 { [moduleName]: string[] }
|
|
138
|
+
* @param {Array<string>|Function} options.enabledModules - 启用的模块列表或获取模块列表的函数
|
|
139
|
+
* @returns {Object} 核心功能对象
|
|
140
|
+
*/
|
|
141
|
+
function createMicroAppCore({
|
|
142
|
+
modelMap,
|
|
143
|
+
enabledModules,
|
|
144
|
+
} = {}) {
|
|
145
|
+
if (!modelMap || typeof modelMap !== 'function') {
|
|
146
|
+
throw new Error('modelMap function is required')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const MAP = modelMap();
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 获取启用的模块列表
|
|
153
|
+
* @returns {Array<string>|null}
|
|
154
|
+
*/
|
|
155
|
+
const getEnabledModules = () => {
|
|
156
|
+
if (Array.isArray(enabledModules)) {
|
|
157
|
+
return enabledModules
|
|
158
|
+
}
|
|
159
|
+
if (typeof enabledModules === 'function') {
|
|
160
|
+
return enabledModules()
|
|
161
|
+
}
|
|
162
|
+
// 默认从 window.GLOBAL_CONFIG.microApp 获取
|
|
163
|
+
if (typeof window !== 'undefined' && window.GLOBAL_CONFIG) {
|
|
164
|
+
return window.GLOBAL_CONFIG.microApp || null
|
|
165
|
+
}
|
|
166
|
+
return null
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 根据路径获取对应的微前端模块名
|
|
171
|
+
* @param {string} path - 路由路径
|
|
172
|
+
* @returns {string|null} 模块名,如果不在任何模块中则返回 null
|
|
173
|
+
*/
|
|
174
|
+
const microAppModule = path => {
|
|
175
|
+
const value = path.split('?')[0];
|
|
176
|
+
const key = Object.keys(MAP).find(key => MAP[key].includes(value));
|
|
177
|
+
const DATA = getEnabledModules();
|
|
178
|
+
return DATA?.includes(key) ? key : null
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 生成微前端应用的完整 URL
|
|
183
|
+
* @param {string} path - 路由路径
|
|
184
|
+
* @returns {string} 完整的 URL
|
|
185
|
+
*/
|
|
186
|
+
const microAppSrc = path => {
|
|
187
|
+
const module = microAppModule(path);
|
|
188
|
+
const base = window.location.href.split('#')[0];
|
|
189
|
+
return `${base}${module ? `${module}/` : ''}#${path}`
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 检测微前端模块是否已部署
|
|
194
|
+
* @param {string} module - 模块名
|
|
195
|
+
* @returns {Promise<boolean>} 是否已部署
|
|
196
|
+
*/
|
|
197
|
+
const microAppDeployed = async module => {
|
|
198
|
+
try {
|
|
199
|
+
const result = await fetch(`/${module}`, {
|
|
200
|
+
method: 'GET',
|
|
201
|
+
cache: 'no-store',
|
|
202
|
+
});
|
|
203
|
+
return result.ok
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
microAppModule,
|
|
211
|
+
microAppSrc,
|
|
212
|
+
microAppDeployed,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 创建默认的微前端核心功能实例
|
|
218
|
+
* 使用 window.GLOBAL_CONFIG.microApp 作为配置源
|
|
219
|
+
* @param {Function} modelMap - 模块路由映射函数
|
|
220
|
+
* @returns {Object} 核心功能对象
|
|
221
|
+
*/
|
|
222
|
+
function createDefaultMicroAppCore(modelMap) {
|
|
223
|
+
return createMicroAppCore({
|
|
224
|
+
modelMap,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Vuex 状态同步插件
|
|
230
|
+
* 用于在父子应用之间同步 Vuex store 状态
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
const MESSAGE = {
|
|
234
|
+
SYNC_FROM_CHILD: 'sync-base-from-child',
|
|
235
|
+
SYNC_FROM_PARENT: 'sync-base-from-parent',
|
|
236
|
+
REQUEST_FROM_CHILD: 'request-base-from-child',
|
|
237
|
+
CHILD_ROUTE_CHANGE: 'child-route-change',
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const LIFE_CYCLE = {
|
|
241
|
+
MOUNT: 'mount',
|
|
242
|
+
UPDATE: 'update',
|
|
243
|
+
UNMOUNT: 'unmount',
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const BRIDGE = { INVOKE: 'bridge-invoke', CALLBACK: 'bridge-callback' };
|
|
247
|
+
|
|
248
|
+
const LIFE_CYCLE_TYPES = Object.values(LIFE_CYCLE);
|
|
249
|
+
const BRIDGE_TYPES = Object.values(BRIDGE);
|
|
250
|
+
|
|
251
|
+
const getIframe = selector => document.querySelector(selector);
|
|
252
|
+
|
|
253
|
+
const postToChild = (iframeSelector, message) => {
|
|
254
|
+
const iframe = getIframe(iframeSelector);
|
|
255
|
+
iframe?.contentWindow?.postMessage(message, window.location.origin);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const postToParent = message => {
|
|
259
|
+
window.parent?.postMessage(message, window.location.origin);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const isLifeCycleMessage = type => LIFE_CYCLE_TYPES.includes(type);
|
|
263
|
+
|
|
264
|
+
const isBridgeMessage = type => BRIDGE_TYPES.includes(type);
|
|
265
|
+
|
|
266
|
+
const isInternalMessage = type =>
|
|
267
|
+
[
|
|
268
|
+
MESSAGE.SYNC_FROM_CHILD,
|
|
269
|
+
MESSAGE.REQUEST_FROM_CHILD,
|
|
270
|
+
MESSAGE.CHILD_ROUTE_CHANGE,
|
|
271
|
+
].includes(type);
|
|
272
|
+
|
|
273
|
+
const syncRouteFromChild = fullPath => {
|
|
274
|
+
if (typeof fullPath !== 'string') return
|
|
275
|
+
const { origin, pathname } = window.location;
|
|
276
|
+
const base =
|
|
277
|
+
pathname === '/' || pathname === ''
|
|
278
|
+
? origin
|
|
279
|
+
: (origin + pathname).replace(/\/$/, '');
|
|
280
|
+
const target = `${base}/#${fullPath}`;
|
|
281
|
+
if (window.location.href !== target) {
|
|
282
|
+
window.location.href = target;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 创建状态同步插件
|
|
288
|
+
* @param {Object} options - 配置选项
|
|
289
|
+
* @param {boolean} options.isParent - 是否为父应用,默认 false
|
|
290
|
+
* @param {string} options.iframeSelector - iframe 选择器,默认 '#microApp'
|
|
291
|
+
* @returns {Function} Vuex 插件函数
|
|
292
|
+
*/
|
|
293
|
+
function baseSyncPlugin({
|
|
294
|
+
isParent = false,
|
|
295
|
+
iframeSelector = '#microApp',
|
|
296
|
+
} = {}) {
|
|
297
|
+
return store => {
|
|
298
|
+
let latestBaseState = store.state.base;
|
|
299
|
+
|
|
300
|
+
// 监听 base 状态变化,自动同步
|
|
301
|
+
store.watch(
|
|
302
|
+
state => state.base,
|
|
303
|
+
newBase => {
|
|
304
|
+
latestBaseState = newBase;
|
|
305
|
+
if (isParent) {
|
|
306
|
+
postToChild(iframeSelector, {
|
|
307
|
+
type: MESSAGE.SYNC_FROM_PARENT,
|
|
308
|
+
payload: latestBaseState,
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
postToParent({
|
|
312
|
+
type: MESSAGE.SYNC_FROM_CHILD,
|
|
313
|
+
payload: latestBaseState,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
{ deep: true }
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
// 附加路由同步功能(子应用使用)
|
|
321
|
+
store.attachRouterSync = router => {
|
|
322
|
+
if (!router || isParent) return
|
|
323
|
+
router.afterEach(to => {
|
|
324
|
+
const parentHref = window.parent?.location?.href || '';
|
|
325
|
+
const parentFullPath = parentHref.includes('#')
|
|
326
|
+
? parentHref.split('#')[1]
|
|
327
|
+
: '';
|
|
328
|
+
const childFullPath = to.fullPath.startsWith('/')
|
|
329
|
+
? to.fullPath
|
|
330
|
+
: '/' + to.fullPath;
|
|
331
|
+
if (parentFullPath !== childFullPath) {
|
|
332
|
+
postToParent({
|
|
333
|
+
type: MESSAGE.CHILD_ROUTE_CHANGE,
|
|
334
|
+
fullPath: to.fullPath,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// 调用微前端生命周期(父应用使用)
|
|
341
|
+
store.callMicroAppLifeCycle = (type, payload) => {
|
|
342
|
+
if (!isParent) return
|
|
343
|
+
if (!isLifeCycleMessage(type)) return
|
|
344
|
+
|
|
345
|
+
postToChild(iframeSelector, {
|
|
346
|
+
type,
|
|
347
|
+
payload,
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// 处理消息
|
|
352
|
+
const handleMessage = event => {
|
|
353
|
+
if (event.origin !== window.location.origin) return
|
|
354
|
+
const { type, payload, fullPath } = event.data || {};
|
|
355
|
+
|
|
356
|
+
// 子应用处理生命周期消息
|
|
357
|
+
if (!isParent && isLifeCycleMessage(type)) {
|
|
358
|
+
window.__MICRO_APP_LIFECYCLE__?.[type]?.(payload);
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// 父应用处理来自子应用的状态同步
|
|
363
|
+
if (isParent && type === MESSAGE.SYNC_FROM_CHILD) {
|
|
364
|
+
store.commit('base/SYNC_STATE', payload);
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// 子应用处理来自父应用的状态同步
|
|
369
|
+
if (!isParent && type === MESSAGE.SYNC_FROM_PARENT) {
|
|
370
|
+
store.commit('base/SYNC_STATE', payload);
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 父应用响应子应用的状态请求
|
|
375
|
+
if (isParent && type === MESSAGE.REQUEST_FROM_CHILD) {
|
|
376
|
+
postToChild(iframeSelector, {
|
|
377
|
+
type: MESSAGE.SYNC_FROM_PARENT,
|
|
378
|
+
payload: latestBaseState,
|
|
379
|
+
});
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// 父应用处理子应用的路由变化
|
|
384
|
+
if (isParent && type === MESSAGE.CHILD_ROUTE_CHANGE) {
|
|
385
|
+
syncRouteFromChild(fullPath);
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 父应用转发其他消息到父窗口
|
|
390
|
+
if (isParent && !isInternalMessage(type) && !isBridgeMessage(type)) {
|
|
391
|
+
window.parent?.postMessage(event.data || {}, '*');
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
window.addEventListener('message', handleMessage);
|
|
396
|
+
|
|
397
|
+
// 子应用初始化时请求父应用状态
|
|
398
|
+
if (!isParent && window.parent) {
|
|
399
|
+
postToParent({ type: MESSAGE.REQUEST_FROM_CHILD });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* @hhfenpm/micro-app
|
|
406
|
+
* 微前端通信桥接和状态同步工具
|
|
407
|
+
*/
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
var index = {
|
|
411
|
+
initBridge,
|
|
412
|
+
createDefaultMicroAppCore,
|
|
413
|
+
baseSyncPlugin,
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
export { baseSyncPlugin, createDefaultMicroAppCore, createMicroAppCore, index as default, initBridge };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 微前端通信桥接
|
|
7
|
+
* 支持父子应用之间的方法调用和通信
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const BRIDGE$1 = {
|
|
11
|
+
INVOKE: 'bridge-invoke',
|
|
12
|
+
CALLBACK: 'bridge-callback',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const post = (target, message) =>
|
|
16
|
+
target?.postMessage(message, window.location.origin);
|
|
17
|
+
|
|
18
|
+
const getIframeWindow = selector =>
|
|
19
|
+
document.querySelector(selector)?.contentWindow;
|
|
20
|
+
|
|
21
|
+
const genId = () => `${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
22
|
+
|
|
23
|
+
const safeClone = value => {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(JSON.stringify(value))
|
|
26
|
+
} catch {
|
|
27
|
+
return String(value)
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 初始化通信桥接
|
|
33
|
+
* @param {Object} options - 配置选项
|
|
34
|
+
* @param {boolean} options.isParent - 是否为父应用,默认 false
|
|
35
|
+
* @param {Object} options.vm - Vue 实例(可选)
|
|
36
|
+
* @param {string} options.iframeSelector - iframe 选择器,默认 '#microApp'
|
|
37
|
+
* @param {Object} options.handlers - 处理器对象(父应用需要提供)
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
const initBridge = ({
|
|
41
|
+
isParent = false,
|
|
42
|
+
vm,
|
|
43
|
+
iframeSelector = '#microApp',
|
|
44
|
+
handlers = {},
|
|
45
|
+
} = {}) => {
|
|
46
|
+
// 父应用使用传入的 handlers,子应用为空对象
|
|
47
|
+
const finalHandlers = isParent ? handlers : Object.create(null);
|
|
48
|
+
|
|
49
|
+
const pending = Object.create(null);
|
|
50
|
+
|
|
51
|
+
// 子应用:创建 $base 代理对象,用于调用父应用方法
|
|
52
|
+
if (!isParent && vm) {
|
|
53
|
+
vm.$base = new Proxy(
|
|
54
|
+
{},
|
|
55
|
+
{
|
|
56
|
+
get(_, namespace) {
|
|
57
|
+
return new Proxy(
|
|
58
|
+
{},
|
|
59
|
+
{
|
|
60
|
+
get(_, method) {
|
|
61
|
+
return params =>
|
|
62
|
+
new Promise(resolve => {
|
|
63
|
+
const callbackId = genId();
|
|
64
|
+
pending[callbackId] = resolve;
|
|
65
|
+
|
|
66
|
+
post(window.parent, {
|
|
67
|
+
type: BRIDGE$1.INVOKE,
|
|
68
|
+
action: `${namespace}.${method}`,
|
|
69
|
+
params,
|
|
70
|
+
callbackId,
|
|
71
|
+
});
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
vm.$baseReady = true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 处理来自子应用的调用请求(父应用)
|
|
84
|
+
const handleInvoke = e => {
|
|
85
|
+
const { action, params, callbackId } = e.data || {};
|
|
86
|
+
if (!action || !callbackId) return
|
|
87
|
+
|
|
88
|
+
const [namespace, method] = action.split('.');
|
|
89
|
+
const fn = finalHandlers?.[namespace]?.[method];
|
|
90
|
+
if (typeof fn !== 'function') return
|
|
91
|
+
|
|
92
|
+
const iframeWindow = getIframeWindow(iframeSelector);
|
|
93
|
+
if (!iframeWindow) return
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const result = fn(params);
|
|
97
|
+
const done = payload =>
|
|
98
|
+
post(iframeWindow, {
|
|
99
|
+
type: BRIDGE$1.CALLBACK,
|
|
100
|
+
callbackId,
|
|
101
|
+
result: safeClone(payload),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
result?.then ? result.then(done).catch(done) : done(result);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
post(iframeWindow, {
|
|
107
|
+
type: BRIDGE$1.CALLBACK,
|
|
108
|
+
callbackId,
|
|
109
|
+
result: safeClone(err?.message || err),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// 处理来自父应用的响应(子应用)
|
|
115
|
+
const handleCallback = e => {
|
|
116
|
+
const { callbackId, result } = e.data || {};
|
|
117
|
+
if (!callbackId || !pending[callbackId]) return
|
|
118
|
+
|
|
119
|
+
pending[callbackId](result);
|
|
120
|
+
delete pending[callbackId];
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// 监听消息
|
|
124
|
+
window.addEventListener('message', e => {
|
|
125
|
+
if (e.origin !== window.location.origin) return
|
|
126
|
+
|
|
127
|
+
const { type } = e.data || {};
|
|
128
|
+
if (isParent && type === BRIDGE$1.INVOKE) handleInvoke(e);
|
|
129
|
+
if (!isParent && type === BRIDGE$1.CALLBACK) handleCallback(e);
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 微前端核心功能
|
|
135
|
+
* 提供模块路由映射、URL 生成、部署检测等功能
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 创建微前端核心功能
|
|
140
|
+
* @param {Object} options - 配置选项
|
|
141
|
+
* @param {Function} options.modelMap - 模块路由映射函数,返回 { [moduleName]: string[] }
|
|
142
|
+
* @param {Array<string>|Function} options.enabledModules - 启用的模块列表或获取模块列表的函数
|
|
143
|
+
* @returns {Object} 核心功能对象
|
|
144
|
+
*/
|
|
145
|
+
function createMicroAppCore({
|
|
146
|
+
modelMap,
|
|
147
|
+
enabledModules,
|
|
148
|
+
} = {}) {
|
|
149
|
+
if (!modelMap || typeof modelMap !== 'function') {
|
|
150
|
+
throw new Error('modelMap function is required')
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const MAP = modelMap();
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 获取启用的模块列表
|
|
157
|
+
* @returns {Array<string>|null}
|
|
158
|
+
*/
|
|
159
|
+
const getEnabledModules = () => {
|
|
160
|
+
if (Array.isArray(enabledModules)) {
|
|
161
|
+
return enabledModules
|
|
162
|
+
}
|
|
163
|
+
if (typeof enabledModules === 'function') {
|
|
164
|
+
return enabledModules()
|
|
165
|
+
}
|
|
166
|
+
// 默认从 window.GLOBAL_CONFIG.microApp 获取
|
|
167
|
+
if (typeof window !== 'undefined' && window.GLOBAL_CONFIG) {
|
|
168
|
+
return window.GLOBAL_CONFIG.microApp || null
|
|
169
|
+
}
|
|
170
|
+
return null
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 根据路径获取对应的微前端模块名
|
|
175
|
+
* @param {string} path - 路由路径
|
|
176
|
+
* @returns {string|null} 模块名,如果不在任何模块中则返回 null
|
|
177
|
+
*/
|
|
178
|
+
const microAppModule = path => {
|
|
179
|
+
const value = path.split('?')[0];
|
|
180
|
+
const key = Object.keys(MAP).find(key => MAP[key].includes(value));
|
|
181
|
+
const DATA = getEnabledModules();
|
|
182
|
+
return DATA?.includes(key) ? key : null
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 生成微前端应用的完整 URL
|
|
187
|
+
* @param {string} path - 路由路径
|
|
188
|
+
* @returns {string} 完整的 URL
|
|
189
|
+
*/
|
|
190
|
+
const microAppSrc = path => {
|
|
191
|
+
const module = microAppModule(path);
|
|
192
|
+
const base = window.location.href.split('#')[0];
|
|
193
|
+
return `${base}${module ? `${module}/` : ''}#${path}`
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 检测微前端模块是否已部署
|
|
198
|
+
* @param {string} module - 模块名
|
|
199
|
+
* @returns {Promise<boolean>} 是否已部署
|
|
200
|
+
*/
|
|
201
|
+
const microAppDeployed = async module => {
|
|
202
|
+
try {
|
|
203
|
+
const result = await fetch(`/${module}`, {
|
|
204
|
+
method: 'GET',
|
|
205
|
+
cache: 'no-store',
|
|
206
|
+
});
|
|
207
|
+
return result.ok
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return false
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
microAppModule,
|
|
215
|
+
microAppSrc,
|
|
216
|
+
microAppDeployed,
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 创建默认的微前端核心功能实例
|
|
222
|
+
* 使用 window.GLOBAL_CONFIG.microApp 作为配置源
|
|
223
|
+
* @param {Function} modelMap - 模块路由映射函数
|
|
224
|
+
* @returns {Object} 核心功能对象
|
|
225
|
+
*/
|
|
226
|
+
function createDefaultMicroAppCore(modelMap) {
|
|
227
|
+
return createMicroAppCore({
|
|
228
|
+
modelMap,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Vuex 状态同步插件
|
|
234
|
+
* 用于在父子应用之间同步 Vuex store 状态
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
const MESSAGE = {
|
|
238
|
+
SYNC_FROM_CHILD: 'sync-base-from-child',
|
|
239
|
+
SYNC_FROM_PARENT: 'sync-base-from-parent',
|
|
240
|
+
REQUEST_FROM_CHILD: 'request-base-from-child',
|
|
241
|
+
CHILD_ROUTE_CHANGE: 'child-route-change',
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const LIFE_CYCLE = {
|
|
245
|
+
MOUNT: 'mount',
|
|
246
|
+
UPDATE: 'update',
|
|
247
|
+
UNMOUNT: 'unmount',
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const BRIDGE = { INVOKE: 'bridge-invoke', CALLBACK: 'bridge-callback' };
|
|
251
|
+
|
|
252
|
+
const LIFE_CYCLE_TYPES = Object.values(LIFE_CYCLE);
|
|
253
|
+
const BRIDGE_TYPES = Object.values(BRIDGE);
|
|
254
|
+
|
|
255
|
+
const getIframe = selector => document.querySelector(selector);
|
|
256
|
+
|
|
257
|
+
const postToChild = (iframeSelector, message) => {
|
|
258
|
+
const iframe = getIframe(iframeSelector);
|
|
259
|
+
iframe?.contentWindow?.postMessage(message, window.location.origin);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const postToParent = message => {
|
|
263
|
+
window.parent?.postMessage(message, window.location.origin);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const isLifeCycleMessage = type => LIFE_CYCLE_TYPES.includes(type);
|
|
267
|
+
|
|
268
|
+
const isBridgeMessage = type => BRIDGE_TYPES.includes(type);
|
|
269
|
+
|
|
270
|
+
const isInternalMessage = type =>
|
|
271
|
+
[
|
|
272
|
+
MESSAGE.SYNC_FROM_CHILD,
|
|
273
|
+
MESSAGE.REQUEST_FROM_CHILD,
|
|
274
|
+
MESSAGE.CHILD_ROUTE_CHANGE,
|
|
275
|
+
].includes(type);
|
|
276
|
+
|
|
277
|
+
const syncRouteFromChild = fullPath => {
|
|
278
|
+
if (typeof fullPath !== 'string') return
|
|
279
|
+
const { origin, pathname } = window.location;
|
|
280
|
+
const base =
|
|
281
|
+
pathname === '/' || pathname === ''
|
|
282
|
+
? origin
|
|
283
|
+
: (origin + pathname).replace(/\/$/, '');
|
|
284
|
+
const target = `${base}/#${fullPath}`;
|
|
285
|
+
if (window.location.href !== target) {
|
|
286
|
+
window.location.href = target;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 创建状态同步插件
|
|
292
|
+
* @param {Object} options - 配置选项
|
|
293
|
+
* @param {boolean} options.isParent - 是否为父应用,默认 false
|
|
294
|
+
* @param {string} options.iframeSelector - iframe 选择器,默认 '#microApp'
|
|
295
|
+
* @returns {Function} Vuex 插件函数
|
|
296
|
+
*/
|
|
297
|
+
function baseSyncPlugin({
|
|
298
|
+
isParent = false,
|
|
299
|
+
iframeSelector = '#microApp',
|
|
300
|
+
} = {}) {
|
|
301
|
+
return store => {
|
|
302
|
+
let latestBaseState = store.state.base;
|
|
303
|
+
|
|
304
|
+
// 监听 base 状态变化,自动同步
|
|
305
|
+
store.watch(
|
|
306
|
+
state => state.base,
|
|
307
|
+
newBase => {
|
|
308
|
+
latestBaseState = newBase;
|
|
309
|
+
if (isParent) {
|
|
310
|
+
postToChild(iframeSelector, {
|
|
311
|
+
type: MESSAGE.SYNC_FROM_PARENT,
|
|
312
|
+
payload: latestBaseState,
|
|
313
|
+
});
|
|
314
|
+
} else {
|
|
315
|
+
postToParent({
|
|
316
|
+
type: MESSAGE.SYNC_FROM_CHILD,
|
|
317
|
+
payload: latestBaseState,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
{ deep: true }
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// 附加路由同步功能(子应用使用)
|
|
325
|
+
store.attachRouterSync = router => {
|
|
326
|
+
if (!router || isParent) return
|
|
327
|
+
router.afterEach(to => {
|
|
328
|
+
const parentHref = window.parent?.location?.href || '';
|
|
329
|
+
const parentFullPath = parentHref.includes('#')
|
|
330
|
+
? parentHref.split('#')[1]
|
|
331
|
+
: '';
|
|
332
|
+
const childFullPath = to.fullPath.startsWith('/')
|
|
333
|
+
? to.fullPath
|
|
334
|
+
: '/' + to.fullPath;
|
|
335
|
+
if (parentFullPath !== childFullPath) {
|
|
336
|
+
postToParent({
|
|
337
|
+
type: MESSAGE.CHILD_ROUTE_CHANGE,
|
|
338
|
+
fullPath: to.fullPath,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// 调用微前端生命周期(父应用使用)
|
|
345
|
+
store.callMicroAppLifeCycle = (type, payload) => {
|
|
346
|
+
if (!isParent) return
|
|
347
|
+
if (!isLifeCycleMessage(type)) return
|
|
348
|
+
|
|
349
|
+
postToChild(iframeSelector, {
|
|
350
|
+
type,
|
|
351
|
+
payload,
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// 处理消息
|
|
356
|
+
const handleMessage = event => {
|
|
357
|
+
if (event.origin !== window.location.origin) return
|
|
358
|
+
const { type, payload, fullPath } = event.data || {};
|
|
359
|
+
|
|
360
|
+
// 子应用处理生命周期消息
|
|
361
|
+
if (!isParent && isLifeCycleMessage(type)) {
|
|
362
|
+
window.__MICRO_APP_LIFECYCLE__?.[type]?.(payload);
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 父应用处理来自子应用的状态同步
|
|
367
|
+
if (isParent && type === MESSAGE.SYNC_FROM_CHILD) {
|
|
368
|
+
store.commit('base/SYNC_STATE', payload);
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 子应用处理来自父应用的状态同步
|
|
373
|
+
if (!isParent && type === MESSAGE.SYNC_FROM_PARENT) {
|
|
374
|
+
store.commit('base/SYNC_STATE', payload);
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 父应用响应子应用的状态请求
|
|
379
|
+
if (isParent && type === MESSAGE.REQUEST_FROM_CHILD) {
|
|
380
|
+
postToChild(iframeSelector, {
|
|
381
|
+
type: MESSAGE.SYNC_FROM_PARENT,
|
|
382
|
+
payload: latestBaseState,
|
|
383
|
+
});
|
|
384
|
+
return
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// 父应用处理子应用的路由变化
|
|
388
|
+
if (isParent && type === MESSAGE.CHILD_ROUTE_CHANGE) {
|
|
389
|
+
syncRouteFromChild(fullPath);
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 父应用转发其他消息到父窗口
|
|
394
|
+
if (isParent && !isInternalMessage(type) && !isBridgeMessage(type)) {
|
|
395
|
+
window.parent?.postMessage(event.data || {}, '*');
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
window.addEventListener('message', handleMessage);
|
|
400
|
+
|
|
401
|
+
// 子应用初始化时请求父应用状态
|
|
402
|
+
if (!isParent && window.parent) {
|
|
403
|
+
postToParent({ type: MESSAGE.REQUEST_FROM_CHILD });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @hhfenpm/micro-app
|
|
410
|
+
* 微前端通信桥接和状态同步工具
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
var index = {
|
|
415
|
+
initBridge,
|
|
416
|
+
createDefaultMicroAppCore,
|
|
417
|
+
baseSyncPlugin,
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
exports.baseSyncPlugin = baseSyncPlugin;
|
|
421
|
+
exports.createDefaultMicroAppCore = createDefaultMicroAppCore;
|
|
422
|
+
exports.createMicroAppCore = createMicroAppCore;
|
|
423
|
+
exports.default = index;
|
|
424
|
+
exports.initBridge = initBridge;
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hhfenpm/micro-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "微前端通信桥接和状态同步工具,支持父子应用通信、状态同步、生命周期管理",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "rollup -c",
|
|
13
|
+
"dev": "rollup -c -w",
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"micro-frontend",
|
|
19
|
+
"micro-app",
|
|
20
|
+
"bridge",
|
|
21
|
+
"iframe",
|
|
22
|
+
"communication",
|
|
23
|
+
"vuex",
|
|
24
|
+
"state-sync"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"vue": "^2.6.0 || ^3.0.0",
|
|
30
|
+
"vuex": "^3.0.0 || ^4.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@rollup/plugin-commonjs": "^25.0.0",
|
|
34
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
35
|
+
"rollup": "^3.29.5"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": ""
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": ""
|
|
43
|
+
},
|
|
44
|
+
"homepage": ""
|
|
45
|
+
}
|