diff --git a/dev/children/vite2/src/main.js b/dev/children/vite2/src/main.js index 4974ba1d3..1c1e90a47 100644 --- a/dev/children/vite2/src/main.js +++ b/dev/children/vite2/src/main.js @@ -47,7 +47,7 @@ let router = null let history = null // 将渲染操作放入 mount 函数 window.mount = (data) => { - history = createWebHistory(import.meta.env.BASE_URL) + history = createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || import.meta.env.BASE_URL) router = createRouter({ history, routes, diff --git a/dev/children/vite4/package.json b/dev/children/vite4/package.json index 0c809e3a7..48db146d7 100644 --- a/dev/children/vite4/package.json +++ b/dev/children/vite4/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "start": "vite", + "start": "vite --force", "build": "vite build", "preview": "vite preview" }, diff --git a/dev/children/vite4/src/main.js b/dev/children/vite4/src/main.js index e0f6dab0e..beef4fae0 100644 --- a/dev/children/vite4/src/main.js +++ b/dev/children/vite4/src/main.js @@ -47,7 +47,8 @@ let router = null let history = null // 将渲染操作放入 mount 函数 window.mount = (data) => { - history = createWebHistory(import.meta.env.BASE_URL) + // console.log(1212121212121212, location.href) + history = createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || import.meta.env.BASE_URL) router = createRouter({ history, routes, diff --git a/dev/main-react16/config/routes.js b/dev/main-react16/config/routes.js index 39eceff8c..79d4abe2a 100644 --- a/dev/main-react16/config/routes.js +++ b/dev/main-react16/config/routes.js @@ -51,6 +51,13 @@ export default [ component: './vite2/vite2', exact: false, }, + { + path: '/vite4', + name: 'vite4', + icon: 'RocketOutlined', + component: './vite4/vite4', + exact: false, + }, { path: '/angular11', name: 'angular11', diff --git a/dev/main-react16/src/config.js b/dev/main-react16/src/config.js index fbc0ded9e..785c9182c 100644 --- a/dev/main-react16/src/config.js +++ b/dev/main-react16/src/config.js @@ -5,6 +5,7 @@ let config = { vue2: 'http://localhost:4001/', vue3: 'http://localhost:4002/', vite2: 'http://localhost:7001/', + vite4: 'http://localhost:7002/', angular11: 'http://localhost:6001/', angular14: 'http://localhost:6002/', } @@ -20,6 +21,7 @@ if (isEnvPro) { angular11: locationOrigin, angular14: locationOrigin, vite2: locationOrigin, + vite4: locationOrigin, } } diff --git a/dev/main-react16/src/locales/zh-CN/menu.js b/dev/main-react16/src/locales/zh-CN/menu.js index b3f19b6d8..013e0599b 100644 --- a/dev/main-react16/src/locales/zh-CN/menu.js +++ b/dev/main-react16/src/locales/zh-CN/menu.js @@ -8,4 +8,5 @@ export default { 'menu.multiple': '多个应用', 'menu.self': '自带页面', 'menu.vite2': 'vite2应用', + 'menu.vite4': 'vite4应用', }; diff --git a/dev/main-react16/src/pages/react16/react16.js b/dev/main-react16/src/pages/react16/react16.js index e3f4d9daf..686c9f0a5 100644 --- a/dev/main-react16/src/pages/react16/react16.js +++ b/dev/main-react16/src/pages/react16/react16.js @@ -327,11 +327,11 @@ export default class App extends React.Component { microApp.addGlobalDataListener(this.handleGlobalDataForBaseApp) - setTimeout(() => { - this.setState({ - showMicroApp: !this.state.showMicroApp, - }) - }, 0); + // setTimeout(() => { + // this.setState({ + // showMicroApp: !this.state.showMicroApp, + // }) + // }, 0); // this.releaseBeforeEach1 = microApp.router.beforeEach((to, from, appName) => { // // const a = document.createElement('div') diff --git a/dev/main-react16/src/pages/vite2/vite2.js b/dev/main-react16/src/pages/vite2/vite2.js index ee5b66cbe..c3668a571 100644 --- a/dev/main-react16/src/pages/vite2/vite2.js +++ b/dev/main-react16/src/pages/vite2/vite2.js @@ -104,10 +104,11 @@ function vite2 (props) { // disableSandbox iframe keep-router-state - // disable-memory-router + disable-memory-router // disable-patch-request // keep-alive // default-page='/micro-app/vite2/page2' + baseroute='/micro-app/demo/vite2' > diff --git a/dev/main-react16/src/pages/vite4/vite4.js b/dev/main-react16/src/pages/vite4/vite4.js new file mode 100644 index 000000000..adb16c72a --- /dev/null +++ b/dev/main-react16/src/pages/vite4/vite4.js @@ -0,0 +1,120 @@ +/** @jsxRuntime classic */ +/** @jsx jsxCustomEvent */ +import jsxCustomEvent from '@micro-zoe/micro-app/polyfill/jsx-custom-event' +import { useState, useEffect } from 'react' +import { Button, Spin, Col } from 'antd' +import { LoadingOutlined } from '@ant-design/icons' +// import { EventCenterForMicroApp } from '@micro-zoe/micro-app' +import config from '../../config' +import './vite4.less' +import microApp from '@micro-zoe/micro-app' + +// 注册子应用vite4的数据通信对象 +// window.eventCenterForVite = new EventCenterForMicroApp('vite4') + +const antIcon = + +function vite4 (props) { + const [data, changeData] = useState({from: '来自基座的初始化数据'}) + const [showLoading, hideLoading] = useState(true) + + function handleMounted () { + console.timeEnd('vite4') + hideLoading(false) + console.log('主应用-生命周期:mounted -- vite4') + } + + function handleDataChange (e) { + console.log('来自 vite4 子应用的数据', e.detail.data) + } + + function jumpToHome () { + microApp.router.push({name: 'vite4', path: '/micro-app/vite4/'}) + } + + function jumpToPage2 () { + microApp.router.push({name: 'vite4', path: '/micro-app/vite4/element-plus'}) + } + + function jumpToPage3 () { + microApp.router.push({name: 'vite4', path: '/micro-app/vite4/ant-design-vue'}) + } + + useEffect(() => { + console.time('vite4') + // const releaseBeforeEach1 = microApp.router.beforeEach((to, from, appName) => { + // console.log('全局 beforeEach: ', to, from, appName) + // }) + + // const releaseBeforeEach2 = microApp.router.beforeEach({ + // vite4 (to, from) { + // console.log('指定 beforeEach: ', to, from) + // } + // }) + + // const releaseAfterEach1 = microApp.router.afterEach((to, from, appName) => { + // console.log('全局 afterEach: ', to, from, appName) + // }) + + // const releaseAfterEach2 = microApp.router.afterEach({ + // vite4 (to, from) { + // console.log('指定 afterEach: ', to, from) + // } + // }) + + microApp.router.setBaseAppRouter(props.history) + + return () => { + // releaseBeforeEach1() + // releaseBeforeEach2() + // releaseAfterEach1() + // releaseAfterEach2() + } + }, []) + + return ( +
+
+ + + + + + +
+ { + showLoading && + } + hideLoading(false)} + onMounted={handleMounted} + onDataChange={handleDataChange} + onAfterhidden={() => console.log('基座:keep-alive:Afterhidden 已推入后台')} + onBeforeshow={() => console.log('基座:keep-alive:Beforeshow 即将推入前台')} + onAftershow={() => {console.log('基座:keep-alive:Aftershow 已经推入前台'); hideLoading(false)}} + onError={() => console.log('渲染出错')} + // destroy + // inline + // disableSandbox + iframe + keep-router-state + disable-memory-router + // disable-patch-request + // keep-alive + // default-page='/micro-app/vite4/page2' + baseroute='/micro-app/demo/vite4' + > + + + {/* */} +
+ ) +} + +export default vite4 diff --git a/dev/main-react16/src/pages/vite4/vite4.less b/dev/main-react16/src/pages/vite4/vite4.less new file mode 100644 index 000000000..92a2fdbee --- /dev/null +++ b/dev/main-react16/src/pages/vite4/vite4.less @@ -0,0 +1,3 @@ +.test-vite4 { + color: red; +} diff --git a/src/create_app.ts b/src/create_app.ts index f13cff5c7..16422590e 100644 --- a/src/create_app.ts +++ b/src/create_app.ts @@ -388,6 +388,12 @@ export default class CreateApp implements AppInterface { this.sandBox?.recordAndReleaseEffect({ keepAlive: true }) } } + /** + * TODO: 这里增加一个处理,如果渲染完成时已经卸载,则进行一些操作 + * 如果是默认模式:删除所有事件和定时器 + * 如果是umd模式:重新记录和清空事件 + * 补充:非必需,优先级低 + */ } /** diff --git a/src/sandbox/iframe/index.ts b/src/sandbox/iframe/index.ts index f12d637cf..6842c9f09 100644 --- a/src/sandbox/iframe/index.ts +++ b/src/sandbox/iframe/index.ts @@ -28,13 +28,14 @@ import { } from '../../interact' import { patchIframeRoute, + actionsForDisableMemoryRoute, } from './route' import { router, initRouteStateWithURL, clearRouteStateFromURL, addHistoryListener, - removeStateAndPathFromBrowser, + removePathFromBrowser, updateBrowserURLWithLocation, patchHistory, releasePatchHistory, @@ -166,7 +167,12 @@ export default class IframeSandbox { this.microAppWindow.__MICRO_APP_NAME__, ) } else { - this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute + // actions when memory-route disable + actionsForDisableMemoryRoute( + this.microAppWindow.__MICRO_APP_NAME__, + this.microAppWindow, + baseroute, + ) } /** @@ -486,7 +492,7 @@ export default class IframeSandbox { } public removeRouteInfoForKeepAliveApp (): void { - removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__) + removePathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__) } /** diff --git a/src/sandbox/iframe/route.ts b/src/sandbox/iframe/route.ts index db0847464..93492be79 100644 --- a/src/sandbox/iframe/route.ts +++ b/src/sandbox/iframe/route.ts @@ -12,6 +12,7 @@ import { import { assign, } from '../../libs/utils' +import globalEnv from '../../libs/global_env' export function patchIframeRoute ( appName: string, @@ -51,3 +52,34 @@ export function patchIframeRoute ( childHost, ) } + +/** + * actions when memory-route disable + * @param appName app name + * @param microAppWindow iframeWindow + * @param baseroute base route for child app + */ +export function actionsForDisableMemoryRoute ( + appName: string, + microAppWindow: microAppWindowType, + baseroute: string, +): void { + microAppWindow.__MICRO_APP_BASE_ROUTE__ = microAppWindow.__MICRO_APP_BASE_URL__ = baseroute + + /** + * Sync browser router info to iframe when disable memory-router + * e.g.: + * vue-router@4.x get target path by remove the base section from location.pathname + * code: window.location.pathname.slice(base.length) || '/'; (base is baseroute) + * NOTE: + * 1. iframe router and browser router are separated, we should update iframe router manually + * 2. withSandbox location is browser location when disable memory-router, so no need to do anything + */ + const rawLocation = globalEnv.rawWindow.location + updateMicroLocation( + appName, + rawLocation.href, + rawLocation, + 'prevent' + ) +} diff --git a/src/sandbox/router/api.ts b/src/sandbox/router/api.ts index d7dbf4a34..467bbd378 100644 --- a/src/sandbox/router/api.ts +++ b/src/sandbox/router/api.ts @@ -17,6 +17,7 @@ import { setMicroState, getMicroState, getMicroPathFromURL, + isMemoryRouterEnabled, } from './core' import { logError, @@ -99,7 +100,7 @@ function createRouterApi (): RouterApi { const appName = formatAppName(to.name) if (appName && isString(to.path)) { const app = appInstanceMap.get(appName) - if (app && (!app.sandBox || !app.useMemoryRouter)) { + if (app && !isMemoryRouterEnabled(appName)) { return logError(`navigation failed, memory router of app ${appName} is closed`) } // active apps, include hidden keep-alive app @@ -204,8 +205,8 @@ function createRouterApi (): RouterApi { * 2. useMemoryRouter is false */ function commonHandlerForAttachToURL (appName: string): void { - const app = appInstanceMap.get(appName)! - if (app.sandBox && app.useMemoryRouter) { + if (isMemoryRouterEnabled(appName)) { + const app = appInstanceMap.get(appName)! attachRouteToBrowserURL( appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location as MicroLocation), diff --git a/src/sandbox/router/core.ts b/src/sandbox/router/core.ts index 9041f002f..85d77fbc9 100644 --- a/src/sandbox/router/core.ts +++ b/src/sandbox/router/core.ts @@ -23,6 +23,7 @@ export function setMicroState ( appName: string, microState: MicroState, ): MicroState { + if (!isMemoryRouterEnabled(appName)) return microState const rawState = globalEnv.rawWindow.history.state const additionalState: Record = { microAppState: assign({}, rawState?.microAppState, { @@ -102,19 +103,22 @@ export function getMicroPathFromURL (appName: string): string | null { * @param microLocation location of child app */ export function setMicroPathToURL (appName: string, microLocation: MicroLocation): HandleMicroPathResult { + const targetFullPath = microLocation.pathname + microLocation.search + microLocation.hash + let isAttach2Hash = false + if (!isMemoryRouterEnabled(appName)) { + return { + fullPath: targetFullPath, + isAttach2Hash, + } + } let { pathname, search, hash } = globalEnv.rawWindow.location const queryObject = getQueryObjectFromURL(search, hash) - const encodedMicroPath = encodeMicroPath( - microLocation.pathname + - microLocation.search + - microLocation.hash - ) + const encodedMicroPath = encodeMicroPath(targetFullPath) /** * Is parent is hash router * In fact, this is not true. It just means that the parameter is added to the hash */ - let isAttach2Hash = false // If hash exists and search does not exist, it is considered as a hash route if (hash && !search) { isAttach2Hash = true @@ -208,5 +212,22 @@ export function getNoHashMicroPathFromURL (appName: string, baseUrl: string): st */ export function isEffectiveApp (appName: string): boolean { const app = appInstanceMap.get(appName) - return !!(app && !app.isPrefetch && !app.isHidden()) + /** + * !!(app && !app.isPrefetch && !app.isHidden()) + * 隐藏的keep-alive应用暂时不作为无效应用,原因如下 + * 1、隐藏后才执行去除浏览器上的微应用的路由信息的操作,导致微应用的路由信息无法去除 + * 2、如果保持隐藏应用内部正常跳转,阻止同步路由信息到浏览器,这样理论上是好的,但是对于location跳转改如何处理?location跳转是基于修改浏览器地址后发送popstate事件实现的,所以应该是在隐藏后不支持通过location进行跳转 + */ + return !!(app && !app.isPrefetch) +} + +/** + * Determine whether the app has enabled memory-router + * NOTE: + * 1. if sandbox disabled, memory-router is disabled + * 2. if app not exist, memory-router is disabled + */ +export function isMemoryRouterEnabled (appName: string): boolean { + const app = appInstanceMap.get(appName) + return !!(app && app.sandBox && app.useMemoryRouter) } diff --git a/src/sandbox/router/history.ts b/src/sandbox/router/history.ts index ee0bc38ff..65c668c50 100644 --- a/src/sandbox/router/history.ts +++ b/src/sandbox/router/history.ts @@ -21,6 +21,7 @@ import { getMicroState, getMicroPathFromURL, isEffectiveApp, + isMemoryRouterEnabled, } from './core' import { dispatchNativeEvent } from './event' import { updateMicroLocation } from './location' @@ -116,6 +117,8 @@ export function nativeHistoryNavigate ( * 2. proxyHistory.pushState/replaceState with limited popstateEvent * 3. api microApp.router.push/replace * 4. proxyLocation.hash = xxx + * NOTE: + * 1. hidden keep-alive app can jump internally, but will not synchronize to browser * @param appName app.name * @param methodName pushState/replaceState * @param result result of add/remove microApp path on browser url @@ -191,8 +194,8 @@ function reWriteHistoryMethod (method: History['pushState' | 'replaceState']): C excludeHiddenApp: true, excludePreRender: true, }).forEach(appName => { - const app = appInstanceMap.get(appName)! - if (app.sandBox && app.useMemoryRouter && !getMicroPathFromURL(appName)) { + if (isMemoryRouterEnabled(appName) && !getMicroPathFromURL(appName)) { + const app = appInstanceMap.get(appName)! attachRouteToBrowserURL( appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location as MicroLocation), diff --git a/src/sandbox/router/index.ts b/src/sandbox/router/index.ts index f77dabe18..07184bd1d 100644 --- a/src/sandbox/router/index.ts +++ b/src/sandbox/router/index.ts @@ -101,7 +101,7 @@ export function clearRouteStateFromURL ( const { pathname, search, hash } = createURL(url) updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent') } - removeStateAndPathFromBrowser(appName) + removePathFromBrowser(appName) clearRouterWhenUnmount(appName) } @@ -109,7 +109,7 @@ export function clearRouteStateFromURL ( * remove microState from history.state and remove microPath from browserURL * called on sandbox.stop or hidden of keep-alive app */ -export function removeStateAndPathFromBrowser (appName: string): void { +export function removePathFromBrowser (appName: string): void { attachRouteToBrowserURL( appName, removeMicroPathFromURL(appName), diff --git a/src/sandbox/with/index.ts b/src/sandbox/with/index.ts index a1c5ca5b0..be686f39d 100644 --- a/src/sandbox/with/index.ts +++ b/src/sandbox/with/index.ts @@ -52,7 +52,7 @@ import { initRouteStateWithURL, clearRouteStateFromURL, addHistoryListener, - removeStateAndPathFromBrowser, + removePathFromBrowser, updateBrowserURLWithLocation, patchHistory, releasePatchHistory, @@ -732,7 +732,7 @@ export default class WithSandBox implements WithSandBoxInterface { } public removeRouteInfoForKeepAliveApp (): void { - removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__) + removePathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__) } /**