关于 Vue Router 下手动执行 history.replaceState 后,跳转其它地址,再返回,路由地址指向不正确的问题解决方案

有这样一个需求:

页面上有很多标签,点击后选中,并异步调取业务接口,要求:

  • 不刷新页面,同时将选中的标签值反应在 URL 的 query 参数里;
  • 当跳转其他页面完成业务操作后,再次返回标签页,选中 URL 上 query 参数对应的标签值*

通常改变 URL 使用 Vue Router 的 router.push()router.replace() 方法:

router.replace({
    query: {
        tag: selectedTag.value
    }
})

但是他们都会立即跳转页面,可能打断用户的操作。JS 原生的 history.replaceState 用于操作 history 记录,它不会立即刷新页面,用它来替换 URL 的 query 再合适不过了:

export function updateQuery(query:{
    [key:string]: string|undefined
}={}){
    const url=new URL(window.location.href)
    // 根据方法参数重置query
    Object.keys(query).forEach(key=>{
        url.searchParams.set(key,query[key]??'')
    })
    window.history.replaceState(null, '', url)
}

在标签上绑定click事件并调用:

updateQuery({
    tag: selectedTag.value,
})

当点击标签时,就会发现浏览器地址栏的 URL 如预期的发生着变化,并且页面没有重新加载。
但是,很快就发现:当切换到下一个页面,再返回(浏览器的返回上一页操作),此时之前给定的参数并未出现再URL的query里,是的,query参数丢了

浏览器调试区出现了警告,告诉我们应该阅读下 Vue Router 的官方说明,显然是哪里漏了关键内容。在文档里,找到了以下描述:

“Vue Router 将信息保存在 history.state 上。”
“我们使用历史状态(history.state)来保存导航信息,如滚动位置,以前的地址等。”

Vue Router 在 history 模式下依赖 state 信息,所以如果使用 JS 原生方法如 history.replaceState()history.pushState() 等,是需要把 history.state 带入的。让我们来看看在依赖 Vue Router history 模式的 history.state 里都有些什么内容:

{
    "back": "/trade-manage/after-sale/detail?id=MKPIE2025081300093",
    "current": "/trade-manage/after-sale?tag=6",
    "forward": "/trade-manage/after-sale/detail?id=MKPIE2025081300093",
    "replaced": true,
    "position": 51,
    "scroll": {
        "left": 0,
        "top": 0
    }
}

从语义上就可以知道,它包含了前进、后退指向地址,甚至是滚动条位置等信息。
通过适当的调整其中的参数,Vue Router 会依照调整后的内容执行相关操作。
如此,遵照官方的要求,我们把 state 置入 history.replaceState() 的第一个参数里,在此之前还需要改变下 history.state.current 的值,它表示当前地址信息,关系到 Vue Router 对路由导航的地址定位,最终得到了以下方法:

export function updateQuery(query:{
    [key:string]: string|undefined
}={}){
    const url=new URL(window.location.href)
    const state=window.history.state
    // 根据方法参数重置query
    Object.keys(query).forEach(key=>{
        url.searchParams.set(key,query[key]??'')
    })
    // 变更current,在跳转前明确当前页面的url,包括query
    state.current=url.href.replace(new RegExp(`^${url.origin}`),'')
    // 将state置入
    window.history.replaceState(state, '', url)
}

由此,问题解决,需求得到了满足。

Refs:history.state 在 Vue Router 里的用法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容