Skip to content

Commit

Permalink
feat(1.0.0-beta.4): Improved the use of disable memory router in ifra…
Browse files Browse the repository at this point in the history
…me sandbox
  • Loading branch information
bailicangdu committed Apr 27, 2023
1 parent 0e1bba2 commit d700aa9
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 128 deletions.
38 changes: 19 additions & 19 deletions dev/children/vite4/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ let router = null
let history = null
// 将渲染操作放入 mount 函数
window.mount = (data) => {
// console.log(1212121212121212, location.href)
history = createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || import.meta.env.BASE_URL)
router = createRouter({
history,
Expand Down Expand Up @@ -283,7 +282,7 @@ setTimeout(() => {

/* ---------------------- Image --------------------- */
// const newImg = new Image()
// newImg.src = '/micro-app/vite2/src/assets/logo.png'
// newImg.src = '/micro-app/vite4/src/assets/logo.png'
// document.body.appendChild(newImg)
// newImg.setAttribute('width', '50px')

Expand All @@ -296,19 +295,20 @@ setTimeout(() => {
/* ---------------------- location 跳转 --------------------- */
// 依次放开每个注释来,尽可能覆盖所有场景
setTimeout(() => {
const baseRoute = (window.__MICRO_APP_BASE_ROUTE__ || import.meta.env.BASE_URL).replace(/\/$/, '')
// window.microApp.location.href = 'https://www.baidu.com/' // origin不同,直接跳转页面
// window.microApp.location.href = '/micro-app/vite2/page2'
// window.microApp.location.href = 'http://localhost:7001/micro-app/vite2/page2' // path改变,刷新浏览器
// window.microApp.location.href = 'http://localhost:7001/micro-app/vite2/page2#abc' // path不变,hash改变,不刷新浏览器,发送popstate、hashchange事件
// window.microApp.location.href = 'http://localhost:7001/micro-app/vite2/page2/' // hash从有到无,刷新浏览器
// window.microApp.location.href = 'http://localhost:7001/micro-app/vite2'
// window.microApp.location.href = 'http://localhost:7001/micro-app/vite2/' // path相同,刷新浏览器
// window.microApp.location.href = 'http://localhost:7001/micro-app/vite2/?a=1' // search变化,刷新浏览器
// window.microApp.location.href = `${baseRoute}/element-plus`
// window.microApp.location.href = `http://localhost:7002${baseRoute}/element-plus` // path改变,刷新浏览器
// window.microApp.location.href = `http://localhost:7002${baseRoute}/element-plus#abc` // path不变,hash改变,不刷新浏览器,发送popstate、hashchange事件
// window.microApp.location.href = `http://localhost:7002${baseRoute}/element-plus/` // hash从有到无,刷新浏览器
// window.microApp.location.href = `http://localhost:7002${baseRoute}`
// window.microApp.location.href = `http://localhost:7002${baseRoute}/` // path相同,刷新浏览器
// window.microApp.location.href = `http://localhost:7002${baseRoute}?a=1` // search变化,刷新浏览器


// window.microApp.location.pathname = '/micro-app/vite2/page2' // path改变,刷新浏览器
// window.microApp.location.pathname = '/micro-app/vite2/page2#hash1' // 无法直接通过pathname修改hash的值,这里的写法是错误的,而且会导致浏览器刷新,需要完善一下
// window.microApp.location.pathname = '/micro-app/vite2/page2?b=2'
// window.microApp.location.pathname = `${baseRoute}/element-plus` // path改变,刷新浏览器
// window.microApp.location.pathname = `${baseRoute}/element-plus#hash1` // 无法直接通过pathname修改hash的值,这里的写法是错误的,而且会导致浏览器刷新,需要完善一下
// window.microApp.location.pathname = `${baseRoute}/element-plus?b=2`

// window.microApp.location.search = '?c=3' // search改变,刷新浏览器
// window.microApp.location.search = '?c=3' // search不变,刷新浏览器
Expand All @@ -317,13 +317,13 @@ setTimeout(() => {
// window.microApp.location.hash = '#a' // hash不变,不发送popstate、hashchange事件


// window.microApp.location.assign('/micro-app/vite2/page2') // path改变,刷新浏览器
// window.microApp.location.assign('http://localhost:7001/micro-app/vite2/page2') // path不改变,刷新浏览器
// window.microApp.location.assign('http://localhost:7001/micro-app/vite2/page2#abc') // path不变,hash改变,不刷新浏览器,发送popstate、hashchange事件
// window.microApp.location.assign(`${baseRoute}/element-plus`) // path改变,刷新浏览器
// window.microApp.location.assign(`http://localhost:7002${baseRoute}/element-plus`) // path不改变,刷新浏览器
// window.microApp.location.assign(`http://localhost:7002${baseRoute}/element-plus#abc`) // path不变,hash改变,不刷新浏览器,发送popstate、hashchange事件

// window.microApp.location.assign('/micro-app/vite2/page2') // 同上
// window.microApp.location.replace('http://localhost:7001/micro-app/vite2/page2') // 同上
// window.microApp.location.replace('http://localhost:7001/micro-app/vite2/page2#abc') // 同上
// window.microApp.location.replace(`${baseRoute}/element-plus`) // 同上
// window.microApp.location.replace(`http://localhost:7002${baseRoute}/element-plus`) // 同上
// window.microApp.location.replace(`http://localhost:7002${baseRoute}/element-plus#abc`) // 同上

// window.microApp.location.reload()

Expand All @@ -336,7 +336,7 @@ setTimeout(() => {
* 基座和子应用都设置了/sugrec的代理,两者都可以正常拿到数据
* 但是当走子应用的代理时,headers信息只能拿到 content-length 和 content-type(with和iframe都一样)
* 走基座的代理时,可以拿到所有的headers头信息
* 子应用:/sugrec,默认补全为 http://localhost:7001/sugrec
* 子应用:/sugrec,默认补全为 http://localhost:7002/sugrec
* 主应用:http://localhost:3000/sugrec
* 注意:!!
* iframe环境下,会自动使用base补全fetch的地址
Expand Down
8 changes: 4 additions & 4 deletions dev/children/vite4/src/pages/home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import HelloWorld from '../components/HelloWorld.vue'
const testHref = () => {
// 通过原生跳转会导致报错
// location.href = 'http://localhost:7001/micro-app/vite2/#/page2' // 报错
// location.href = '/micro-app/vite2/#/page2' // 报错
// location.href = 'http://localhost:7001/micro-app/vite4/#/page2' // 报错
// location.href = '/micro-app/vite4/#/page2' // 报错
// location.href = 'https://www.baidu.com/' // 报错
// location.href = 'http://localhost:3000/micro-app/vite2/#/page2' // -- 不报错
// location.href = 'http://localhost:3000/micro-app/vite4/#/page2' // -- 不报错
// vite环境下,通过window.microApp.location进行跳转
// window.microApp.location.href = 'https://www.baidu.com/' // origin不同,直接跳转页面
window.microApp.location.href = '/micro-app/vite2/page2'
window.microApp.location.href = window.__MICRO_APP_BASE_ROUTE__ ? window.__MICRO_APP_BASE_ROUTE__ + '/element-plus' : '/micro-app/vite4/element-plus'
}
// parent.console.timeEnd('vite')
Expand Down
14 changes: 7 additions & 7 deletions dev/main-react16/src/global.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,17 @@ microApp.start({
// })
// }, 10000)

window.addEventListener('popstate', (e) => {
window.addEventListener('hashchange', (e) => {
// const a = document.createElement('div')
// a.innerHTML = '55555555'
// a.innerHTML = '666666666'
// document.body.appendChild(a)
console.log('基座 popstate', 'state:', e.state)
// history.replaceState(history.state, '', location.href)
console.log('基座 hashchange', e,)
})

window.addEventListener('hashchange', (e) => {
window.addEventListener('popstate', (e) => {
// const a = document.createElement('div')
// a.innerHTML = '666666666'
// a.innerHTML = '55555555'
// document.body.appendChild(a)
console.log('基座 hashchange', e, e.newURL, e.oldURL)
console.log('基座 popstate', 'state:', e.state)
// history.replaceState(history.state, '', location.href)
})
2 changes: 1 addition & 1 deletion dev/main-react16/src/pages/vite2/vite2.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ 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'
Expand Down
6 changes: 3 additions & 3 deletions dev/main-react16/src/pages/vite4/vite4.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ function vite4 (props) {
}

function jumpToHome () {
microApp.router.push({name: 'vite4', path: '/micro-app/vite4/'})
microApp.router.push({name: 'vite4', path: '/micro-app/demo/vite4/'})
}

function jumpToPage2 () {
microApp.router.push({name: 'vite4', path: '/micro-app/vite4/element-plus'})
microApp.router.push({name: 'vite4', path: '/micro-app/demo/vite4/element-plus'})
}

function jumpToPage3 () {
microApp.router.push({name: 'vite4', path: '/micro-app/vite4/ant-design-vue'})
microApp.router.push({name: 'vite4', path: '/micro-app/demo/vite4/ant-design-vue'})
}

function consoleRouteCurrent () {
Expand Down
1 change: 1 addition & 0 deletions src/sandbox/iframe/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default class IframeSandbox {
* 2. withSandbox location is browser location when disable memory-router, so no need to do anything
*/
/**
* TODO:
* 做一些记录:
* 1. iframe关闭虚拟路由系统后,default-page无法使用,推荐用户直接使用浏览器地址控制首页渲染
* 补充:keep-router-state 也无法配置,因为keep-router-state一定为true。
Expand Down
71 changes: 49 additions & 22 deletions src/sandbox/router/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { getActiveApps } from '../../micro_app'
import globalEnv from '../../libs/global_env'
import { navigateWithNativeEvent, attachRouteToBrowserURL } from './history'
import bindFunctionToRawTarget from '../bind_function'
import { updateMicroLocationWithEvent } from './event'

export interface RouterApi {
router: Router,
Expand Down Expand Up @@ -99,38 +100,64 @@ function createRouterApi (): RouterApi {
return function (to: RouterTarget): void {
const appName = formatAppName(to.name)
if (appName && isString(to.path)) {
const app = appInstanceMap.get(appName)
if (app && !isMemoryRouterEnabled(appName)) {
return logError(`navigation failed, memory router of app ${appName} is closed`)
}
// active apps, include hidden keep-alive app
if (getActiveApps({ excludePreRender: true }).includes(appName)) {
const microLocation = app!.sandBox!.proxyWindow.location as MicroLocation
/**
* active apps, exclude prerender app or hidden keep-alive app
* NOTE:
* 1. prerender app or hidden keep-alive app clear and record popstate event, so we cannot control app jump through the API
* 2. disable memory-router
*/
if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
const app = appInstanceMap.get(appName)!
const microLocation = app.sandBox!.proxyWindow.location as MicroLocation
const targetLocation = createURL(to.path, microLocation.href)
// Only get path data, even if the origin is different from microApp
const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState'
navigateWithRawHistory(appName, methodName, targetLocation, to.state)
/**
* TODO:
* 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
* 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
* 补充:
* 核心思路:减小对基座的影响(就是子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
* 未来的思路有两种:
* 1、减少对基座的影响,主要是解决vue循环刷新的问题
* 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
* 两者选一个吧,如果选2,则下面这两行代码可以去掉
* 要不这样吧,history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
* 如果关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
* 。。。。先这样吧
*/
if (!isMemoryRouterEnabled(appName)) {
updateMicroLocationWithEvent(appName, targetFullPath)
}
}
} else {
/**
* app not exit or unmounted, update browser URL with replaceState
* use base app location.origin as baseURL
*/
const rawLocation = globalEnv.rawWindow.location
const targetLocation = createURL(to.path, rawLocation.origin)
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
if (getMicroPathFromURL(appName) !== targetFullPath) {
navigateWithRawHistory(
appName,
to.replace === false ? 'pushState' : 'replaceState',
targetLocation,
to.state,
)
}
logWarn('navigation failed, app does not exist or is inactive')
}

// /**
// * app not exit or unmounted, update browser URL with replaceState
// * use base app location.origin as baseURL
// * 应用不存在或已卸载,依然使用replaceState来更新浏览器地址 -- 不合理
// */
// /**
// * TODO: 应用还没渲染或已经卸载最好不要支持跳转了,我知道这是因为解决一些特殊场景,但这么做是非常反直觉的
// * 并且在新版本中有多种路由模式,如果应用不存在,我们根本无法知道是哪种模式,那么这里的操作就无意义了。
// */
// const rawLocation = globalEnv.rawWindow.location
// const targetLocation = createURL(to.path, rawLocation.origin)
// const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
// if (getMicroPathFromURL(appName) !== targetFullPath) {
// navigateWithRawHistory(
// appName,
// to.replace === false ? 'pushState' : 'replaceState',
// targetLocation,
// to.state,
// )
// }
} else {
logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`)
}
Expand Down
58 changes: 36 additions & 22 deletions src/sandbox/router/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,7 @@ export function addHistoryListener (appName: string): CallableFunction {
}).includes(appName) &&
!e.onlyForBrowser
) {
const microPath = getMicroPathFromURL(appName)
const app = appInstanceMap.get(appName)!
const proxyWindow = app.sandBox!.proxyWindow
const microAppWindow = app.sandBox!.microAppWindow
let isHashChange = false
// for hashChangeEvent
const oldHref = proxyWindow.location.href
// Do not attach micro state to url when microPath is empty
if (microPath) {
const oldHash = proxyWindow.location.hash
updateMicroLocation(appName, microPath, microAppWindow.location as MicroLocation)
isHashChange = proxyWindow.location.hash !== oldHash
}

// dispatch formatted popStateEvent to child
dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow)

// dispatch formatted hashChangeEvent to child when hash change
if (isHashChange) dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref)

// clear element scope before trigger event of next app
removeDomScope()
updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName))
}
}

Expand All @@ -82,6 +61,41 @@ export function addHistoryListener (appName: string): CallableFunction {
}
}

/**
* Effect: use to trigger child app jump
* Actions:
* 1. update microLocation with target path
* 2. dispatch popStateEvent & hashChangeEvent
* @param appName app name
* @param targetFullPath target path of child app
*/
export function updateMicroLocationWithEvent (
appName: string,
targetFullPath: string | null,
): void {
const app = appInstanceMap.get(appName)!
const proxyWindow = app.sandBox!.proxyWindow
const microAppWindow = app.sandBox!.microAppWindow
let isHashChange = false
// for hashChangeEvent
const oldHref = proxyWindow.location.href
// Do not attach micro state to url when targetFullPath is empty
if (targetFullPath) {
const oldHash = proxyWindow.location.hash
updateMicroLocation(appName, targetFullPath, microAppWindow.location as MicroLocation)
isHashChange = proxyWindow.location.hash !== oldHash
}

// dispatch formatted popStateEvent to child
dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow)

// dispatch formatted hashChangeEvent to child when hash change
if (isHashChange) dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref)

// clear element scope before trigger event of next app
removeDomScope()
}

/**
* dispatch formatted popstate event to microApp
* @param appName app name
Expand Down
1 change: 1 addition & 0 deletions src/sandbox/router/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ 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)
// TODO: 如果所有模式统一发送popstate事件,则isMemoryRouterEnabled(appName)要去掉
if (oldFullPath !== result.fullPath && isMemoryRouterEnabled(appName)) {
dispatchNativeEvent(appName, onlyForBrowser, oldHref)
}
Expand Down
9 changes: 9 additions & 0 deletions src/sandbox/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ export { addHistoryListener } from './event'
export { getNoHashMicroPathFromURL } from './core'
export { patchHistory, releasePatchHistory } from './history'

/**
* TODO: 关于关闭虚拟路由系统的临时笔记
* 1. with沙箱关闭虚拟路由最好和iframe保持一致
* 2. default-page无法使用,但是用基座的地址可以实现一样的效果
* 3. keep-router-state功能失效,因为始终为true
* 4. 基座控制子应用跳转地址改变,正确的值为:baseRoute + 子应用地址,这要在文档中说明,否则很容易出错,确实也很难理解
* 5. 是否需要发送popstate事件,为了减小对基座的影响,现在不发送
* 6. 关闭后导致的vue3路由冲突问题需要在文档中明确指出(2处:在关闭虚拟路由系统的配置那里着重说明,在vue常见问题中说明)
*/
/**
* The router system has two operations: read and write
* Read through location and write through history & location
Expand Down

0 comments on commit d700aa9

Please sign in to comment.