Vue.js 路由

1.前言

  • Vue Router官网
  • 在此之前需要了解单页应用single page web application,SPA)和多页面(Multiple page application,MPA)的差别
对比项目 多页面MPA 单页应用SPA
应用构成 多个完整页面构成 一个外壳页面和多个页面片段构成
加载方式 加载一个完整的HTML页面 模拟加载新页面,其实内部是div切换
页面间传递数据 传递不方便,可通过本地存储,或者url传参实现 支持全局状态共享,支持事件机制
  • 单页应用通俗的说就是将子页面打包成一个个div,切换页面时,只是切换这些div而言,当前网页并没有重载,所以子页面间的切换更流畅,因为是共用一个页面框架,方便共享其内存数据,便于数据交互
  • 如果子页面少,可以通过v-show或者v-if指令自行实现加载相关的子页面,但是如果子页面众多,手写v-if的逻辑将不负重堪,这时候就需要用到路由
  • 路由就是通过监听的url的改变事件,来切换展示对应的内容(内容一般会封装成组件),在此之间,需要建立一个url和组件之间的关系表

2.路由注册

  • 这里以vue2.x作为演示(vue2.x和vue3.x对应的vue-router版本不一样,需要注意)
  • 引入vue和vue-router.js,引入组件
  • 创建路由实例,传入配置项(path字段的值为url的hash,component字段值为子组件,redirect字段用来重定向)
  • 实例化Vue实例,并配置router字段
  • vue根据当前网页的hash值,将对应的组件渲染到router-view标签中
  • 考虑到用户会直接访问应用主页,此时hash为/,应将其重定向到home路由
  • 用户输入的hash可能是无效的,可以使用通配符进行匹配(写到最后面)
<body>
    <div id="app" v-cloak>
        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>
</body>
</html>
<script src="./libs/vue/2.6.14/vue.min.js"></script>
<script src="./libs/httpVueLoader/httpVueLoader.js"></script>
<script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"></script>
<script>
    //创建组件
    const Home = { template: '<div>Home</div>' }
    const Foo = { template: '<div>foo</div>' }
    const Bar = { template: '<div>bar</div>' }
    const Not_found = { template: '<div>Not found</div>' }
    //创建 router 实例,然后传 `routes` 配置
    const router = new VueRouter({
        routes:[
            { path: '/',  redirect: '/home' },
            { path: '/home', name:"home", component: Home },
            { path: '/foo', name:"foo", component: Foo },
            { path: '/bar', name:"bar", component: Bar },
            { path: '*',  component: Not_found }
        ]
    })
    //创建vue实例
    new Vue({
        el:"#app",
        router: router
    })
</script>

3.路由切换

  • 声明式路由:通过点击router-link标签生成的元素进行路由跳转,通过to属性控制目标路由的地址,值可是字符串(path路径),也可以用对象形式指定路由的name
<body>
    <div id="app" v-cloak>
        <!-- 字符串语法 绝对路径 -->
        <router-link to="/foo">foo</router-link>
        <!-- 字符串语法 相对路径 -->
        <router-link to="bar">bar</router-link>

        <!-- 对象语法 路由需要指定name to前面记得加: -->
        <router-link :to="{'name':'foo'}">foo</router-link>
        <router-link :to="{'name':'bar'}">bar</router-link>


        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>
</body>
  • 编程式路由:通过$router这个对象调用不同的方法来进行路由切换
方法名 参数 说明
push() path字符串或者对象 跳转到新路由,并创建一条新的历史记录
replace() path字符串或者对象 跳转到新路由,并创建一条新的历史记录
back() 后退一步
go() 数字,前进的步数,支持负数 前进
//通过路径跳转
this.$router.push('/chatList')
//对象语法
this.$router.push({path:"/chatList"})
//通过name跳转
this.$router.push({name:"mailList"})
//后退一步,等同于 this.$router.back()
this.$router.go(-1)
//前进3步
this.$router.go(3)
  • 每次路由切换都会改变浏览器地址栏的url,却不会重载页面,而且还能监听其变化,其底层原理为:
//向历史记录栈中添加一条记录 路由跳转
window.history.pushState(data, title, url)
//修改历史记录栈的状态 路由替换
window.history.replaceState(data, title, url)

4.路由传参

  • 路由传参分为query传参和params传参,前者就是在hash后面通过?key=val的形式挂载,而params是定义一个占位符将一部分路径截取为相应的参数
  • query传参:支持手动拼接和自动拼接
//传入字符串:手动拼接
this.$router.push("/chatList?id=666")
//传入对象:自动拼接 等价于"/chatList?id=666&age=18"
this.$router.push({name:"/mailList",query:{id:666,age:18}})
  • params传参:在设置路由path字段时设置占位符,占位符的数据会被当作传参处理
{
    path:"/chatList/:id",
    component:chatList,
    name:"chatList"
},
{
    path:"/mailList/:id/:age",//多个params参数
    component:mailList,
    name:"mailList"
},
//传入字符串:手动拼接
this.$router.push('/chatList/777')
//传入字符串:手动拼接多个参数
this.$router.push('/mailList/888/20')

//传入对象:自动拼接 等价于 /mailList/888/20
this.$router.push({name:"chatList",params:{id:777}})
this.$router.push({name:"mailList",params:{id:888,age:20}})
  • 通过query方式传递的参数封装在$route.query中,而通过params方式传递的参数则封装在$route.params中。
<p>{{$route.query.id}}</p>
<p>{{$route.params.id}}</p>
  • !注意,query传参是拼接在hash中的,仍然属于hash,而不是search,这与传统的query参数是不同的
//vue路由的参数拼接在hash后面
http://localhost:8080/#/find?id=666

http://localhost:8080/#/mailList/888/20

5.当前路由的信息

  • $route包含了当前路由的信息,可以此获取当前路由的名称,路径,还有外界的传参,常用的属性如下:
属性 说明
path 当前路由路径
name 当前路由名称
query query传参
params params传参
meta 元数据
  • meta属性可以为该路由保存一些自定义的数据,例如:
{
    path:"/mailList",
    component:mailList, 
    name:"mailList",
    meta:{
        need_login:true
    }
}

6.嵌套路由

  • 路由支持多层嵌套,子组件内部还可以再定义子路由,需要2个步骤
  • 在当前路由下面添加children属性(值为数组),每个数组元素对应一个子路由
  • 在当前组件HTML中下设定 router-view 标签,用来展示其子路由对应的内容
routes: [
{
    path: "/aboutMe",
    component: aboutMe,
    children: [{
        path: '/aboutMe/child1',
        //绝对路由
        component: child1
    },
    {
        path: 'child2',
        //相对路由,匹配/aboutMe/child2
        component: child2
    },
    {
        path: "/aboutMe",
        redirect: "/aboutMe/child1"
    }]
}
],

7.缓存路由组件

  • 使用方法:在需要缓存路由组件的外层包裹一个keep-alive标签即可
<keep-alive>
    <router-view></router-view>
</keep-alive>
  • 在与后台做交互时,经常在created或者mounted中请求数据,这就意味着每次切换路由都会触发ajax并且重新渲染,在一些数据实时性要求不高的应用中显然不合适,这时候就需要缓存路由组件。
  • 路由组件被缓存之后,除了初次渲染会触发生命周期函数,其余时候切换路由不会再触发他们,而是会触发activated和deactivated钩子函数。且组件的数据也能被保存下来。

8.路由守卫

  • 定义:在路由发生变化时进行一些特殊的处理而定义的函数,他有3个参数:
参数 说明
to 即将进入的路由对象,参照$route
from 即将离开的路由对象,参照$route
next 此方法必须调用,根据传入的参数决定此次路由跳转的走向
  • next()的参数说
参数 说明
正常跳转路由
false 中断当前路由的跳转
{ path: '/login'} 路由拦截成功,重定向至某个路由,会再次触发此路由守卫
  • 代码
//在路由模块中定义,在路由进行跳转前会执行该代码
var router = new Router()
router.beforeEach((to,from,next) =>{ 
    //判断即将进入的路由是否需要登陆状态
    if(to.meta.needLogin === false){
        next() //不需要登陆状态的页面直接跳转路由
    }else if(window.localStorage.getItem('token') === null){
        //需要登陆的页面先判断有无token 没有则跳转到登录页
        next({name:'Login'})
    }else{
        next() //有token直接跳转
    }
})

在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:

1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码

2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token

3、前端拿到token,将token存储到localStorage或vuex中,并跳转到用户页面或者Home页面

4、前端每次跳转路由,利用路由守卫来判断此路由是否需要登陆状态,不需要则直接跳转。如果需要,则查看 localStroage 中有无 token ,没有就跳转到登录页面,有则正常跳转

5、每次调后端接口,都要在请求头中加token(在请求拦截器或者默认配置中添加)

6、后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401

7、如果前端拿到状态码为401,就清除token信息并跳转到登录页面

//main.js

import axios from 'axios'
//创建实例
var instance = axios.create({
  baseURL: 'http://xxx.com/api'
})

instance.interceptors.response.use(
  response => {
    //拦截响应,做统一处理 
    return response
  },
  //接口错误状态处理,也就是说无响应时的处理
  error => {
    console.log(error.response.status)
    if(error.response.status == 401) {
      //console.log('登录失效,请重新登录')
      //清除token
      window.localStorage.removeItem('token')
      //清除其他信息
      window.localStorage.removeItem('otherInfo')
      //跳转到登录页
      window.location.replace('/')
    }
    return Promise.reject(error.response.status) // 返回接口返回的错误信息
  })
//将实例挂在到Vue中
Vue.prototype.$ajax = instance
posted @ 2019-10-21 17:43  ---空白---  阅读(301)  评论(0编辑  收藏  举报