背景
最近在使用vue-router4的时候有遇到一种写法
router.replace('/home?id=123')
这种写法导致了一个问题,就是路由会跳转到 /home
,后面的数据就丢失了,但是把replace
换成push
(router.push('/home?id=123')
)就没问题,那这两个api在执行的时候会有些什么不同呢?又是什么导致了这种写法的错误?下面是查看vue-router源码所得到的答案
分析
我们直接来到replace
和push
这两个api的入口处
// vue-router@v4.0.0
// src/router.ts
function push(to: RouteLocationRaw | RouteLocation) {
return pushWithRedirect(to)
}
function replace(to: RouteLocationRaw | RouteLocationNormalized) {
return push(assign(locationAsObject(to), { replace: true }))
}
可以看到replace
其实也会调用push
,只是会通过locationAsObject
将我们传入的值转为object
然后还多了一个{replace:true}
function locationAsObject(
to: RouteLocationRaw | RouteLocationNormalized
): Exclude<RouteLocationRaw, string> | RouteLocationNormalized {
return typeof to === 'string' ? { path: to } : assign({}, to)
}
所以在调用push
的时候我们传入的to
就已经变成了
{
path:'/home?id=123',
replace:true
}
这和直接调用router.push('/home?id=123')
,所接收到的to
就已经不同了, 那么问题必然就在这不同之处了,让我们继续往下看,来到pushWithRedirect
函数里面
// src/router.ts
function pushWithRedirect( to: RouteLocationRaw | RouteLocation, redirectedFrom?: RouteLocation ): Promise<NavigationFailure | void | undefined> {
const targetLocation: RouteLocation = (pendingLocation = resolve(to))
...
}
这里会先调用resolve
对我们的传入进行处理,所以我们进入到resolve
// src/router.ts
function resolve( rawLocation: Readonly<RouteLocationRaw>, currentLocation?: RouteLocationNormalizedLoaded ): RouteLocation & { href: string } {
...
if (typeof rawLocation === 'string') {...}
if ('path' in rawLocation) {...}
...
}
从这两个if
可以看到,根据我们传入的to
的类型会走到不同的分支,那么可以推断出router.replace('/home?id=123')
和router.push('/home?id=123')
到了这步会走到两个不同的分支(因为replace会调用locationAsObject
)
当使用replace
的时候resolve
接收到的是{path:'/home?id=123',replace:true}
,而使用push
,resolve
接收到的是'/home?id=123'
我们先来看if ('path' in rawLocation)
这个分支,也就是开头的用法会走到的分支
// src/router.ts resolve
if ('path' in rawLocation) {
...
matcherLocation = assign({}, rawLocation, {
path: parseURL(parseQuery, rawLocation.path, currentLocation.path).path,
})
}
可以看到这一步是创建了一个变量matcherLocation
,然后调用了parseURL
处理我们传入的path
,然后得到返回值后取了path
属性,那么我们就去parseURL
看下,到底做了些什么处理
// src/location.ts
export function parseURL( parseQuery: (search: string) => LocationQuery, location: string, currentLocation: string = '/' ): LocationNormalized {
let path: string | undefined,
query: LocationQuery = {},
searchString = '',
hash = ''
const searchPos = location.indexOf('?')
const hashPos = location.indexOf('#', searchPos > -1 ? searchPos : 0)
if (searchPos > -1) {
// 可以看到在这一步,path属性从'/home?id=123'变成了'/home'
path = location.slice(0, searchPos)
searchString = location.slice(
searchPos + 1,
hashPos > -1 ? hashPos : location.length
)
// searchString -> id=123
query = parseQuery(searchString)
}
...
return {
fullPath: path + (searchString && '?') + searchString + hash,
path,
query,
hash,
}
}
经过parseURL
转换后我们的to
变成了
{
fullPath:'/home?id=123',
path:'/home',
query:{
id:123
},
hash:'',
}
然后我们回到src/router.ts resolve
// src/router.ts resolve
if ('path' in rawLocation) {
...
matcherLocation = assign({}, rawLocation, {
path: parseURL(parseQuery, rawLocation.path, currentLocation.path).path,
})
}
从这一步开始,matcherLocation
里面就只从parseURL
的返回值里面取了path
一个属性,我们的query:{id:123}
,就已经丢失了,后面的代码里面取到的path就已经是/home
了,同理router.push({path:'/home?id=123'})
这种写法也会获取不到id。 自此,这个问题就找到答案了,至于为什么使用router.push(string)
就不会有这个问题,答案在src/router.ts resolve
中另外一个if
分支里面
总结
其实vue-router的文档上已经介绍过这个api的使用方法了
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
所以…
今天的文章vue-router4使用router.replace传入字符串时数据丢失分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17937.html