From 0e1bba2a1d364da5850b852355e2d684ccc564b7 Mon Sep 17 00:00:00 2001
From: bailicangdu <1264889788@qq.com>
Date: Wed, 26 Apr 2023 20:47:54 +0800
Subject: [PATCH] feat(1.0.0-beta.4): Optimize iframe support for disable
memory router
---
dev/children/vite2/.gitignore | 1 +
dev/children/vite2/components.d.ts | 2 -
dev/children/vite4/.gitignore | 1 +
dev/children/vite4/package.json | 2 +-
dev/main-react16/src/pages/vite4/vite4.js | 27 ++--
src/create_app.ts | 7 +
src/sandbox/iframe/index.ts | 52 +++++---
src/sandbox/iframe/route.ts | 32 -----
src/sandbox/router/core.ts | 149 ++++++++++++----------
src/sandbox/router/history.ts | 4 +-
src/sandbox/router/index.ts | 4 +-
src/sandbox/router/location.ts | 1 +
src/sandbox/with/index.ts | 10 +-
typings/global.d.ts | 1 +
14 files changed, 158 insertions(+), 135 deletions(-)
diff --git a/dev/children/vite2/.gitignore b/dev/children/vite2/.gitignore
index 3b520b4a0..ef55d152e 100644
--- a/dev/children/vite2/.gitignore
+++ b/dev/children/vite2/.gitignore
@@ -1,6 +1,7 @@
node_modules
.DS_Store
dist
+vite2
dist-ssr
*.local
/vite
diff --git a/dev/children/vite2/components.d.ts b/dev/children/vite2/components.d.ts
index 2fc8b29de..b7c03aea1 100644
--- a/dev/children/vite2/components.d.ts
+++ b/dev/children/vite2/components.d.ts
@@ -66,8 +66,6 @@ declare module '@vue/runtime-core' {
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
- NButton: typeof import('naive-ui')['NButton']
- NPopover: typeof import('naive-ui')['NPopover']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
diff --git a/dev/children/vite4/.gitignore b/dev/children/vite4/.gitignore
index a547bf36d..cb9e2ada3 100644
--- a/dev/children/vite4/.gitignore
+++ b/dev/children/vite4/.gitignore
@@ -9,6 +9,7 @@ lerna-debug.log*
node_modules
dist
+vite4
dist-ssr
*.local
diff --git a/dev/children/vite4/package.json b/dev/children/vite4/package.json
index 48db146d7..0c809e3a7 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 --force",
+ "start": "vite",
"build": "vite build",
"preview": "vite preview"
},
diff --git a/dev/main-react16/src/pages/vite4/vite4.js b/dev/main-react16/src/pages/vite4/vite4.js
index adb16c72a..4ef322557 100644
--- a/dev/main-react16/src/pages/vite4/vite4.js
+++ b/dev/main-react16/src/pages/vite4/vite4.js
@@ -40,17 +40,21 @@ function vite4 (props) {
microApp.router.push({name: 'vite4', path: '/micro-app/vite4/ant-design-vue'})
}
+ function consoleRouteCurrent () {
+ console.log('router.current', microApp.router.current.get('vite4'))
+ }
+
useEffect(() => {
console.time('vite4')
- // const releaseBeforeEach1 = microApp.router.beforeEach((to, from, appName) => {
- // console.log('全局 beforeEach: ', to, from, appName)
- // })
+ 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 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)
@@ -65,8 +69,8 @@ function vite4 (props) {
microApp.router.setBaseAppRouter(props.history)
return () => {
- // releaseBeforeEach1()
- // releaseBeforeEach2()
+ releaseBeforeEach1()
+ releaseBeforeEach2()
// releaseAfterEach1()
// releaseAfterEach2()
}
@@ -82,6 +86,7 @@ function vite4 (props) {
+
{
@@ -106,7 +111,7 @@ function vite4 (props) {
keep-router-state
disable-memory-router
// disable-patch-request
- // keep-alive
+ keep-alive
// default-page='/micro-app/vite4/page2'
baseroute='/micro-app/demo/vite4'
>
diff --git a/src/create_app.ts b/src/create_app.ts
index 16422590e..48135e8df 100644
--- a/src/create_app.ts
+++ b/src/create_app.ts
@@ -503,6 +503,7 @@ export default class CreateApp implements AppInterface {
keepRouteState: keepRouteState && !destroy,
destroy,
clearData: clearData || destroy,
+ useMemoryRouter: this.useMemoryRouter,
})
// dispatch unmount event to base app
@@ -589,6 +590,12 @@ export default class CreateApp implements AppInterface {
false,
)
+ /**
+ * TODO:
+ * 1. iframe沙箱在关闭虚拟路由系统时,重新展示时不更新浏览器地址,这样和with沙箱保持一致。
+ * 但是iframe是可以做到重新展示时更新浏览器地址的,这里临时不支持,等待后续with沙箱也支持的时候再优化
+ * 只需要去除 if (this.useMemoryRouter) 的判断即可
+ */
if (this.useMemoryRouter) {
// called before lifeCyclesEvent
this.sandBox?.setRouteInfoForKeepAliveApp()
diff --git a/src/sandbox/iframe/index.ts b/src/sandbox/iframe/index.ts
index 6842c9f09..75fe1790a 100644
--- a/src/sandbox/iframe/index.ts
+++ b/src/sandbox/iframe/index.ts
@@ -28,7 +28,6 @@ import {
} from '../../interact'
import {
patchIframeRoute,
- actionsForDisableMemoryRoute,
} from './route'
import {
router,
@@ -160,19 +159,33 @@ export default class IframeSandbox {
}: SandBoxStartParams): void {
if (this.active) return
this.active = true
- if (useMemoryRouter) {
- this.initRouteState(defaultPage)
- // unique listener of popstate event for sub app
- this.removeHistoryListener = addHistoryListener(
- this.microAppWindow.__MICRO_APP_NAME__,
- )
- } else {
- // actions when memory-route disable
- actionsForDisableMemoryRoute(
- this.microAppWindow.__MICRO_APP_NAME__,
- this.microAppWindow,
- baseroute,
- )
+ /**
+ * Sync router info to iframe when exec sandbox.start with disable or enable memory-router
+ * e.g.:
+ * vue-router@4.x get target path by remove the base section from rawLocation.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
+ */
+ /**
+ * 做一些记录:
+ * 1. iframe关闭虚拟路由系统后,default-page无法使用,推荐用户直接使用浏览器地址控制首页渲染
+ * 补充:keep-router-state 也无法配置,因为keep-router-state一定为true。
+ * 2. 导航拦截、current.route 可以正常使用
+ * 3. 可以正常控制子应用跳转,方式还是自上而下(也可以是子应用内部跳转,这种方式更好一点,减小对基座的影响,不会导致vue的循环刷新)
+ * 4. 关闭虚拟路由以后会对应 route-mode='custom' 模式,包括with沙箱也会这么做
+ * 5. 关闭虚拟路由是指尽可能模式没有虚拟路由的情况,子应用直接获取浏览器location和history,控制浏览器跳转
+ */
+ this.initRouteState(defaultPage)
+
+ // unique listener of popstate event for child app
+ this.removeHistoryListener = addHistoryListener(
+ this.microAppWindow.__MICRO_APP_NAME__,
+ )
+
+ if (!useMemoryRouter) {
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute
}
/**
@@ -198,15 +211,16 @@ export default class IframeSandbox {
keepRouteState,
destroy,
clearData,
+ useMemoryRouter,
}: SandBoxStopParams): void {
if (!this.active) return
this.recordAndReleaseEffect({ clearData }, !umdMode || destroy)
- if (this.removeHistoryListener) {
- this.clearRouteState(keepRouteState)
- // release listener of popstate
- this.removeHistoryListener()
- }
+ // if keep-route-state is true or disable memory-router, preserve microLocation state
+ this.clearRouteState(keepRouteState || !useMemoryRouter)
+
+ // release listener of popstate for child app
+ this.removeHistoryListener?.()
if (!umdMode || destroy) {
this.deleteIframeElement()
diff --git a/src/sandbox/iframe/route.ts b/src/sandbox/iframe/route.ts
index 93492be79..db0847464 100644
--- a/src/sandbox/iframe/route.ts
+++ b/src/sandbox/iframe/route.ts
@@ -12,7 +12,6 @@ import {
import {
assign,
} from '../../libs/utils'
-import globalEnv from '../../libs/global_env'
export function patchIframeRoute (
appName: string,
@@ -52,34 +51,3 @@ 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/core.ts b/src/sandbox/router/core.ts
index 85d77fbc9..beb3173b1 100644
--- a/src/sandbox/router/core.ts
+++ b/src/sandbox/router/core.ts
@@ -23,35 +23,48 @@ 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, {
- [appName]: microState
- })
+ if (isMemoryRouterEnabled(appName)) {
+ const rawState = globalEnv.rawWindow.history.state
+ const additionalState: Record = {
+ microAppState: assign({}, rawState?.microAppState, {
+ [appName]: microState
+ })
+ }
+
+ // create new state object
+ return assign({}, rawState, additionalState)
}
- // create new state object
- return assign({}, rawState, additionalState)
+ return microState
}
// delete micro app state form origin state
export function removeMicroState (appName: string, rawState: MicroState): MicroState {
- if (isPlainObject(rawState?.microAppState)) {
- if (!isUndefined(rawState.microAppState[appName])) {
- delete rawState.microAppState[appName]
- }
- if (!Object.keys(rawState.microAppState).length) {
- delete rawState.microAppState
+ if (isMemoryRouterEnabled(appName)) {
+ if (isPlainObject(rawState?.microAppState)) {
+ if (!isUndefined(rawState.microAppState[appName])) {
+ delete rawState.microAppState[appName]
+ }
+ if (!Object.keys(rawState.microAppState).length) {
+ delete rawState.microAppState
+ }
}
+
+ return assign({}, rawState)
}
- return assign({}, rawState)
+ return rawState
}
// get micro app state form origin state
export function getMicroState (appName: string): MicroState {
- return globalEnv.rawWindow.history.state?.microAppState?.[appName] || null
+ const rawState = globalEnv.rawWindow.history.state
+
+ if (isMemoryRouterEnabled(appName)) {
+ return rawState?.microAppState?.[appName] || null
+ }
+
+ return rawState
}
const ENC_AD_RE = /&/g // %M1
@@ -92,58 +105,62 @@ function formatQueryAppName (appName: string) {
*/
export function getMicroPathFromURL (appName: string): string | null {
const rawLocation = globalEnv.rawWindow.location
- const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash)
- const microPath = queryObject.hashQuery?.[formatQueryAppName(appName)] || queryObject.searchQuery?.[formatQueryAppName(appName)]
- return isString(microPath) ? decodeMicroPath(microPath) : null
+ if (isMemoryRouterEnabled(appName)) {
+ const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash)
+ const microPath = queryObject.hashQuery?.[formatQueryAppName(appName)] || queryObject.searchQuery?.[formatQueryAppName(appName)]
+ return isString(microPath) ? decodeMicroPath(microPath) : null
+ }
+ return rawLocation.pathname + rawLocation.search + rawLocation.hash
}
/**
* Attach child app fullPath to browser url
* @param appName app.name
- * @param microLocation location of child app
+ * @param targetLocation location of child app or rawLocation of window
*/
-export function setMicroPathToURL (appName: string, microLocation: MicroLocation): HandleMicroPathResult {
- const targetFullPath = microLocation.pathname + microLocation.search + microLocation.hash
+export function setMicroPathToURL (appName: string, targetLocation: MicroLocation): HandleMicroPathResult {
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.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(targetFullPath)
-
- /**
- * Is parent is hash router
- * In fact, this is not true. It just means that the parameter is added to the hash
- */
- // If hash exists and search does not exist, it is considered as a hash route
- if (hash && !search) {
- isAttach2Hash = true
- if (queryObject.hashQuery) {
- queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath
- } else {
- queryObject.hashQuery = {
- [formatQueryAppName(appName)]: encodedMicroPath
+ if (isMemoryRouterEnabled(appName)) {
+ let { pathname, search, hash } = globalEnv.rawWindow.location
+ const queryObject = getQueryObjectFromURL(search, 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
+ */
+ // If hash exists and search does not exist, it is considered as a hash route
+ if (hash && !search) {
+ isAttach2Hash = true
+ if (queryObject.hashQuery) {
+ queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath
+ } else {
+ queryObject.hashQuery = {
+ [formatQueryAppName(appName)]: encodedMicroPath
+ }
}
- }
- const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?'
- hash = baseHash + stringifyQuery(queryObject.hashQuery)
- } else {
- if (queryObject.searchQuery) {
- queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath
+ const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?'
+ hash = baseHash + stringifyQuery(queryObject.hashQuery)
} else {
- queryObject.searchQuery = {
- [formatQueryAppName(appName)]: encodedMicroPath
+ if (queryObject.searchQuery) {
+ queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath
+ } else {
+ queryObject.searchQuery = {
+ [formatQueryAppName(appName)]: encodedMicroPath
+ }
}
+ search = '?' + stringifyQuery(queryObject.searchQuery)
+ }
+
+ return {
+ fullPath: pathname + search + hash,
+ isAttach2Hash,
}
- search = '?' + stringifyQuery(queryObject.searchQuery)
}
return {
- fullPath: pathname + search + hash,
+ fullPath: targetFullPath,
isAttach2Hash,
}
}
@@ -155,18 +172,20 @@ export function setMicroPathToURL (appName: string, microLocation: MicroLocation
*/
export function removeMicroPathFromURL (appName: string, targetLocation?: MicroLocation): HandleMicroPathResult {
let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location
- const queryObject = getQueryObjectFromURL(search, hash)
-
let isAttach2Hash = false
- if (queryObject.hashQuery?.[formatQueryAppName(appName)]) {
- isAttach2Hash = true
- delete queryObject.hashQuery?.[formatQueryAppName(appName)]
- const hashQueryStr = stringifyQuery(queryObject.hashQuery)
- hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr
- } else if (queryObject.searchQuery?.[formatQueryAppName(appName)]) {
- delete queryObject.searchQuery?.[formatQueryAppName(appName)]
- const searchQueryStr = stringifyQuery(queryObject.searchQuery)
- search = searchQueryStr ? '?' + searchQueryStr : ''
+
+ if (isMemoryRouterEnabled(appName)) {
+ const queryObject = getQueryObjectFromURL(search, hash)
+ if (queryObject.hashQuery?.[formatQueryAppName(appName)]) {
+ isAttach2Hash = true
+ delete queryObject.hashQuery?.[formatQueryAppName(appName)]
+ const hashQueryStr = stringifyQuery(queryObject.hashQuery)
+ hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr
+ } else if (queryObject.searchQuery?.[formatQueryAppName(appName)]) {
+ delete queryObject.searchQuery?.[formatQueryAppName(appName)]
+ const searchQueryStr = stringifyQuery(queryObject.searchQuery)
+ search = searchQueryStr ? '?' + searchQueryStr : ''
+ }
}
return {
diff --git a/src/sandbox/router/history.ts b/src/sandbox/router/history.ts
index 65c668c50..696b3423a 100644
--- a/src/sandbox/router/history.ts
+++ b/src/sandbox/router/history.ts
@@ -141,7 +141,9 @@ export function navigateWithNativeEvent (
const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null
// navigate with native history method
nativeHistoryNavigate(appName, methodName, result.fullPath, state, title)
- if (oldFullPath !== result.fullPath) dispatchNativeEvent(appName, onlyForBrowser, oldHref)
+ if (oldFullPath !== result.fullPath && isMemoryRouterEnabled(appName)) {
+ dispatchNativeEvent(appName, onlyForBrowser, oldHref)
+ }
}
}
diff --git a/src/sandbox/router/index.ts b/src/sandbox/router/index.ts
index 07184bd1d..0ed440d37 100644
--- a/src/sandbox/router/index.ts
+++ b/src/sandbox/router/index.ts
@@ -62,7 +62,9 @@ export function initRouteStateWithURL (
/**
* initialize browser information according to microLocation
- * called on sandbox.start or reshow of keep-alive app
+ * Scenes:
+ * 1. sandbox.start
+ * 2. reshow of keep-alive app
*/
export function updateBrowserURLWithLocation (
appName: string,
diff --git a/src/sandbox/router/location.ts b/src/sandbox/router/location.ts
index 809b313b9..a45c0e22e 100644
--- a/src/sandbox/router/location.ts
+++ b/src/sandbox/router/location.ts
@@ -258,6 +258,7 @@ export function updateMicroLocation (
): void {
// record old values of microLocation to `from`
const from = createGuardLocation(appName, microLocation)
+ // if is iframeSandbox, microLocation muse be rawLocation of iframe, not proxyLocation
const newLocation = createURL(path, microLocation.href)
if (isIframeSandbox(appName)) {
const microAppWindow = appInstanceMap.get(appName)!.sandBox.microAppWindow
diff --git a/src/sandbox/with/index.ts b/src/sandbox/with/index.ts
index be686f39d..38471b575 100644
--- a/src/sandbox/with/index.ts
+++ b/src/sandbox/with/index.ts
@@ -139,6 +139,7 @@ export default class WithSandBox implements WithSandBoxInterface {
}: SandBoxStartParams): void {
if (this.active) return
this.active = true
+ // TODO: with沙箱关闭虚拟路由保持和iframe一致
if (useMemoryRouter) {
if (isUndefined(this.microAppWindow.location)) {
this.setMicroAppRouter(
@@ -190,22 +191,25 @@ export default class WithSandBox implements WithSandBoxInterface {
* @param keepRouteState prevent reset route
* @param destroy completely destroy, delete cache resources
* @param clearData clear data from base app
+ * @param useMemoryRouter use virtual router
*/
public stop ({
umdMode,
keepRouteState,
destroy,
clearData,
+ useMemoryRouter,
}: SandBoxStopParams): void {
if (!this.active) return
this.recordAndReleaseEffect({ umdMode, clearData, destroy }, !umdMode || destroy)
- if (this.removeHistoryListener) {
+ if (useMemoryRouter) {
this.clearRouteState(keepRouteState)
- // release listener of popstate
- this.removeHistoryListener()
}
+ // release listener of popstate for child app
+ this.removeHistoryListener?.()
+
/**
* NOTE:
* 1. injectedKeys and escapeKeys must be placed at the back
diff --git a/typings/global.d.ts b/typings/global.d.ts
index 7922323a5..78806b042 100644
--- a/typings/global.d.ts
+++ b/typings/global.d.ts
@@ -52,6 +52,7 @@ declare module '@micro-app/types' {
keepRouteState: boolean
destroy: boolean
clearData: boolean
+ useMemoryRouter: boolean
}
interface releaseGlobalEffectParams {