diff --git a/dev/children/angular11/src/main.ts b/dev/children/angular11/src/main.ts
index d501962ac..31b0a83a4 100644
--- a/dev/children/angular11/src/main.ts
+++ b/dev/children/angular11/src/main.ts
@@ -11,7 +11,8 @@ if (environment.production) {
declare global {
interface Window {
microApp: any
- __MICRO_APP_NAME__: string
+ mount: CallableFunction
+ unmount: CallableFunction
__MICRO_APP_ENVIRONMENT__: string
}
}
@@ -25,37 +26,36 @@ declare global {
// })
// .catch(err => console.error(err))
-// console.log('微应用child-angular11渲染了');
+// console.log('微应用child-angular11渲染了 -- 默认模式');
// // 监听卸载操作
-// window.addEventListener("unmount", function () {
+// window.unmount = () => {
// app.destroy();
// app = null;
-// console.log('微应用child-angular11卸载了');
-// })
+// console.log('微应用child-angular11卸载了 --- 默认模式');
+// }
// ----------分割线---umd模式------两种模式任选其一-------------- //
let app = null;
-// 将渲染操作放入 mount 函数
-async function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = async () => {
app = await platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err))
- console.log('微应用child-angular11渲染了');
+ console.log('微应用child-angular11渲染了 -- UMD模式');
}
-// 将卸载操作放入 unmount 函数
-function unmount () {
+// 👇 将卸载操作放入 unmount 函数
+window.unmount = () => {
app.destroy();
app = null;
- console.log('微应用child-angular11卸载了');
+ console.log('微应用child-angular11卸载了 --- UMD模式');
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount();
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount();
}
+
+// -------------------分割线-------------------- //
diff --git a/dev/children/react16/src/index.js b/dev/children/react16/src/index.js
index cba39fc5b..e68ed6fbd 100644
--- a/dev/children/react16/src/index.js
+++ b/dev/children/react16/src/index.js
@@ -53,40 +53,38 @@ window.addEventListener('appstate-change', function (e) {
// document.getElementById('root')
// );
-// // 监听卸载
-// window.addEventListener('unmount', function () {
+// // 注册unmount函数,卸载时会自动执行
+// window.unmount = () => {
// ReactDOM.unmountComponentAtNode(document.getElementById('root'));
-// console.log('微应用react16卸载了 -- 自定义事件unmount');
-// })
+// console.log('微应用react16卸载了 -- 默认模式');
+// }
// console.timeEnd('react#16');
/* ----------------------分割线-umd模式--------------------- */
-function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
ReactDOM.render(
,
document.getElementById('root')
);
- console.log('微应用react16渲染了 -- 来自umd-mount');
+ console.log('微应用react16渲染了 -- UMD模式');
console.timeEnd('react#16');
}
-function unmount () {
- console.log('微应用react16卸载了 -- 来自umd-unmount');
+// 👇 将卸载操作放入 unmount 函数
+window.unmount = () => {
// 卸载时关闭弹窗
notification.destroy()
- // 卸载应用
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
+ console.log('微应用react16卸载了 -- UMD模式');
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount();
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
diff --git a/dev/children/react17/src/index.js b/dev/children/react17/src/index.js
index 9205d1baf..19716756b 100644
--- a/dev/children/react17/src/index.js
+++ b/dev/children/react17/src/index.js
@@ -8,7 +8,8 @@ import reportWebVitals from './reportWebVitals';
// 发送数据
window.microApp?.dispatch({'from': '来自微应用react17的数据' + (+new Date())})
-function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
ReactDOM.render(
@@ -16,32 +17,21 @@ function mount () {
document.getElementById('root')
);
- console.log("微应用react17渲染来了 -- 来自umd-mount");
+ console.log("微应用react17渲染来了 -- UMD模式");
}
-function unmount () {
- console.log("微应用react17卸载了 -- 来自umd-unmount");
- // 卸载应用
+// 👇 将卸载操作放入 unmount 函数
+window.unmount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
+ console.log("微应用react17卸载了 -- UMD模式");
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount();
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
-
-window.addEventListener('popstate', (e) => {
- console.log('popstate', e)
-})
-
-window.addEventListener('hashchange', (e) => {
- console.log('hashchange', e)
-})
diff --git a/dev/children/vue2/src/main.js b/dev/children/vue2/src/main.js
index a5dd000ec..d0833199b 100644
--- a/dev/children/vue2/src/main.js
+++ b/dev/children/vue2/src/main.js
@@ -36,47 +36,35 @@ let app = null
// }).$mount('#app')
// // 监听卸载
-// window.addEventListener('unmount', function () {
+// window.unmount = () => {
// app.$destroy()
// app.$el.innerHTML = ''
// app = null
-// console.log('微应用vue2卸载了 -- 自定义事件unmount')
-// })
+// console.log('微应用vue2卸载了 -- 默认模式')
+// }
// -------------------分割线-umd模式------------------ //
-export function mount (props) {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
app = new Vue({
router,
render: h => h(App),
- }).$mount(props?.container?.querySelector('#app') || '#app')
- console.log("微应用vue2渲染了 -- 来自umd-mount")
+ }).$mount('#app')
+ console.log("微应用vue2渲染了 -- UMD模式")
}
-// 卸载应用
-export function unmount () {
+// 👇 将卸载操作放入 unmount 函数
+window.unmount = () => {
app.$destroy()
app.$el.innerHTML = ''
app = null
- console.log("微应用vue2卸载了 -- 来自umd-unmount")
+ console.log("微应用vue2卸载了 -- UMD模式")
}
-export function bootstrap() {
-
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
-window.addEventListener('popstate', (e) => {
- console.log('子应用vue2 popstate', e)
-})
-
-window.addEventListener('hashchange', (e) => {
- console.log('子应用vue2 hashchange', e, e.newURL, e.oldURL)
-})
+// -------------------分割线------------------ //
diff --git a/dev/children/vue3/src/main.js b/dev/children/vue3/src/main.js
index 89878e3a9..0c32196b6 100644
--- a/dev/children/vue3/src/main.js
+++ b/dev/children/vue3/src/main.js
@@ -1,33 +1,42 @@
// import './public-path'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
-// import ElementPlus from 'element-plus'
+import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
import routes from './router'
import App from './App.vue'
+
+// -------------------分割线-默认模式------------------ //
// const app = createApp(App)
+// const router = createRouter({
+// history: createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || '/micro-app/vue3/'),
+// routes,
+// })
// app.use(ElementPlus)
// app.use(Antd)
// app.use(router)
// app.mount('#app')
-// console.log('微应用vue3渲染了')
+// console.log('微应用vue3渲染了 -- 默认模式')
// // 监听卸载
-// window.addEventListener('unmount', function () {
-// console.log('微应用vue3卸载了')
+// window.unmount = () => {
+// console.log('微应用vue3卸载了 -- 默认模式')
// // 卸载应用
// app.unmount()
-// })
+// }
+
+
+// -------------------分割线-umd模式------------------ //
let app = null
let router = null
let history = null
-// 将渲染操作放入 mount 函数
-function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
history = createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || '/micro-app/vue3/')
router = createRouter({
history,
@@ -40,24 +49,22 @@ function mount () {
app.use(router)
app.mount('#app')
- console.log('微应用child-vue3渲染了')
+ console.log('微应用child-vue3渲染了 -- UMD模式')
}
-// 将卸载操作放入 unmount 函数
-function unmount () {
+// 👇 将卸载操作放入 unmount 函数
+window.unmount = () => {
app?.unmount()
history?.destroy()
app = null
router = null
history = null
- console.log('微应用child-vue3卸载了')
+ console.log('微应用child-vue3卸载了 -- UMD模式')
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- // @ts-ignore
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
+
+// -------------------分割线------------------ //
diff --git a/docs/1.x/zh-cn/advanced.md b/docs/1.x/zh-cn/advanced.md
index 2b785efe2..5bac747d2 100644
--- a/docs/1.x/zh-cn/advanced.md
+++ b/docs/1.x/zh-cn/advanced.md
@@ -34,255 +34,3 @@ microApp.start({
> [!NOTE]
> 1、如果跨域请求带cookie,那么`Access-Control-Allow-Origin`不能设置为`*`,这一点需要注意
-
-
-## 2、性能&内存优化
-`micro-app`支持两种渲染微前端的模式,默认模式和umd模式。
-
-- **默认模式:**子应用在初次渲染和后续渲染时会顺序执行所有js,以保证多次渲染的一致性。
-- **umd模式:**子应用暴露出`mount`、`unmount`方法,此时只在初次渲染时执行所有js,后续渲染只会执行这两个方法,在多次渲染时具有更好的性能和内存表现。
-
-**我的项目是否需要切换为umd模式?**
-
-如果子应用渲染和卸载不频繁,那么使用默认模式即可,如果子应用渲染和卸载非常频繁建议使用umd模式。
-
-**切换为umd模式:子应用在window上注册mount和unmount方法**
-
-
-
-#### ** React **
-```js
-// index.js
-import React from "react"
-import ReactDOM from "react-dom"
-import App from './App'
-
-// 👇 将渲染操作放入 mount 函数
-function mount () {
- ReactDOM.render(, document.getElementById("root"))
-}
-
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
- ReactDOM.unmountComponentAtNode(document.getElementById("root"))
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
-}
-```
-
-#### ** Vue2 **
-
-```js
-// main.js
-import Vue from 'vue'
-import router from './router'
-import App from './App.vue'
-
-let app = null
-// 👇 将渲染操作放入 mount 函数
-function mount () {
- app = new Vue({
- router,
- render: h => h(App),
- }).$mount('#app')
-}
-
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
- app.$destroy()
- app.$el.innerHTML = ''
- app = null
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
-}
-```
-
-#### ** Vue3 **
-
-```js
-// main.js
-import { createApp } from 'vue'
-import * as VueRouter from 'vue-router'
-import routes from './router'
-import App from './App.vue'
-
-let app = null
-let router = null
-let history = null
-// 👇 将渲染操作放入 mount 函数
-function mount () {
- history = VueRouter.createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || '/')
- router = VueRouter.createRouter({
- history,
- routes,
- })
-
- app = createApp(App)
- app.use(router)
- app.mount('#app')
-}
-
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
- app.unmount()
- history.destroy()
- app = null
- router = null
- history = null
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
-}
-```
-
-#### ** Angular **
-以`angular11`为例。
-
-```js
-// main.ts
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-import { AppModule } from './app/app.module';
-
-declare global {
- interface Window {
- microApp: any
- __MICRO_APP_NAME__: string
- __MICRO_APP_ENVIRONMENT__: string
- }
-}
-
-let app = null;
-// 👇 将渲染操作放入 mount 函数
-async function mount () {
- app = await platformBrowserDynamic()
- .bootstrapModule(AppModule)
- .catch(err => console.error(err))
-}
-
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
- // angular在部分场景下执行destroy时会删除根元素app-root,此时可删除app.destroy()以避免这个问题
- app.destroy();
- app = null;
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount();
-}
-```
-
-
-#### ** Vite **
-因为vite作为子应用时关闭了沙箱,导致`__MICRO_APP_ENVIRONMENT__`和`__MICRO_APP_NAME__`两个变量失效,所以需要自行判断是否微前端环境以及手动填写应用name值。
-
-这里以 vue3 + vue-router4 为例:
-```js
-// main.js
-import { createApp } from 'vue'
-import * as VueRouter from 'vue-router'
-import routes from './router'
-import App from './App.vue'
-
-let app = null
-let router = null
-let history = null
-// 👇 将渲染操作放入 mount 函数
-function mount () {
- history = VueRouter.createWebHashHistory()
- router = VueRouter.createRouter({
- history,
- routes,
- })
-
- app = createApp(App)
- app.use(router)
- app.mount('#app')
-}
-
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
- app.unmount()
- history.destroy()
- app = null
- router = null
- history = null
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (如果是微前端环境) {
- // 应用的name值,即 元素的name属性值
- window[`micro-app-${应用的name值}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
-}
-```
-
-#### ** 其它 **
-```js
-// entry.js
-
-// 👇 将渲染操作放入 mount 函数
-function mount () {
- ...
-}
-
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
- ...
-}
-
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
-}
-```
-
-
-#### 自定义名称
-
-通常注册函数的形式为 `window['micro-app-${window.__MICRO_APP_NAME__}'] = {}`,但也支持自定义名称,`window['自定义的名称'] = {}`
-
-自定义的值需要在``标签中通过`library`属性指定。
-
-```html
-
-```
-
-> [!NOTE]
->
-> 1、mount和unmount方法都是必须的
->
-> 2、nextjs, nuxtjs等ssr框架作为子应用时暂不支持umd模式
->
-> 3、因为注册了`unmount`函数,所以卸载监听事件 `window.addEventListener('unmount', () => {})` 就不需要了
->
-> 4、umd模式下,因为初次渲染和后续渲染逻辑不同,可能会出现一些问题,如:[#138](https://github.com/micro-zoe/micro-app/issues/138)
diff --git a/docs/1.x/zh-cn/changelog.md b/docs/1.x/zh-cn/changelog.md
index 52c6c7d76..242eaf555 100644
--- a/docs/1.x/zh-cn/changelog.md
+++ b/docs/1.x/zh-cn/changelog.md
@@ -8,6 +8,18 @@
---
+### 1.0.0-alpha.5
+
+`2022-08-01`
+
+- **New**
+
+ - 🆕 新增子应用全局钩子函数`mount`, `unmount`,简化UMD模式接入步骤。
+
+- **Update**
+ - 🚀 更新了1.0版本文档
+
+
### 1.0.0-alpha.4
`2022-07-28`
diff --git a/docs/1.x/zh-cn/framework/angular.md b/docs/1.x/zh-cn/framework/angular.md
index ff4a84647..f2bae61d5 100644
--- a/docs/1.x/zh-cn/framework/angular.md
+++ b/docs/1.x/zh-cn/framework/angular.md
@@ -84,10 +84,10 @@ platformBrowserDynamic()
// 监听卸载操作
-window.addEventListener('unmount', function () {
+window.unmount = () => {
app.destroy();
app = null;
-})
+}
```
@@ -95,7 +95,7 @@ window.addEventListener('unmount', function () {
以下配置是针对子应用的,它们是可选的,建议根据实际情况选择设置。
#### 1、开启umd模式,优化内存和性能
-`micro-app`支持两种渲染微前端的模式,默认模式和umd模式。
+MicroApp支持两种渲染微前端的模式,默认模式和umd模式。
- **默认模式:**子应用在初次渲染和后续渲染时会顺序执行所有js,以保证多次渲染的一致性。
- **umd模式:**子应用暴露出`mount`、`unmount`方法,此时只在初次渲染时执行所有js,后续渲染只会执行这两个方法,在多次渲染时具有更好的性能和内存表现。
@@ -110,47 +110,40 @@ import { AppModule } from './app/app.module';
declare global {
interface Window {
microApp: any
- __MICRO_APP_NAME__: string
+ mount: CallableFunction
+ unmount: CallableFunction
__MICRO_APP_ENVIRONMENT__: string
}
}
let app = null;
-// 👇 将渲染操作放入 mount 函数
-async function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = async () => {
app = await platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err))
}
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
+// 👇 将卸载操作放入 unmount 函数,就是上面步骤2中的卸载函数
+window.unmount = () => {
// angular在部分场景下执行destroy时会删除根元素app-root,此时可删除app.destroy()以避免这个问题
app.destroy();
app = null;
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount();
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount();
}
```
-> [!NOTE]
->
-> 1、mount和unmount方法都是必须的
->
-> 2、因为注册了`unmount`函数,此时上述步骤2中监听卸载事件可以省略
#### 2、设置 webpack.jsonpFunction
如果微前端正常运行,则可以忽略这一步。
如果子应用资源加载混乱导致渲染失败,可以尝试设置`jsonpFunction`来解决,因为相同的`jsonpFunction`名称会导致资源污染。
-这种情况常见于基座和子应用都是通过`create-react-app`等脚手架创建的项目。
+这种情况常见于基座和子应用都是通过`create-react-app`等脚手架创建的react项目,vue项目中并不常见。
**解决方式:修改子应用的webpack配置**
diff --git a/docs/1.x/zh-cn/framework/react.md b/docs/1.x/zh-cn/framework/react.md
index 74d38f2f3..022bb8986 100644
--- a/docs/1.x/zh-cn/framework/react.md
+++ b/docs/1.x/zh-cn/framework/react.md
@@ -47,21 +47,21 @@ headers: {
}
```
-#### 2、监听卸载事件
-子应用被卸载时会接受到一个名为`unmount`的事件,在此可以进行卸载相关操作。
+#### 2、注册卸载函数
+子应用卸载时会自动执行`window.unmount`,在此可以进行卸载相关操作。
```js
// index.js
-window.addEventListener('unmount', function () {
+window.unmount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
-})
+}
```
### 可选设置
以下配置是针对子应用的,它们是可选的,建议根据实际情况选择设置。
#### 1、开启umd模式,优化内存和性能
-`micro-app`支持两种渲染微前端的模式,默认模式和umd模式。
+MicroApp支持两种渲染微前端的模式,默认模式和umd模式。
- **默认模式:**子应用在初次渲染和后续渲染时会顺序执行所有js,以保证多次渲染的一致性。
- **umd模式:**子应用暴露出`mount`、`unmount`方法,此时只在初次渲染时执行所有js,后续渲染只会执行这两个方法,在多次渲染时具有更好的性能和内存表现。
@@ -74,33 +74,25 @@ import React from "react"
import ReactDOM from "react-dom"
import App from './App'
-// 👇 将渲染操作放入 mount 函数
-function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
ReactDOM.render(, document.getElementById("root"))
}
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
+// 👇 将卸载操作放入 unmount 函数,就是上面步骤2中的卸载函数
+window.unmount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
```
-> [!NOTE]
->
-> 1、mount和unmount方法都是必须的
->
-> 2、因为注册了`unmount`函数,此时上述步骤2中监听卸载事件可以省略
#### 2、设置 webpack.jsonpFunction
-如果微前端正常运行,则可以忽略这一步。
+如果微前端正常运行,可以忽略这一步。
如果子应用资源加载混乱导致渲染失败,可以尝试设置`jsonpFunction`来解决,因为相同的`jsonpFunction`名称会导致资源污染。
diff --git a/docs/1.x/zh-cn/framework/vue.md b/docs/1.x/zh-cn/framework/vue.md
index 2b92aba96..f177c6c3a 100644
--- a/docs/1.x/zh-cn/framework/vue.md
+++ b/docs/1.x/zh-cn/framework/vue.md
@@ -46,8 +46,8 @@ devServer: {
}
```
-#### 2、监听卸载事件
-子应用被卸载时会接受到一个名为`unmount`的事件,在此可以进行卸载相关操作。
+#### 2、注册卸载函数
+子应用卸载时会自动执行`window.unmount`,在此可以进行卸载相关操作。
@@ -57,10 +57,10 @@ devServer: {
// main.js
const app = new Vue(...)
-// 监听卸载操作
-window.addEventListener('unmount', function () {
+// 卸载应用
+window.unmount = () => {
app.$destroy()
-})
+}
```
#### ** Vue3 **
@@ -69,10 +69,10 @@ window.addEventListener('unmount', function () {
const app = createApp(App)
app.mount('#app')
-// 监听卸载操作
-window.addEventListener('unmount', function () {
+// 卸载应用
+window.unmount = () => {
app.unmount()
-})
+}
```
@@ -81,7 +81,7 @@ window.addEventListener('unmount', function () {
以下配置是针对子应用的,它们是可选的,建议根据实际情况选择设置。
#### 1、开启umd模式,优化内存和性能
-`micro-app`支持两种渲染微前端的模式,默认模式和umd模式。
+MicroApp支持两种渲染微前端的模式,默认模式和umd模式。
- **默认模式:**子应用在初次渲染和后续渲染时会顺序执行所有js,以保证多次渲染的一致性。
- **umd模式:**子应用暴露出`mount`、`unmount`方法,此时只在初次渲染时执行所有js,后续渲染只会执行这两个方法,在多次渲染时具有更好的性能和内存表现。
@@ -98,27 +98,24 @@ import router from './router'
import App from './App.vue'
let app = null
-// 👇 将渲染操作放入 mount 函数
-function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
app = new Vue({
router,
render: h => h(App),
}).$mount('#app')
}
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
+// 👇 将卸载操作放入 unmount 函数,就是上面步骤2中的卸载函数
+window.unmount = () => {
app.$destroy()
app.$el.innerHTML = ''
app = null
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
```
@@ -133,8 +130,8 @@ import App from './App.vue'
let app = null
let router = null
let history = null
-// 👇 将渲染操作放入 mount 函数
-function mount () {
+// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
+window.mount = () => {
history = VueRouter.createWebHistory()
router = VueRouter.createRouter({
history,
@@ -146,8 +143,8 @@ function mount () {
app.mount('#app')
}
-// 👇 将卸载操作放入 unmount 函数
-function unmount () {
+// 👇 将卸载操作放入 unmount 函数,就是上面步骤2中的卸载函数
+window.unmount = () => {
app.unmount()
history.destroy()
app = null
@@ -155,29 +152,21 @@ function unmount () {
history = null
}
-// 微前端环境下,注册mount和unmount方法
-if (window.__MICRO_APP_ENVIRONMENT__) {
- window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
-} else {
- // 非微前端环境直接渲染
- mount()
+// 如果不在微前端环境,则直接执行mount渲染
+if (!window.__MICRO_APP_ENVIRONMENT__) {
+ window.mount()
}
```
-> [!NOTE]
->
-> 1、mount和unmount方法都是必须的
->
-> 2、因为注册了`unmount`函数,此时上述步骤2中监听卸载事件可以省略
#### 2、设置 webpack.jsonpFunction
如果微前端正常运行,则可以忽略这一步。
如果子应用资源加载混乱导致渲染失败,可以尝试设置`jsonpFunction`来解决,因为相同的`jsonpFunction`名称会导致资源污染。
-这种情况常见于基座和子应用都是通过`create-react-app`等脚手架创建的项目。
+这种情况常见于基座和子应用都是通过`create-react-app`脚手架创建的react项目,vue项目中并不常见。
**解决方式:修改子应用的webpack配置**
diff --git a/docs/1.x/zh-cn/keep-alive.md b/docs/1.x/zh-cn/keep-alive.md
index 8f1fb3b20..49ae27409 100644
--- a/docs/1.x/zh-cn/keep-alive.md
+++ b/docs/1.x/zh-cn/keep-alive.md
@@ -1,6 +1,4 @@
-*0.6.0及以上版本支持*
-
-在应用之间切换时,你有时会想保留这些应用的状态,以便恢复用户的操作行为和提升重复渲染的性能,此时开启keep-alive模式可以达到这样的效果。
+在应用之间切换时,我们有时会想保留这些应用的状态,以便恢复用户的操作行为和提升重复渲染的性能,此时开启keep-alive模式可以达到这样的效果。
开启keep-alive后,应用卸载时不会销毁,而是推入后台运行。
@@ -29,13 +27,13 @@ keep-alive模式与普通模式最大的不同是生命周期,因为它不会
子应用渲染出错时触发,只有会导致渲染终止的错误才会触发此生命周期。
#### 5. afterhidden
-子应用卸载时触发。
+子应用推入后台时触发。
#### 6. beforeshow
-子应用再次渲染之前触发`(初始化时不执行)`。
+子应用推入前台之前触发`(初始化时不执行)`。
#### 7. aftershow
-子应用再次渲染之后触发`(初始化时不执行)`。
+子应用推入前台之后触发`(初始化时不执行)`。
#### 监听生命周期
@@ -59,9 +57,9 @@ import jsxCustomEvent from '@micro-zoe/micro-app/polyfill/jsx-custom-event'
onCreated={() => console.log('micro-app元素被创建')}
onBeforemount={() => console.log('即将被渲染,只在初始化时执行一次')}
onMounted={() => console.log('已经渲染完成,只在初始化时执行一次')}
- onAfterhidden={() => console.log('已卸载')}
- onBeforeshow={() => console.log('即将重新渲染,初始化时不执行')}
- onAftershow={() => console.log('已经重新渲染,初始化时不执行')}
+ onAfterhidden={() => console.log('已推入后台')}
+ onBeforeshow={() => console.log('即将推入前台,初始化时不执行')}
+ onAftershow={() => console.log('已经推入前台,初始化时不执行')}
onError={() => console.log('渲染出错')}
/>
```
@@ -96,13 +94,13 @@ export default {
console.log('已经渲染完成,只在初始化时执行一次'),
},
afterhidden () {
- console.log('已卸载'),
+ console.log('已推入后台'),
},
beforeshow () {
- console.log('即将重新渲染,初始化时不执行'),
+ console.log('即将推入前台,初始化时不执行'),
},
aftershow () {
- console.log('已经重新渲染,初始化时不执行'),
+ console.log('已经推入前台,初始化时不执行'),
},
error () {
console.log('渲染出错'),
@@ -135,13 +133,8 @@ window.addEventListener('appstate-change', function (e) {
## 常见问题
-#### 1、再次渲染时url和页面不匹配
-keep-alive的应用在卸载时会保留页面状态,再次渲染时直接恢复,当应用再次渲染时的url与离开时不一致,则出现url和页面不匹配的问题。
-
-如果这个问题对你造成了困扰,可以通过监听`appstate-change`事件,在`beforeshow`时进行修复,根据url跳转对应的页面。
-
-#### 2、如何恢复页面滚动位置?
+#### 1、如何恢复页面滚动位置?
micro-app不会记录页面滚动位置,应用再次渲染时也不会进行恢复,需要开发者进行记录和恢复。
-#### 3、子应用内部页面切换后状态丢失
-micro-app的keep-alive是应用级别的,它只会保留当前正在活动的页面状态,以保证应用被卸载和重新渲染时的状态保留,如果想要缓存具体的页面或组件,需要使用子应用框架的能力,如:vue的keep-alive。
+#### 2、子应用内部页面切换后状态丢失
+micro-app的keep-alive是应用级别的,它只会保留当前正在活动的页面状态,如果想要缓存具体的页面或组件,需要使用子应用框架的能力,如:vue的keep-alive。
diff --git a/docs/1.x/zh-cn/questions.md b/docs/1.x/zh-cn/questions.md
index 698636cbf..b870cdc6a 100644
--- a/docs/1.x/zh-cn/questions.md
+++ b/docs/1.x/zh-cn/questions.md
@@ -36,79 +36,22 @@ micro-app依赖于CustomElements和Proxy两个较新的API。
- PC端:除了IE浏览器,其它浏览器基本兼容。
- 移动端:ios10+、android5+
-## 4、微应用无法渲染但没有报错
-请检查路由配置是否正确,详情查看[路由](/zh-cn/route)一章,或者[下面第5条:jsonpFunction是否冲突](/zh-cn/questions?id=_5、webpack-jsonpfunction-冲突导致渲染失败)
-## 5、webpack-jsonpfunction-冲突导致渲染失败
-这种情况常见于多个应用都是通过create-react-app等类似脚手架创建的项目,或一个应用多次重复渲染。
-
-因为相同的jsonpFunction名称会导致资源加载混乱。
-
-**解决方式:修改子应用的webpack配置**
-
-
-#### ** webpack4 **
-```js
-// webpack.config.js
-module.exports = {
- output: {
- ...
- jsonpFunction: `webpackJsonp_custom_app_name`,
- globalObject: 'window',
- },
-}
-```
-
-#### ** webpack5 **
-```js
-// webpack.config.js
-module.exports = {
- output: {
- ...
- chunkLoadingGlobal: 'webpackJsonp_custom_app_name',
- globalObject: 'window',
- },
-}
-```
-
-
-
-## 6、开发时每次保存文件时报错 (热更新导致报错)
-在一些场景下,热更新会导致保存时报错,请关闭热更新来解决这个问题,同时我们也在尝试更好的解决方案。
-
-## 7、vue3的问题
-**1、样式失效**
-
-通过[禁用样式隔离](/zh-cn/configure?id=disablescopecss)解决。
-
-**2、图片等静态资源无法正常加载**
-
-vue3中需要配置publicPath补全资源路径,详情请查看[publicPath](/zh-cn/static-source?id=publicpath)
-
-
-## 8、开发环境中渲染angular子应用报错
-目前需要关闭angular的热更新来解决这个问题,同时我们也在尝试更好的解决方案。
-```bash
-"scripts": {
- "start": "ng serve --live-reload false",
-},
-```
-
-## 9、micro-app 报错 an app named xx already exists
+## 4、micro-app 报错 an app named xx already exists
这是`name`名称冲突导致的,请确保每个子应用的`name`值是唯一的。
-## 10、基座应用的样式影响到子应用
+## 5、基座应用的样式影响到子应用
虽然我们将子应用的样式进行隔离,但基座应用的样式依然会影响到子应用,如果发生冲突,推荐通过约定前缀或CSS Modules方式解决。
如果你使用的是`ant-design`等组件库,一般会提供添加前缀进行样式隔离的功能。
-## 11、子应用在沙箱环境中如何获取到外部真实window?
+## 6、子应用在沙箱环境中如何获取到外部真实window?
目前有3种方式在子应用中获取外部真实window
- 1、new Function("return window")() 或 Function("return window")()
- 2、(0, eval)('window')
- 3、window.rawWindow
-## 12、错误信息:xxx 未定义
+## 7、错误信息:xxx undefined
**包括:**
- `xxx is not defined`
@@ -149,54 +92,11 @@ microApp.start({
})
```
-## 13、子应用加载sockjs-node失败
- 这个问题常见于create-react-app创建的子应用,推荐通过插件系统来解决。
-```js
-microApp.start({
- plugins: {
- modules: {
- '子应用name': [{
- loader(code) {
- if (code.indexOf('sockjs-node') > -1) {
- code = code.replace('window.location.port', '子应用端口').replace('window.location.hostname', '子应用host,如果和基座相同则不需要替换hostname')
- }
- return code
- }
- }],
- }
- }
-})
-```
-实际情况可能更加复杂,上面只是一种解决思路。
-
-
-## 14、子应用请求接口失败
- - 1、请确保接口请求没有跨域问题,因为子应用被加载到基座渲染,所以请求接口是从基座发送。
-
- - 2、请求的接口为相对地址,会以基座域名进行补全,导致报错。
-
- 如:`fetch('/api/data')`,在请求时会自动被浏览器补全为`fetch(基座域名 + '/api/data')`
-
- 为了避免这个问题,子应用需要使用完整的地址:`fetch(子应用域名 + '/api/data')`
-
-## 15、子应用反向代理失败
- **解决方式:**子应用使用完整的地址发送请求
-
- 如:`fetch('/api/data')` 改为 `fetch(子应用域名 + '/api/data')`
-
- 如果还是报跨域问题,则是服务端做了限制,此时需要撤除上述操作,并将子应用的代理放到基座应用中。
-
-## 16、子应用多次渲染后内存越来越大
- 参考[内存优化](/zh-cn/advanced?id=_3、内存优化)一章
-
-## 17、子应用之间如何跳转
- 参考[应用之间如何跳转](/zh-cn/route?id=应用之间如何跳转)一章
-
-## 18、jsonp请求如何处理?
+## 8、jsonp请求如何处理?
参考[ignore](/zh-cn/configure?id=ignore忽略元素)
-## 19、子应用通过a标签下载文件失败
+## 9、子应用通过a标签下载文件失败
**原因:**当跨域时(基座和文件在不同域名下),无法通过a标签的download属性实现下载。
**解决方式:**
diff --git a/src/create_app.ts b/src/create_app.ts
index 8a9848e6a..735c1775a 100644
--- a/src/create_app.ts
+++ b/src/create_app.ts
@@ -17,6 +17,7 @@ import {
isPromise,
logError,
getRootContainer,
+ isObject,
} from './libs/utils'
import dispatchLifecyclesEvent, { dispatchCustomEventToMicroApp } from './interact/lifecycles_event'
import globalEnv from './libs/global_env'
@@ -204,10 +205,14 @@ export default class CreateApp implements AppInterface {
execScripts(this.source.scripts, this, (isFinished: boolean) => {
if (!this.umdMode) {
const { mount, unmount } = this.getUmdLibraryHooks()
+ /**
+ * umdHookUnmount can works in non UMD mode
+ * register with window.unmount
+ */
+ this.umdHookUnmount = unmount as Func
// if mount & unmount is function, the sub app is umd mode
if (isFunction(mount) && isFunction(unmount)) {
this.umdHookMount = mount as Func
- this.umdHookUnmount = unmount as Func
this.umdMode = true
if (this.sandBox) this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true
// this.sandBox?.recordUmdSnapshot()
@@ -284,7 +289,7 @@ export default class CreateApp implements AppInterface {
* send an unmount event to the micro app or call umd unmount hook
* before the sandbox is cleared
*/
- if (this.umdHookUnmount) {
+ if (isFunction(this.umdHookUnmount)) {
try {
umdHookUnmountResult = this.umdHookUnmount()
} catch (e) {
@@ -469,8 +474,15 @@ export default class CreateApp implements AppInterface {
if (appStates.UNMOUNT !== this.state) {
const global = (this.sandBox?.proxyWindow ?? globalEnv.rawWindow) as any
this.libraryName = getRootContainer(this.container!).getAttribute('library') || `micro-app-${this.name}`
- // do not use isObject
- return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {}
+
+ if (isObject(global[this.libraryName])) {
+ return global[this.libraryName]
+ }
+
+ return {
+ mount: this.sandBox?.proxyWindow.mount,
+ unmount: this.sandBox?.proxyWindow.unmount,
+ }
}
return {}
diff --git a/src/libs/global_env.ts b/src/libs/global_env.ts
index 669b12158..1400d74e3 100644
--- a/src/libs/global_env.ts
+++ b/src/libs/global_env.ts
@@ -1,3 +1,4 @@
+import type { Func } from '@micro-app/types'
import { isSupportModuleScript, isBrowser, getCurrentAppName, assign } from './utils'
import { rejectMicroAppStyle } from '../source/patch'
@@ -20,6 +21,8 @@ declare global {
__MICRO_APP_ENVIRONMENT__?: boolean
__MICRO_APP_UMD_MODE__?: boolean
__MICRO_APP_BASE_APPLICATION__?: boolean
+ mount: Func
+ unmount: Func
}
interface Node {