Vue-router的原理及push和replace的用法

12,558 阅读5分钟

原理

前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求

有两种方式:
1. 在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航
2. 使用H5的window.history功能,使用URL的Hash来模拟一个完整的URL。

两种模式比较

一般的需求场景中,hash模式与history模式是差不多的,根据MDN的介绍,调用history.pushState()相比于直接修改hash主要有以下优势:

pushState设置的新url可以是与当前url同源的任意url,而hash只可修改#后面的部分,故只可设置与当前同文档的url

pushState设置的新url可以与当前url一模一样,这样也会把记录添加到栈中,而hash设置的新值必须与原来不一样才会触发记录添加到栈中

pushState通过stateObject可以添加任意类型的数据记录中,而hash只可添加短字符串

pushState可额外设置title属性供后续使用

history模式的问题

对于单页应用来说,理想的使用场景是仅在进入应用时加载index.html,后续在的网络操作通过ajax完成,不会根据url重新请求页面,但是如果用户直接在地址栏中输入并回车,浏览器重启重新加载等特殊情况。

hash模式仅改变hash部分的内容,而hash部分是不会包含在http请求中的(hash带#):

oursite.com/#/user/id //如请求,只会发送http://oursite.com/ 所以hash模式下遇到根据url请求页面不会有问题

而history模式则将url修改的就和正常请求后端的url一样(history不带#)

oursite.com/user/id 如果这种向后端发送请求的话,后端没有配置对应/user/id的get路由处理,会返回404错误。

官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果URL匹配不到任何静态资源,则应该返回同一个index.html页面,这个页面就是你app依赖的页面。同时这么做以后,服务器就不再返回404错误页面,因为对于所有路径都会返回index.html文件。为了避免这种情况,在Vue应用里面覆盖所有的路由情况,然后在给出一个404页面。或者,如果是用Node.js作后台,可以使用服务端的路由来匹配URL,当没有匹配到路由的时候返回404,从而实现fallback

Vue-Router跳转

在vue项目中,往往会遇到这样的情况,就是要实现在一文章列表中,点击其中一条跳转到下个页面,然后将这一条的相关数据带到下个页面中显示,无论点哪一条都是跳到相同的页面结构(下一个页面的页面使用的组件是一样的),只是填的数据不一样,这个时候就需要实现跳转的时候一起把参数携带过去。

单个参数传递

比如下面的例子

<template>
  <div class="template">
    <!--点击文章列表跳转到相应的文章页面-->
    <ul>
      <li v-for="(article_task,index) in tasks">
      <!--使用v-bind动态绑定id传递给目标路径-->
      
      <!--注意: 这里携带了了article_task的一个参数id-->
        <router-link tag="a" :to="{path:'/articleTask',query:{id:article_task.id}}">
          <div class="article-render">
            <span>{{index+1}}.</span>
            <span>{{article_task.title}}</span>
            <span>
              {{article_task.content}}%
            </span>
          </div>
        </router-link>
        
      </li>
    </ul>
  </div>
</template>

但是:上面的的router-link传递一个一个参数id,当我们需要传递多个参数的时候应该怎么办呢,很简单,只需要将query的参数用逗号隔开就好了

多个参数传递

<router-link tag="a" 
    :to="{
        path:'/articleTask',
        query:{id:article_task.id,title:article_task.title,content:article_task.content}
    }">
</router-link>

但是问题,又来了。。。。。代码这样写贼鸡儿多,万一写错一个那我岂不是文章不现实了,所以还有一个更简单的办法

优化多个参数传递

就是将要传的参数以一个object传递,在跳转的页面中created中,接收这个object

<router-link tag="a" :to="{path:'/articleTask',query{arry:article_task}}"></router-link>

传递参数可以,怎么接收参数呢?在目标组件里接收参数,只需要在created()钩子中接收即可,实现如下:

<script>
  export default {
    data() {
      return {
      }
    },
    created() {
    //单个参数传递的时候
      this.id = this.$route.query.id;//获取上个页面传递的id,在下面获取数据的时候先提交id
    //这边接收上个组件传递过来的arry数组,赋值给data中定义的articleTask
      this.articleTask = this.$route.query.arry;
    },
  }
</script>
<router-link tag="a" :to="{path:'/articleTask',query{id:article_task.id}}">

vue 跳转的两种写法

声明式的写法

上面的写法是声明式的写法,直接写在组件里面template里面

表示目标路由的链接。当被点击后,内部会立刻把to的值传到router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。

router.push(location)想要导航到不同的URL,则使用router.push方法。这个方法会向history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的URL

编程式的写法

编程式的写法router.push(location) 点击一个标签触发一个事件,在methods中对应的事件中写入this.$router.push(.....)

const router = new VueRouter({
  routes: [
    // 下面的对象就是路由记录
    { path: '/foo', component: Foo,
      children: [
        // 这也是个路由记录
        { path: 'bar', component: Bar }
      ]
    }
  ]
})

编程式写法的几种方式

字符串

this.$router.push('login')

对象

this.$router.push({path: '/login?url=' + this.$route.path});

命名的路由

this.$router.push({ name: 'login', params: { userId: 123 }})

带查询参数,变成/foo/bar?selected=1

this.$router.push({path: '/foo/bar', query: {selected: "1"}});

params传参和query传参有什么区别

query要用path来引入,params要用name来引入,而params传参配置的是name,在params中配置path无效接收参数都是类似的,分别是this.$route.query.namethis.$route.params.name

query更加类似于我们ajaxget传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示

query在路由配置不需要设置参数,而params必须设置

query传递的参数会显示在地址栏中

params传参刷新会无效,但是query会保存传递过来的值,刷新不变 ;

this.$router.replace()

用法参考this.$router.push()

设置replace属性(默认值:false)的话,当点击时,会调用 router.replace()而不是 router.push(),于是导航后不会留下history 记录。即使点击返回按钮也不会回到这个页面。

加上replace: true后,它不会向history添加新记录,而是跟它的方法名一样 —— 替换掉当前的history记录。

本文使用 mdnice 排版