Vue路由动态base方案
# 问题
由于项目技术升级,项目组需要整合 qiankun
,用于对接后续新增的子项目。由于历史原因,作为基座的项目在路由设置了 base
,不希望跳转子项目的时候,这个路由 base
出现,改动已经写好的路由风险又太大,也不希望再创建一个项目作为基座项目。因此希望有一个方案,可以根据特定的条件修改已经设置的路由 base
。
# 思路
方案实际上与框架无关,但因为项目使用的是 vue-router@3.x
,所以主要还是以 vue-router@3.x
为例进行分析。
对于路由操作,最常用的是 push
方法,查看源码[1]可以看到 push
方法实际上是对 router
对象中 history
对象的 push
方法的调用,再看 history
的初始化[2],可以看出它是根据配置属性中的 mode
属性构造不同的类型实例。
history
有三种类型,分别是 HashHistory
、HTML5History
和 AbstractHistory
,他们都是继承自 History
类,实际上我们只需看其中一个类即可。
通过查看代码可以发现,实际上方法内部使用到 base
的方法只有有 push
、replace
和 ensureURL
三个方法,会自动添加 base
的方法也就是只有这三个,理论上通过改写这三个方法,在方法执行前修改 base
的值,可以实现路由的动态 base
。
# 方案
- 使用patch-package (opens new window)对源码进行修改
这个方法是通过对源码进行修改,从而实现自己想要的效果,比较麻烦,所以不推荐。
- 对
router
实例方法进行修改
编写一个中间件,对 router
的实例对象中的方法进行改写,在执行push
、replace
和 ensureURL
三个方法前,调用自定义的方法,对 base
进行改写,改写后再调用原本的方法,从而实现动态 base
。
function createHref(base, fullPath, mode) {
const path = mode === 'hash' ? '#' + fullPath : fullPath
return base ? cleanPath(base + '/' + path) : path
}
function cleanPath(path) {
return path.replace(/\/\//g, '/')
}
export function routerDynamicBasePatch(router, withBase = true) {
const history = router.history
function handleBase(location) {
let _withBase = true
if (typeof withBase === 'function') {
const route = this.router.match(location, this.current)
_withBase = withBase(route)
} else if (typeof withBase === 'boolean') {
_withBase = withBase
}
const base = this.base
return { withBase: _withBase, base }
}
const cbFactory = (ctx) => (base) => (fn) => (data) => {
fn && fn(data)
ctx.base = base
}
history.push = function (location, onComplete, onAbort) {
const { withBase, base } = handleBase.bind(this)(location)
if (!withBase) this.base = ''
const _onComplete = cbFactory(this)(base)(onComplete)
const _onAbort = cbFactory(this)(base)(onAbort)
history.constructor.prototype.push.call(this, location, _onComplete, _onAbort)
}
history.replace = function (location, onComplete, onAbort) {
const { withBase, base } = handleBase.bind(this)(location)
if (!withBase) this.base = ''
const _onComplete = cbFactory(this)(base)(onComplete)
const _onAbort = cbFactory(this)(base)(onAbort)
history.constructor.prototype.replace.call(this, location, _onComplete, _onAbort)
}
history.ensureURL = function ensureURL(push) {
let _withBase = true
if (typeof withBase === 'function') {
_withBase = withBase(this.current)
} else if (typeof withBase === 'boolean') {
_withBase = withBase
}
const base = this.base
if (!_withBase) this.base = ''
history.constructor.prototype.ensureURL.call(this, push)
this.base = base
}
router.resolve = function (to, current, append) {
const { location, route } = router.constructor.prototype.resolve.call(this, to, current, append)
const { withBase } = handleBase.bind(this.history)(location)
const fullPath = route.redirectedFrom || route.fullPath
const base = withBase ? this.history.base : ''
const href = createHref(base, fullPath, this.mode)
return {
location: location,
route: route,
href: href,
// for backwards compat
normalizedTo: location,
resolved: route
}
}
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 , 转载请注明出处!