From 9b1b661f1f704508cf7eae336fd033a03b5d1433 Mon Sep 17 00:00:00 2001
From: bailicangdu <1264889788@qq.com>
Date: Mon, 13 Sep 2021 19:10:17 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=86=E6=B8=B2?=
=?UTF-8?q?=E6=9F=93=E7=BC=93=E5=AD=98micro-app=E5=85=83=E7=B4=A0=E6=97=B6?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84micro-app-head,=20micro-app-body?=
=?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/zh-cn/changelog.md | 9 +++
docs/zh-cn/configure.md | 4 +-
docs/zh-cn/data.md | 2 +-
docs/zh-cn/route.md | 71 ++++++++++++++++---
docs/zh-cn/static-source.md | 4 +-
examples/main-vue2/src/pages/multiple.vue | 2 +
.../main-vue3-vite/src/pages/multiple.vue | 2 +
package.json | 2 +-
src/__tests__/micro_app_element.test.ts | 37 ++++------
src/create_app.ts | 5 +-
src/libs/utils.ts | 7 +-
src/micro_app_element.ts | 39 ++++++----
12 files changed, 130 insertions(+), 54 deletions(-)
diff --git a/docs/zh-cn/changelog.md b/docs/zh-cn/changelog.md
index 2b8f694cc..9b018be50 100644
--- a/docs/zh-cn/changelog.md
+++ b/docs/zh-cn/changelog.md
@@ -8,6 +8,15 @@
---
+### 0.3.3
+
+`2021-09-13`
+
+- **Bug Fix**
+
+ - 🐞 修复了data属性赋值后插入文档时,初始化data值无法通过setAttribute拦截的问题
+ - 🐞 修复了渲染缓存micro-app元素时导致的micro-app-head, micro-app-body重复的问题
+
### 0.3.2
`2021-09-10`
diff --git a/docs/zh-cn/configure.md b/docs/zh-cn/configure.md
index 96d7ad235..80c26a80b 100644
--- a/docs/zh-cn/configure.md
+++ b/docs/zh-cn/configure.md
@@ -159,7 +159,7 @@ microApp.start({
```
-### global(资源共享)
+### global
当多个子应用使用相同的js或css资源,在link、script设置`global`属性会将文件提取为公共文件,共享给其它应用。
设置`global`属性后文件第一次加载会放入公共缓存,其它子应用加载相同的资源时直接从缓存中读取内容并渲染,从而提升性能、节省流量。
@@ -170,7 +170,7 @@ microApp.start({
```
-### globalAssets(资源共享)
+### globalAssets
globalAssets用于设置全局共享资源,它和预加载的思路相同,在浏览器空闲时加载资源并放入缓存,提高渲染效率。
当子应用加载相同地址的js或css资源时,会直接从缓存中提取数据。
diff --git a/docs/zh-cn/data.md b/docs/zh-cn/data.md
index 465df7b60..edb92f5f5 100644
--- a/docs/zh-cn/data.md
+++ b/docs/zh-cn/data.md
@@ -41,7 +41,7 @@
```
diff --git a/docs/zh-cn/route.md b/docs/zh-cn/route.md
index f24bbbbf4..6e61a32d5 100644
--- a/docs/zh-cn/route.md
+++ b/docs/zh-cn/route.md
@@ -5,7 +5,7 @@
micro-app不是iframe,不会重开一个window窗口,基座应用和子应用本质是在同一个页面渲染,所以影响到子应用路由的是浏览器地址。micro-app的url属性只是html的地址,它只是用来获取html。
-**举个栗子🌰 :**
+**举个栗子 🌰 :**
浏览器地址为:`http://localhost:3000/page1/`,此时路由地址为`page1`。
@@ -21,7 +21,7 @@ micro-app不是iframe,不会重开一个window窗口,基座应用和子应
同理,页面参数和hash也是以浏览器为准。
-**再举个栗子🌰 :**
+**栗子2 🌰 :**
子应用是hash路由,我们要渲染子应用的page1页面,那么下面的hash值是无效的,`#/page1`应该添加到浏览器地址上。
```html
@@ -32,7 +32,7 @@ micro-app不是iframe,不会重开一个window窗口,基座应用和子应
```
-**再再举个栗子🌰 :**
+**栗子3 🌰 :**
基座应用是history路由,子应用是hash路由,我们要跳转基座应用的`my-app`页面,页面中嵌入子应用,我们要展现子应用的`page1`页面。
@@ -46,8 +46,7 @@ micro-app配置如下:
```
-
-**再再再举个栗子🌰 :**
+**栗子4 🌰 :**
基座应用是history路由,子应用也是history路由,我们要跳转基座应用的`my-app`页面,`my-app`页面中嵌入子应用,我们要展现子应用的`page1`页面。
@@ -239,7 +238,63 @@ window.dispatchEvent(new PopStateEvent('popstate', { state: null }))
> [!NOTE]
> 1、popstate事件是全局发送的,所有正在运行的应用(包括发送popstate事件的应用)都会接受到popstate事件并进行路由匹配,此时要注意和兜底路由的冲突。
>
-> 2、一些框架(比如angular)对popstate支持性不好,此时建议使用下面的方式2进行跳转。
+> 2、popstate常出现一些预料不到的问题,此时建议使用下面的方式2进行跳转。
+
+### 2、基座路由控制
+
+例如:
+
+**基座下发跳转方法:**
+
+
+#### ** React **
+```js
+import { useEffect } from 'react'
+import microApp from '@micro-zoe/micro-app'
+
+export default (props) => {
+ function pushState (path) {
+ props.history.push(path)
+ }
+
+ useEffect(() => {
+ // 👇 基座向子应用下发一个名为pushState的方法
+ microApp.setData(子应用名称, { pushState })
+ }, [])
+
+ return (
+
+
+
+ )
+}
+```
+
+#### ** Vue **
+
+```html
+
+
+
+
+```
+
+
+子应用通过 `window.microApp.getData().pushState(path)` 进行跳转。
-### 2、数据通信进行控制
-如基座下发指令控制子应用进行跳转,或者子应用向基座应用上传一个可以控制自身路由的函数。
+这种方式更加规范,出错的可能性更小。
diff --git a/docs/zh-cn/static-source.md b/docs/zh-cn/static-source.md
index 0466c6597..590d9e19e 100644
--- a/docs/zh-cn/static-source.md
+++ b/docs/zh-cn/static-source.md
@@ -41,7 +41,7 @@ import './public-path'
当多个子应用拥有相同的js或css资源,可以指定这些资源在多个子应用之间共享,在子应用加载时直接从缓存中提取数据,从而提高渲染效率和性能。
设置资源共享的方式有两种:
-#### 1、设置 globalAssets
+#### 方式一、设置 globalAssets
globalAssets用于设置全局共享资源,它和预加载的思路相同,在浏览器空闲时加载资源并放入缓存。
当子应用加载相同地址的js或css资源时,会直接从缓存中提取数据。
@@ -59,7 +59,7 @@ microApp.start({
})
```
-#### 2、设置 global 属性
+#### 方式一、设置 global 属性
在link、script设置`global`属性会将文件提取为公共文件,共享给其它应用。
设置`global`属性后文件第一次加载会放入公共缓存,其它子应用加载相同的资源时直接从缓存中读取内容。
diff --git a/examples/main-vue2/src/pages/multiple.vue b/examples/main-vue2/src/pages/multiple.vue
index 3768e2889..1fb75c7b4 100644
--- a/examples/main-vue2/src/pages/multiple.vue
+++ b/examples/main-vue2/src/pages/multiple.vue
@@ -8,6 +8,7 @@
url='http://localhost:3001/micro-app/react16/'
baseRoute='/multiple'
:data='data'
+ library='micro-app-react16'
>
diff --git a/examples/main-vue3-vite/src/pages/multiple.vue b/examples/main-vue3-vite/src/pages/multiple.vue
index c4ca3ad6d..bbb0b5ab2 100644
--- a/examples/main-vue3-vite/src/pages/multiple.vue
+++ b/examples/main-vue3-vite/src/pages/multiple.vue
@@ -8,6 +8,7 @@
url='http://localhost:3001/micro-app/react16/'
baseRoute='/multiple'
:data='data'
+ library='micro-app-react16'
>
diff --git a/package.json b/package.json
index f61e66165..90f6d3f63 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@micro-zoe/micro-app",
- "version": "0.3.2",
+ "version": "0.3.3",
"description": "A minimalist solution for building micro front-end applications",
"private": false,
"main": "lib/index.min.js",
diff --git a/src/__tests__/micro_app_element.test.ts b/src/__tests__/micro_app_element.test.ts
index 71efa9e46..9eea9b50d 100644
--- a/src/__tests__/micro_app_element.test.ts
+++ b/src/__tests__/micro_app_element.test.ts
@@ -1,7 +1,6 @@
/* eslint-disable promise/param-names */
import { commonStartEffect, releaseAllEffect, ports } from './common'
import { appInstanceMap } from '../create_app'
-import MicroAppElement from '../micro_app_element'
import microApp from '..'
import { defer } from '../libs/utils'
@@ -163,27 +162,6 @@ describe('micro_app_element', () => {
appCon.removeChild(microappElement8)
})
- // microAppCount为0后依然卸载应用,此时无法执行卸载
- test('unmount app when microAppCount less than 1', async () => {
- // 清空所有app
- appCon.innerHTML = ''
- const microappElement9 = document.createElement('micro-app')
- microappElement9.setAttribute('name', 'test-app9')
- microappElement9.setAttribute('url', `http://127.0.0.1:${ports.micro_app_element}/common/`)
-
- appCon.appendChild(microappElement9)
- await new Promise((reslove) => {
- microappElement9.addEventListener('mounted', () => {
- // @ts-ignore
- microappElement9.disconnectedCallback()
- expect(MicroAppElement.microAppCount).toBe(0)
- reslove(true)
- }, false)
- })
-
- appCon.removeChild(microappElement9)
- })
-
// 重新渲染带有shadowDom和baseurl属性应用 -- 分支覆盖
test('coverage branch of remount app with shadowDom & baseurl', async () => {
const microappElement10 = document.createElement('micro-app')
@@ -253,4 +231,19 @@ describe('micro_app_element', () => {
})
})
})
+
+ // getBaseRouteCompatible 分支覆盖
+ test('coverage branch of getBaseRouteCompatible', async () => {
+ const microappElement14 = document.createElement('micro-app')
+ microappElement14.setAttribute('name', 'test-app14')
+ microappElement14.setAttribute('url', `http://127.0.0.1:${ports.micro_app_element}/common/`)
+ microappElement14.setAttribute('baseroute', '/path')
+
+ appCon.appendChild(microappElement14)
+ await new Promise((reslove) => {
+ microappElement14.addEventListener('mounted', () => {
+ reslove(true)
+ }, false)
+ })
+ })
})
diff --git a/src/create_app.ts b/src/create_app.ts
index ba4824178..1ef61fc14 100644
--- a/src/create_app.ts
+++ b/src/create_app.ts
@@ -130,7 +130,7 @@ export default class CreateApp implements AppInterface {
this.status = appStatus.MOUNTING
- cloneNode(this.source.html!, this.container!)
+ cloneNode(this.source.html!, this.container! as Element)
this.sandBox?.start(this.baseroute)
if (!this.umdHookMount) {
@@ -142,11 +142,10 @@ export default class CreateApp implements AppInterface {
this.umdHookMount = mount as Func
this.umdHookunMount = unmount as Func
this.sandBox?.recordUmdSnapshot()
- this.source.html!.innerHTML = ''
/**
* TODO: Some UI frameworks insert and record container elements to micro-app-body, such as modal and notification. The DOM remounted is a cloned element, so the cached elements of UI frameworks are invalid, this may cause bug when remount app
*/
- cloneNode(this.container!, this.source.html!)
+ cloneNode(this.container! as Element, this.source.html!)
formatHTMLStyleAfterUmdInit(this.source.html!, this.name)
this.umdHookMount()
}
diff --git a/src/libs/utils.ts b/src/libs/utils.ts
index fe0f5bc98..b2958d65a 100644
--- a/src/libs/utils.ts
+++ b/src/libs/utils.ts
@@ -244,10 +244,11 @@ export function pureCreateElement (tagNam
* @param origin Cloned element
* @param target Accept cloned elements
*/
-export function cloneNode (origin: T, target: Q): void {
- const clonedOrigin = origin.cloneNode(true)
+export function cloneNode (origin: T, target: Q): void {
+ target.innerHTML = ''
+ const clonedNode = origin.cloneNode(true)
const fragment = document.createDocumentFragment()
- Array.from(clonedOrigin.childNodes).forEach((node: Node) => {
+ Array.from(clonedNode.childNodes).forEach((node: Node) => {
fragment.appendChild(node)
})
target.appendChild(fragment)
diff --git a/src/micro_app_element.ts b/src/micro_app_element.ts
index c34b9b299..dc8fa0d39 100644
--- a/src/micro_app_element.ts
+++ b/src/micro_app_element.ts
@@ -11,19 +11,28 @@ import microApp from './micro_app'
import dispatchLifecyclesEvent from './interact/lifecycles_event'
import { listenUmountAppInline, replaseUnmountAppInline } from './libs/additional'
+// record all micro-app elements
+const elementInstanceMap = new Map()
export default class MicroAppElement extends HTMLElement implements MicroAppElementType {
- static microAppCount = 0
static get observedAttributes (): string[] {
return ['name', 'url']
}
+ constructor () {
+ super()
+ // cloned node of umd container also trigger constructor, we should skip
+ if (!this.querySelector('micro-app-head')) {
+ this.performWhenFirstCreated()
+ }
+ }
+
appName = ''
appUrl = ''
version = version
isWating = false
cacheData: Record | null = null
- // 👇Configuration
+ // 👇 Configuration
// shadowDom: use shadowDOM, default is false
// destory: whether delete cache resources when unmount, default is false
// inline: whether js runs in inline script mode, default is false
@@ -33,10 +42,8 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem
// baseRoute: route prefix, default is ''
connectedCallback (): void {
- if (++MicroAppElement.microAppCount === 1) {
- patchElementPrototypeMethods()
- rejectMicroAppStyle()
- listenUmountAppInline()
+ if (!elementInstanceMap.has(this)) {
+ this.performWhenFirstCreated()
}
defer(() => dispatchLifecyclesEvent(
@@ -71,12 +78,11 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem
}
disconnectedCallback (): void {
- if (MicroAppElement.microAppCount > 0) {
- this.handleUnmount(this.getDisposeResult('destory'))
- if (--MicroAppElement.microAppCount === 0) {
- releasePatches()
- replaseUnmountAppInline()
- }
+ elementInstanceMap.delete(this)
+ this.handleUnmount(this.getDisposeResult('destory'))
+ if (elementInstanceMap.size === 0) {
+ releasePatches()
+ replaseUnmountAppInline()
}
}
@@ -104,6 +110,15 @@ export default class MicroAppElement extends HTMLElement implements MicroAppElem
}
}
+ // Perform global initialization when the element count is 1
+ performWhenFirstCreated (): void {
+ if (elementInstanceMap.set(this, true).size === 1) {
+ patchElementPrototypeMethods()
+ rejectMicroAppStyle()
+ listenUmountAppInline()
+ }
+ }
+
/**
* handle for change of name an url after element inited
*/