最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue-router4使用router.replace传入字符串时数据丢失

    正文概述 掘金(白爪)   2021-02-07   1209

    背景

    最近在使用vue-router4的时候有遇到一种写法

    router.replace('/home?id=123') 
    

    这种写法导致了一个问题,就是路由会跳转到 /home,后面的数据就丢失了,但是把replace换成push(router.push('/home?id=123') )就没问题,那这两个api在执行的时候会有些什么不同呢?又是什么导致了这种写法的错误?下面是查看vue-router源码所得到的答案

    分析

    我们直接来到replacepush这两个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},而使用pushresolve接收到的是'/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传入字符串时数据丢失

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元