@xingtukeji/micro 1.0.2 → 1.0.3
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/dist/index.js +1913 -0
- package/dist/style.css +1 -0
- package/dist/types/api.d.ts +21 -0
- package/dist/types/child.d.ts +16 -0
- package/dist/types/components/XTMicroView.vue.d.ts +6 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/parent.d.ts +28 -0
- package/dist/types/types.d.ts +1 -0
- package/dist/types/utils.d.ts +8 -0
- package/package.json +9 -3
- package/postcss.config.js +23 -0
- package/src/api.ts +108 -0
- package/src/child.ts +186 -0
- package/src/components/XTMicroView.vue +72 -0
- package/src/index.d.ts +29 -0
- package/src/index.ts +16 -0
- package/src/parent.ts +268 -0
- package/src/types.ts +8 -0
- package/src/utils.ts +94 -0
- package/tsconfig.json +24 -0
- package/vite.config.ts +28 -0
- package/index.ts +0 -291
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.xt-micro{background-color:#fff}.xt-micro:before{align-items:center;content:"加载中";display:flex;justify-content:center;inset:0;position:absolute;z-index:-1}.micro-iframe{border:none;height:100%;margin:0;padding:0;width:100%}.micro-iframe:after{align-items:center;background:#fff;content:"加载中";display:flex;justify-content:center;inset:0;position:absolute}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AppOption, App } from './index.d';
|
|
2
|
+
import { microInit } from './child';
|
|
3
|
+
/**
|
|
4
|
+
* 安装微应用
|
|
5
|
+
* @param app
|
|
6
|
+
*/
|
|
7
|
+
export declare const setupApp: (app: AppOption) => App | undefined;
|
|
8
|
+
export declare const getApp: (name: string) => App;
|
|
9
|
+
/**
|
|
10
|
+
* 启动微应用
|
|
11
|
+
* @param name 应用名称
|
|
12
|
+
* @param options 应用配置
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export declare const startApp: (name: string, options?: Partial<AppOption>) => Promise<unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* 停止指定名称的应用程序
|
|
18
|
+
* @param {string} name - 应用程序的名称
|
|
19
|
+
*/
|
|
20
|
+
export declare const stopApp: (name: string) => void;
|
|
21
|
+
export { microInit };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MicroInitOptions } from './index.d';
|
|
2
|
+
/**
|
|
3
|
+
* 微应用-子应用初始化
|
|
4
|
+
* 此时方法上下文是子应用
|
|
5
|
+
* 非必须,但建议使用
|
|
6
|
+
* @param options
|
|
7
|
+
*/
|
|
8
|
+
export declare const microInit: (options?: MicroInitOptions) => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* 同步子应用路由到主应用路由
|
|
11
|
+
*/
|
|
12
|
+
export declare function syncUrlToWindow(): void;
|
|
13
|
+
/**
|
|
14
|
+
* 同步主应用路由到子应用
|
|
15
|
+
*/
|
|
16
|
+
export declare function syncUrlToIframe(iframeWindow: Window): void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{
|
|
2
|
+
appId: StringConstructor;
|
|
3
|
+
}, {}, unknown, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{
|
|
4
|
+
appId: StringConstructor;
|
|
5
|
+
}>>, {}, {}>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MicroApp } from './parent';
|
|
2
|
+
export interface AppOption {
|
|
3
|
+
name: string
|
|
4
|
+
url: string
|
|
5
|
+
el?: HTMLElement | string
|
|
6
|
+
preload?: boolean
|
|
7
|
+
token?: () => string | string
|
|
8
|
+
params?: { [key: string]: any }
|
|
9
|
+
sync?: boolean
|
|
10
|
+
}
|
|
11
|
+
export interface App extends AppOption {
|
|
12
|
+
instance: MicroApp
|
|
13
|
+
}
|
|
14
|
+
export interface MicroInitOptions {
|
|
15
|
+
hideHeaderCssSelector?: string
|
|
16
|
+
authHandler?: (token: string) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 扩展 Window 接口,防止ts报错
|
|
20
|
+
declare global {
|
|
21
|
+
interface Window {
|
|
22
|
+
[key: string]: any
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export * from './api'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AppOption, App } from './index.d';
|
|
2
|
+
export declare const isMicro: import('vue').Ref<boolean>;
|
|
3
|
+
export declare class MicroApp {
|
|
4
|
+
app: App | AppOption;
|
|
5
|
+
dom: HTMLIFrameElement | undefined;
|
|
6
|
+
panel: HTMLElement | undefined;
|
|
7
|
+
constructor(app: AppOption);
|
|
8
|
+
mount(el: HTMLElement | string | undefined, isHide?: boolean): void;
|
|
9
|
+
unmount(): void;
|
|
10
|
+
/**
|
|
11
|
+
* 预加载渲染
|
|
12
|
+
* TODO 还差用户切走之后,如何将ifr还原回去,等待下次载入
|
|
13
|
+
* @param el 挂载dom对象
|
|
14
|
+
*/
|
|
15
|
+
preloadRender(el: HTMLElement | string | undefined): void;
|
|
16
|
+
/**
|
|
17
|
+
* 预加载app,先放到body,待启动时,挪过来
|
|
18
|
+
*/
|
|
19
|
+
preload(): void;
|
|
20
|
+
changeOptions(options: Partial<AppOption>): void;
|
|
21
|
+
onLoad(_event: Event): void;
|
|
22
|
+
onUnload(_event: Event): void;
|
|
23
|
+
onError(): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 初始化主应用微前端
|
|
27
|
+
*/
|
|
28
|
+
export declare const mainMicroInit: () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CONSTANT: Record<string, string>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function getQueryParameter(name: string, url?: string): string | null;
|
|
2
|
+
export declare function getAbsolutePath(url: string | undefined, base: string, hash?: boolean): string | undefined;
|
|
3
|
+
export declare function anchorElementGenerator(url: string): HTMLAnchorElement;
|
|
4
|
+
export declare function getAnchorElementQueryMap(anchorElement: HTMLAnchorElement): {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function debounce<T extends (...args: any[]) => any>(func: T, delay: number): (...args: Parameters<T>) => void;
|
|
8
|
+
export declare function addOrReplaceUrlParam(url: string, paramName: string, paramValue: string): string;
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingtukeji/micro",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "",
|
|
6
|
-
"main": "index.
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/types/index.d.ts",
|
|
7
8
|
"author": "",
|
|
8
9
|
"license": "ISC",
|
|
9
10
|
"publishConfig": {
|
|
10
11
|
"access": "public",
|
|
11
12
|
"registry": "https://registry.npmjs.org/"
|
|
12
13
|
},
|
|
13
|
-
"
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"vite-plugin-dts": "^4.5.0"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "vite build"
|
|
19
|
+
}
|
|
14
20
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const defaultList = [
|
|
2
|
+
require('postcss-import'), // 使css支持@import相互引入
|
|
3
|
+
// require('postcss-simple-vars'), // 使css支持变量($blue: #056ef0;),老项目如果用到了sass里的变量,需要加载该插件
|
|
4
|
+
require('postcss-utilities'), // 支持常用工具(具体参照: https://github.com/ismamz/postcss-utilities#:~:text=postcss-utilities%20is%20a%20PostCSS%20plugin%20that%20includes%20the,started%20using%20postcss-utilities%20Try%20it%20in%20the%20browser%21)
|
|
5
|
+
require('postcss-nested'), // 支持嵌套语法,貌似postcss-preset-env里含语法嵌套,出现嵌套问题再尝试加载此插件
|
|
6
|
+
require('postcss-preset-env')({
|
|
7
|
+
stage: 0
|
|
8
|
+
}), // 对css4语法进行降阶(如自定义选择器),已经包含autoprefixer
|
|
9
|
+
]
|
|
10
|
+
const developmentList = [
|
|
11
|
+
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const productionList = [
|
|
15
|
+
require('cssnano'), // css压缩
|
|
16
|
+
]
|
|
17
|
+
module.exports = {
|
|
18
|
+
parser: 'postcss-scss',
|
|
19
|
+
plugins: [
|
|
20
|
+
...defaultList,
|
|
21
|
+
...(process.env.NODE_ENV === 'development' ? developmentList : productionList)
|
|
22
|
+
]
|
|
23
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {AppOption, App} from './index.d'
|
|
2
|
+
import {MicroApp} from './parent'
|
|
3
|
+
import {microInit} from './child'
|
|
4
|
+
|
|
5
|
+
let _apps: Record<string, App> = {}
|
|
6
|
+
|
|
7
|
+
const defaultAppOption: AppOption = {
|
|
8
|
+
url: '',
|
|
9
|
+
name: '',
|
|
10
|
+
sync: true
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 安装微应用
|
|
14
|
+
* @param app
|
|
15
|
+
*/
|
|
16
|
+
export const setupApp = (app: AppOption): App | undefined => {
|
|
17
|
+
// 如果app的url属性不存在或者为空字符串或者为'undefined',则打印错误信息并返回
|
|
18
|
+
if(!app.url || app.url === '' || app.url === 'undefined'){
|
|
19
|
+
console.error('app url is required', app)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
// 将defaultAppOption对象中的属性赋值给app对象
|
|
23
|
+
const op = Object.assign({}, app, defaultAppOption)
|
|
24
|
+
// 将app对象中的属性和MicroApp类的实例赋值给_apps对象中的app.name属性
|
|
25
|
+
_apps[app.name] = {
|
|
26
|
+
...op,
|
|
27
|
+
instance: new MicroApp(app),
|
|
28
|
+
}
|
|
29
|
+
// 返回_apps对象中的app.name属性
|
|
30
|
+
return _apps[app.name]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 导出一个函数,用于获取指定名称的应用
|
|
34
|
+
export const getApp = (name: string): App => {
|
|
35
|
+
// 返回指定名称的应用
|
|
36
|
+
return _apps[name]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 设置应用配置
|
|
41
|
+
* @param {App} app - 应用对象
|
|
42
|
+
* @param {Partial<AppOption>} options - 应用选项(可选)
|
|
43
|
+
* @returns {App} - 设置后的应用对象
|
|
44
|
+
*/
|
|
45
|
+
const setApp = (app: App, options?: Partial<AppOption>) => {
|
|
46
|
+
// 返回一个对象,该对象将app和options合并
|
|
47
|
+
return (_apps[app.name] = {
|
|
48
|
+
...app,
|
|
49
|
+
...options,
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 启动微应用
|
|
54
|
+
* @param name 应用名称
|
|
55
|
+
* @param options 应用配置
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
export const startApp = (name: string, options?: Partial<AppOption>) => {
|
|
59
|
+
// 返回一个Promise对象
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
// 调用getApp函数,传入name参数,获取app对象
|
|
62
|
+
let app = getApp(name)
|
|
63
|
+
// 如果app对象存在
|
|
64
|
+
if (app) {
|
|
65
|
+
// 如果options参数存在
|
|
66
|
+
if (options) {
|
|
67
|
+
// 调用app对象的changeOptions方法,传入options参数
|
|
68
|
+
app.instance.changeOptions(options)
|
|
69
|
+
// 调用setApp函数,传入app对象和options参数,重新设置app对象
|
|
70
|
+
app = setApp(app, options)
|
|
71
|
+
}
|
|
72
|
+
// 如果app对象的preload属性存在
|
|
73
|
+
if (app.preload) {
|
|
74
|
+
// 调用app对象的preloadRender方法,传入app对象的el属性
|
|
75
|
+
app.instance.preloadRender(app.el)
|
|
76
|
+
} else {
|
|
77
|
+
// 否则,调用app对象的mount方法,传入app对象的el属性
|
|
78
|
+
app.instance.mount(app.el)
|
|
79
|
+
}
|
|
80
|
+
// 调用_postMicro函数,传入app对象
|
|
81
|
+
// _postMicro(app)
|
|
82
|
+
// 调用resolve方法,传入app对象
|
|
83
|
+
resolve(app)
|
|
84
|
+
} else {
|
|
85
|
+
// 否则,调用reject方法,传入'app not found'字符串
|
|
86
|
+
reject('app not found')
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 停止指定名称的应用程序
|
|
93
|
+
* @param {string} name - 应用程序的名称
|
|
94
|
+
*/
|
|
95
|
+
export const stopApp = (name: string) => {
|
|
96
|
+
// 获取名为name的app
|
|
97
|
+
let app = getApp(name)
|
|
98
|
+
// 如果app存在
|
|
99
|
+
if (app) {
|
|
100
|
+
// 调用app的unmount方法
|
|
101
|
+
app.instance.unmount()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
microInit
|
|
108
|
+
}
|
package/src/child.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import {getQueryParameter} from './utils'
|
|
2
|
+
import {CONSTANT} from './types'
|
|
3
|
+
import {MicroApp} from './parent'
|
|
4
|
+
import {MicroInitOptions} from './index.d'
|
|
5
|
+
/**
|
|
6
|
+
* 微应用-子应用初始化
|
|
7
|
+
* 此时方法上下文是子应用
|
|
8
|
+
* 非必须,但建议使用
|
|
9
|
+
* @param options
|
|
10
|
+
*/
|
|
11
|
+
export const microInit = async (options: MicroInitOptions = {}) => {
|
|
12
|
+
//处理url参数
|
|
13
|
+
const micro = getQueryParameter(CONSTANT.IS_MICRO)
|
|
14
|
+
const microName = getQueryParameter(CONSTANT.MICRO_NAME)
|
|
15
|
+
//设置环境变量
|
|
16
|
+
if (micro) {
|
|
17
|
+
window[CONSTANT.IS_MICRO] = true
|
|
18
|
+
}
|
|
19
|
+
// console.log(`子应用「${microName}」初始化`, document)
|
|
20
|
+
// 尝试全局隐藏 header,该方式有一定的入侵,
|
|
21
|
+
function addGlobalStyle(css: string) {
|
|
22
|
+
var styleSheet = document.styleSheets[0];
|
|
23
|
+
if (!styleSheet) {
|
|
24
|
+
var style = document.createElement('style');
|
|
25
|
+
document.head.appendChild(style);
|
|
26
|
+
styleSheet = style.sheet!;
|
|
27
|
+
}
|
|
28
|
+
styleSheet.insertRule(css, styleSheet.cssRules.length);
|
|
29
|
+
}
|
|
30
|
+
// addGlobalStyle('._hedaui-header { display: none !important; }')
|
|
31
|
+
if(options.hideHeaderCssSelector) {
|
|
32
|
+
addGlobalStyle(`${options.hideHeaderCssSelector} { display: none !important; }`)
|
|
33
|
+
}
|
|
34
|
+
// 子应用监听message,设置环境变量
|
|
35
|
+
window.addEventListener('message', (e) => {
|
|
36
|
+
if (e.data.cmd === `$xt/micro/${microName}`) {
|
|
37
|
+
console.log('子应用收到消息:', microName, )
|
|
38
|
+
window[CONSTANT.MICRO_APP] = { ...e.data.data, mainBaseURL: e.origin }
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
patchIframeHistory(window)
|
|
42
|
+
|
|
43
|
+
// window.addEventListener('load', () => {
|
|
44
|
+
// console.log('子应用加载完成:', microName, window.location.href)
|
|
45
|
+
// // 监听 popstate 事件
|
|
46
|
+
// // window.addEventListener('popstate', function (event) {
|
|
47
|
+
// // console.log('History state changed:', event.state);
|
|
48
|
+
// // // 在这里可以根据新的历史状态进行相应的页面更新操作
|
|
49
|
+
// // });
|
|
50
|
+
// // patchIframeHistory(window, "http://localhost:5173", "http://localhost:8888")
|
|
51
|
+
// })
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
//处理token
|
|
56
|
+
const token = getQueryParameter(CONSTANT.MICRO_TOKEN)
|
|
57
|
+
options.authHandler && token && await options.authHandler(token)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//子应用给父应用同步路由
|
|
61
|
+
const _syncMicroPath = (app: MicroApp) => {
|
|
62
|
+
// console.log(`子应用给父应用「${app.app.name}」同步路由`, app.dom)
|
|
63
|
+
const parentWindow = window.parent;
|
|
64
|
+
const message = 'Hello from child!';
|
|
65
|
+
parentWindow.postMessage(message, 'http://localhost:8080');
|
|
66
|
+
app.dom?.contentWindow?.postMessage
|
|
67
|
+
({
|
|
68
|
+
cmd: `$xt/micro/${app.app.name}/sync`,
|
|
69
|
+
data: {
|
|
70
|
+
name: app.app.name,
|
|
71
|
+
url: app.app.url,
|
|
72
|
+
preload: app.app.preload,
|
|
73
|
+
// params: app.params
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 对iframe的history的pushState和replaceState进行修改
|
|
80
|
+
* 将从location劫持后的数据修改回来,防止跨域错误
|
|
81
|
+
* 同步路由到主应用
|
|
82
|
+
* @param iframeWindow
|
|
83
|
+
* @param appHostPath 子应用的 host path
|
|
84
|
+
* @param mainHostPath 主应用的 host path
|
|
85
|
+
*/
|
|
86
|
+
function patchIframeHistory(iframeWindow: Window): void {
|
|
87
|
+
const history = iframeWindow.history;
|
|
88
|
+
const rawHistoryPushState = history.pushState;
|
|
89
|
+
const rawHistoryReplaceState = history.replaceState;
|
|
90
|
+
history.pushState = function (data: any, title: string, url?: string): void {
|
|
91
|
+
// debugger;
|
|
92
|
+
// const baseUrl =
|
|
93
|
+
// mainHostPath + iframeWindow.location.pathname + iframeWindow.location.search + iframeWindow.location.hash;
|
|
94
|
+
// const mainUrl = getAbsolutePath(url?.replace(appHostPath, ""), baseUrl);
|
|
95
|
+
// const ignoreFlag = url === undefined;
|
|
96
|
+
|
|
97
|
+
rawHistoryPushState.call(history, data, title, url);
|
|
98
|
+
// if (ignoreFlag) return;
|
|
99
|
+
// updateBase(iframeWindow, appHostPath, mainHostPath);
|
|
100
|
+
syncUrlToWindow();
|
|
101
|
+
};
|
|
102
|
+
history.replaceState = function (data: any, title: string, url?: string): void {
|
|
103
|
+
// debugger;
|
|
104
|
+
// const baseUrl =
|
|
105
|
+
// mainHostPath + iframeWindow.location.pathname + iframeWindow.location.search + iframeWindow.location.hash;
|
|
106
|
+
// const mainUrl = getAbsolutePath(url?.replace(appHostPath, ""), baseUrl);
|
|
107
|
+
// const ignoreFlag = url === undefined;
|
|
108
|
+
|
|
109
|
+
rawHistoryReplaceState.call(history, data, title, url);
|
|
110
|
+
// if (ignoreFlag) return;
|
|
111
|
+
// updateBase(iframeWindow, appHostPath, mainHostPath);
|
|
112
|
+
syncUrlToWindow();
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 同步子应用路由到主应用路由
|
|
117
|
+
*/
|
|
118
|
+
// 导出一个函数,用于同步url到window
|
|
119
|
+
export function syncUrlToWindow(): void {
|
|
120
|
+
if(!window[CONSTANT.MICRO_APP]) return
|
|
121
|
+
const { name, sync, mainBaseURL } = window[CONSTANT.MICRO_APP];
|
|
122
|
+
// console.trace('syncUrlToWindow')
|
|
123
|
+
//todo 此处应该postmessage到主应用,主应用再进行同步路由
|
|
124
|
+
const curUrl = window.location.pathname + window.location.search + window.location.hash;
|
|
125
|
+
window.parent.postMessage({
|
|
126
|
+
cmd: '$xt/micro/sync',
|
|
127
|
+
data: {
|
|
128
|
+
name: name,
|
|
129
|
+
url: window.encodeURIComponent(curUrl)
|
|
130
|
+
}
|
|
131
|
+
}, mainBaseURL);
|
|
132
|
+
|
|
133
|
+
// debugger
|
|
134
|
+
// let winUrlElement = anchorElementGenerator(window.parent.location.href);
|
|
135
|
+
// const queryMap = getAnchorElementQueryMap(winUrlElement);
|
|
136
|
+
// // 非同步且url上没有当前id的查询参数,否则就要同步参数或者清理参数
|
|
137
|
+
// if (!sync && !queryMap[name]) {
|
|
138
|
+
// (winUrlElement = null)
|
|
139
|
+
// return
|
|
140
|
+
// };
|
|
141
|
+
|
|
142
|
+
// const curUrl = iframeWindow.location.pathname + iframeWindow.location.search + iframeWindow.location.hash;
|
|
143
|
+
// let validShortPath = "";
|
|
144
|
+
// // 同步
|
|
145
|
+
// if (sync) {
|
|
146
|
+
// queryMap[name] = window.encodeURIComponent(
|
|
147
|
+
// curUrl
|
|
148
|
+
// );
|
|
149
|
+
// // 清理
|
|
150
|
+
// } else {
|
|
151
|
+
// delete queryMap[name];
|
|
152
|
+
// }
|
|
153
|
+
// const newQuery =
|
|
154
|
+
// "?" +
|
|
155
|
+
// Object.keys(queryMap)
|
|
156
|
+
// .map((key) => key + "=" + queryMap[key])
|
|
157
|
+
// .join("&");
|
|
158
|
+
// winUrlElement.search = newQuery;
|
|
159
|
+
// console.log('window', iframeWindow.parent.location.href)
|
|
160
|
+
// // if (winUrlElement.href !== iframeWindow.parent.location.href) {
|
|
161
|
+
// // console.log('window', iframeWindow.parent)
|
|
162
|
+
// // // iframeWindow.parent.history.replaceState(null, "", winUrlElement.href);
|
|
163
|
+
// // // window.history.replaceState(null, "", winUrlElement.href);
|
|
164
|
+
// // }
|
|
165
|
+
// winUrlElement = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 同步主应用路由到子应用
|
|
170
|
+
*/
|
|
171
|
+
export function syncUrlToIframe(iframeWindow: Window): void {
|
|
172
|
+
// 获取当前路由路径
|
|
173
|
+
const { pathname, search, hash } = iframeWindow.location;
|
|
174
|
+
const { id, url, sync, execFlag, prefix, inject } = iframeWindow.__WUJIE;
|
|
175
|
+
|
|
176
|
+
// 只在浏览器刷新或者第一次渲染时同步
|
|
177
|
+
const idUrl = sync && !execFlag ? getSyncUrl(id, prefix) : url;
|
|
178
|
+
// 排除href跳转情况
|
|
179
|
+
const syncUrl = (/^http/.test(idUrl) ? null : idUrl) || url;
|
|
180
|
+
const { appRoutePath } = appRouteParse(syncUrl);
|
|
181
|
+
|
|
182
|
+
const preAppRoutePath = pathname + search + hash;
|
|
183
|
+
if (preAppRoutePath !== appRoutePath) {
|
|
184
|
+
iframeWindow.history.replaceState(null, "", inject.mainHostPath + appRoutePath);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { onMounted, ref, watch, onUnmounted } from 'vue';
|
|
3
|
+
import { startApp, stopApp } from '../api'
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
appId: String
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const refPanel = ref()
|
|
9
|
+
const init = () => {
|
|
10
|
+
props.appId && startApp(props.appId, {
|
|
11
|
+
el: refPanel.value
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
watch(() => props.appId, (newVal, oldVal) => {
|
|
16
|
+
if(newVal !== oldVal && oldVal){
|
|
17
|
+
stopApp(oldVal)
|
|
18
|
+
}
|
|
19
|
+
init()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
onMounted(() => {
|
|
24
|
+
init()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
onUnmounted(()=>{
|
|
28
|
+
console.log('微应用销毁', props.appId)
|
|
29
|
+
})
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div ref="refPanel" class="xt-micro w-100% h-100% overflow-hidden"></div>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<style lang="postcss" scoped></style>
|
|
37
|
+
<style>
|
|
38
|
+
.xt-micro{
|
|
39
|
+
background-color: #fff;
|
|
40
|
+
}
|
|
41
|
+
.xt-micro::before{
|
|
42
|
+
content: '加载中';
|
|
43
|
+
position: absolute;
|
|
44
|
+
top: 0;
|
|
45
|
+
left: 0;
|
|
46
|
+
right:0;
|
|
47
|
+
bottom: 0;
|
|
48
|
+
display: flex;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
align-items: center;
|
|
51
|
+
z-index: -1;
|
|
52
|
+
}
|
|
53
|
+
.micro-iframe{
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
border: none;
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
}
|
|
60
|
+
.micro-iframe::after{
|
|
61
|
+
content: '加载中';
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 0;
|
|
64
|
+
left: 0;
|
|
65
|
+
right:0;
|
|
66
|
+
bottom: 0;
|
|
67
|
+
display: flex;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
align-items: center;
|
|
70
|
+
background: #fff;
|
|
71
|
+
}
|
|
72
|
+
</style>
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
import {MicroApp} from './parent'
|
|
3
|
+
|
|
4
|
+
export interface AppOption {
|
|
5
|
+
name: string
|
|
6
|
+
url: string
|
|
7
|
+
el?: HTMLElement | string
|
|
8
|
+
preload?: boolean
|
|
9
|
+
token?: () => string | string
|
|
10
|
+
params?: { [key: string]: any }
|
|
11
|
+
sync?: boolean
|
|
12
|
+
}
|
|
13
|
+
export interface App extends AppOption {
|
|
14
|
+
instance: MicroApp
|
|
15
|
+
}
|
|
16
|
+
export interface MicroInitOptions {
|
|
17
|
+
hideHeaderCssSelector?: string
|
|
18
|
+
authHandler?: (token: string) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 扩展 Window 接口,防止ts报错
|
|
22
|
+
declare global {
|
|
23
|
+
interface Window {
|
|
24
|
+
[key: string]: any
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export * from './api'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import type { App } from 'vue'
|
|
4
|
+
import XTMicroView from './components/XTMicroView.vue'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
import {mainMicroInit} from './parent'
|
|
9
|
+
export * from './api'
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
install: (app: App, _options: any = {}) => {
|
|
13
|
+
app.component("xt-micro-view", XTMicroView);
|
|
14
|
+
mainMicroInit()
|
|
15
|
+
}
|
|
16
|
+
}
|