@xingtukeji/micro 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.
Files changed (2) hide show
  1. package/index.ts +268 -0
  2. package/package.json +14 -0
package/index.ts ADDED
@@ -0,0 +1,268 @@
1
+ import qs from 'qs'
2
+ import { ref } from 'vue'
3
+
4
+ export interface AppOption {
5
+ name: string
6
+ url: string
7
+ el?: HTMLElement | string
8
+ preload?: boolean
9
+ params?: { [key: string]: any }
10
+ }
11
+ export interface App extends AppOption {
12
+ instance: MicroApp
13
+ }
14
+
15
+ const _apps = []
16
+
17
+ /**
18
+ * 判断是否在微应用中
19
+ */
20
+ const _isMicro = ref(false)
21
+ Object.defineProperty(window, '__XT_MICRO', {
22
+ get: function () {
23
+ return this.__XT_MICRO
24
+ },
25
+ set: function (newValue) {
26
+ _isMicro.value = newValue
27
+ },
28
+ })
29
+ export const isMicro = _isMicro
30
+
31
+ //dom id前缀
32
+ export const DOM_ID_PREFIX = 'xt-ifr-'
33
+
34
+ /**
35
+ * 生成微应用id
36
+ * 增加前缀
37
+ * @param name 微应用名称(唯一)
38
+ * @returns 返回dom id
39
+ */
40
+ const generateId = (name: string) => {
41
+ return `${DOM_ID_PREFIX}${name}`
42
+ }
43
+ /**
44
+ * 处理微应用url
45
+ * @param url 微应用url
46
+ * @param params 携带参数
47
+ * @returns 拼接后的url
48
+ */
49
+ const generateURL = (url: string, params: { [key: string]: any } = {}) => {
50
+ function isObject(variable) {
51
+ return variable !== null && typeof variable === 'object' && !Array.isArray(variable)
52
+ }
53
+ const _parmas = typeof params === 'function' ? params() : isObject(params) ? params : {}
54
+ return `${url}?__XT_MICRO=true&${qs.stringify(_parmas)}`
55
+ }
56
+
57
+ class MicroApp {
58
+ app: App | AppOption
59
+ dom: HTMLIFrameElement
60
+ panel: HTMLElement
61
+
62
+ constructor(app: AppOption) {
63
+ this.app = app
64
+ app.preload && this.preload()
65
+ return this
66
+ }
67
+
68
+ mount(el: HTMLElement | string, isHide = false) {
69
+ let panel: HTMLElement | null
70
+ if (el) {
71
+ if (typeof el === 'string') {
72
+ panel = document.querySelector(el)
73
+ } else {
74
+ panel = el
75
+ }
76
+ //这里清空了容器的内容,为了方便,如果还有其他元素需要特别处理,而且也影响元素再复用
77
+ panel.innerHTML = ''
78
+ } else {
79
+ panel = document.body
80
+ }
81
+
82
+ if (panel) {
83
+ let ifr = <HTMLIFrameElement>document.getElementById(generateId(this.app.name))
84
+ if (!ifr) {
85
+ ifr = document.createElement('iframe')
86
+ ifr.id = generateId(this.app.name)
87
+ const url = generateURL(this.app.url, this.app.params)
88
+ ifr.src = url
89
+ ifr.classList.add('micro-iframe')
90
+ ifr.onload = this.onLoad.call(this)
91
+ // ifr.onunload = this.onUnload
92
+ ifr.onerror = this.onError
93
+ this.dom = ifr
94
+ }
95
+ // else {
96
+ // ifr = this.dom
97
+ // }
98
+ isHide ? (ifr.style.display = 'none') : ifr.style.removeProperty('display')
99
+ this.panel = panel
100
+ panel.appendChild(ifr)
101
+ } else {
102
+ throw new Error('el not found')
103
+ }
104
+ }
105
+
106
+ unmount() {
107
+ const appDom = document.getElementById(generateId(this.app.name))
108
+ if (appDom) {
109
+ if (this.preload) {
110
+ appDom.style.display = 'none'
111
+ document.body.appendChild(appDom)
112
+ } else {
113
+ this.panel.removeChild(appDom)
114
+ }
115
+ } else {
116
+ throw new Error('app dom not found')
117
+ }
118
+ }
119
+
120
+ /**
121
+ * 预加载渲染
122
+ * TODO 还差用户切走之后,如何将ifr还原回去,等待下次载入
123
+ * @param el 挂载dom对象
124
+ */
125
+ preloadRender(el: HTMLElement | string) {
126
+
127
+ let appDom: HTMLElement | null = this.dom
128
+ this.mount(el)
129
+
130
+ // const appDom = document.getElementById(generateId(this.app.name))
131
+ // if (!appDom) {
132
+ //
133
+ // this.render(el)
134
+ // return
135
+ // }
136
+ //
137
+ // let panel
138
+ // if (typeof el === 'string') {
139
+ // panel = document.querySelector(el)
140
+ // } else {
141
+ // panel = el
142
+ // }
143
+ // appDom!.style.removeProperty('display')
144
+ // panel?.appendChild(appDom)
145
+ }
146
+
147
+ /**
148
+ * 预加载app,先放到body,待启动时,挪过来
149
+ */
150
+ preload() {
151
+ this.mount(undefined, true)
152
+ }
153
+
154
+ changeOptions(options: Partial<AppOption>) {
155
+ this.app = {
156
+ ...this.app,
157
+ ...options,
158
+ }
159
+ }
160
+
161
+ onLoad() {
162
+
163
+ }
164
+
165
+ onUnload() {
166
+
167
+ }
168
+
169
+ onError() {
170
+
171
+ }
172
+ }
173
+
174
+ /**
175
+ * 安装微应用
176
+ * @param app
177
+ */
178
+ export const setupApp = (app: AppOption) => {
179
+ _postMicro()
180
+ return (_apps[app.name] = {
181
+ ...app,
182
+ instance: new MicroApp(app),
183
+ })
184
+ }
185
+
186
+ export const getApp = (name: string) => {
187
+ return _apps[name]
188
+ }
189
+
190
+ const setApp = (app: App, options?: Partial<AppOption>) => {
191
+ return (_apps[app.name] = {
192
+ ...app,
193
+ ...options,
194
+ })
195
+ }
196
+
197
+ /**
198
+ * 启动微应用
199
+ * @param name 应用名称
200
+ * @param options 应用配置
201
+ * @returns
202
+ */
203
+ export const startApp = (name: string, options?: Partial<AppOption>) => {
204
+ return new Promise((resolve, reject) => {
205
+ let app = getApp(name)
206
+ if (app) {
207
+ if (options) {
208
+ app.instance.changeOptions(options)
209
+ app = setApp(app, options)
210
+ }
211
+ if (app.preload) {
212
+ app.instance.preloadRender(app.el)
213
+ } else {
214
+ app.instance.mount(app.el)
215
+ }
216
+ _postMicro()
217
+ resolve(app)
218
+ } else {
219
+ reject('app not found')
220
+ }
221
+ })
222
+ }
223
+
224
+ export const stopApp = (name: string) => {
225
+ let app = getApp(name)
226
+ if (app) {
227
+ app.instance.unmount()
228
+ }
229
+ }
230
+
231
+ function getQueryParameter(name, url = window.location.href) {
232
+ name = name.replace(/[\[\]]/g, "\\$&");
233
+ var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
234
+ results = regex.exec(url);
235
+ if (!results) return null;
236
+ if (!results[2]) return '';
237
+ return decodeURIComponent(results[2].replace(/\+/g, " "));
238
+ }
239
+
240
+ /**
241
+ * 微应用-子应用初始化
242
+ * 非必须,但建议使用
243
+ */
244
+ export const microInit = () => {
245
+
246
+ //处理url参数
247
+ // const urlParams = new URLSearchParams(window.location.search)
248
+ // const micro = urlParams.get('__XT_MICRO')
249
+ const micro = getQueryParameter('__XT_MICRO')
250
+ //
251
+ if (micro) {
252
+ window['__XT_MICRO'] = true
253
+ }
254
+ //监听message,设置环境变量
255
+ window.addEventListener('message', (e) => {
256
+ //
257
+ if (e.data.cmd === '$xt/micro') {
258
+ //
259
+ window['__XT_MICRO'] = e.data.data
260
+ }
261
+ })
262
+ }
263
+
264
+ // window.postMessage({ cmd: '$xt/micro', data: true }, '*')
265
+
266
+ const _postMicro = () => {
267
+ window.postMessage({ cmd: '$xt/micro', data: true }, '*')
268
+ }
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@xingtukeji/micro",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "description": "",
6
+ "main": "index.ts",
7
+ "author": "",
8
+ "license": "ISC",
9
+ "publishConfig": {
10
+ "access": "public",
11
+ "registry": "https://registry.npmjs.org/"
12
+ },
13
+ "scripts": {}
14
+ }