vue-router 路由
安装及引入
// 安装 npm i vue-router
// 引入 main.js ... import VueRouter from "vue-router" import router from './router'
... Vue.use(VueRouter) ... new Vue({ router, // Vue 实例上创建 render: h => h(App), }).$mount('#app')
配置路由
编写路由配置项
路由配置文件通常创建在 src/router/index.js
// src/router/index.js
// 引入VueRouter import VueRouter from 'vue-router' // 引入路由组件 import About from '../components/About' import Home from '../components/Home' // 创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] }) // 暴露router export default router
实现路由切换
在切换处(如导航栏)配置 router-link 组件, 并通过 router-view 指定展示位置
activate-class 表示激活时添加的class
<router-link active-class="active" to="/about">About</router-link>
<router-view></router-view>
注:1.路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。(可使用 keep-alive 组件缓存路由)
3.整个 Vue 实例只能有一个 router ,可以通过组件的 $router 属性获取到
命名路由
当路由层级过多时,可以使用命名的方式来简化路径配置,如下:
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello', //给路由命名 path:'welcome', component:Hello, } ] } ] }
简化跳转:
// 简化前,需要写完整的路径 <router-link to="/demo/test/welcome">跳转</router-link> // 简化后,直接通过名字跳转 <router-link :to="{name:'hello'}">跳转</router-link> // 简化写法配合传递参数 <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
多级路由
路由中可嵌套路由,实现多级路由配置,通过 children 配置项,配置子路由。
routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, children:[ //通过children配置子级路由 { path:'news', //此处一定不要写:/news component:News }, { path:'message',//此处一定不要写:/message component:Message } ] } ]
路由跳转时,要写完整路径
<router-link to="/home/news">News</router-link>
接收参数
路由切换时,可以向组件内传递参数,以下是几种传参方式
query参数
// 写法一 <router-link :to="/home/message/detail?id=666&title=你好"> // 写法2 <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link>
获取参数:
this.$route.query.id this.$route.query.title
params参数
声明参数:
{ name:'home', path:'/home/:id/:title', //使用占位符声明接收params参数 component:Home, }
配置参数:
// 写法1 <router-link :to="/home/666/你好">跳转</router-link> // 写法2 <router-link :to="{ // 注意,使用此种写法,必须用name配置路由 name:'home', params:{ id:666, title:'你好' } }" >跳转</router-link>
接收参数:
this.$route.params.id this.$route.params.title
props参数
路由还可通过配置项中的 props 属性传值,由组件的 props 接收
分为三种写法:
1.直接传值(不灵活)
{ path:"/home", component: Home, props:{ id: 666, title: '你好'} }
组件中的 props 配置:
props:[ "id", "title" ]
2.配合 params 参数,此时配置项中的 props 为一个 boolean 值,会把接收到的所有的params参数传递给组件中的props。
{ path:"/home", component: Home, props: true }
3.配合 query 参数,通过函数返回值的形式,传入 query 参数,配合组件中的 props 接收
props({query}){ return{ id: query.id, title: query.title, } }
解构的对象参数打印在控制台如下:
replace
<router-link replace .......>News</router-link>
动态路由
有的时候,我们需要把满足某种规则的路由全部匹配到同一个组件,我们不可能为每一个符合规则的路径都进行配置
... { path: '/home/:homeid', name: 'home', component: Home } ...
此时,我们在使用下述路径时均映射为同一个组件
/home/1
/home/2
/home/3
...
动态路由参数变化
当从 /home/1 导航到 /home/2,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
此时,可以使用 beforeRouteUpdate 钩子来进行一些单独的更新操作
beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /home/:homeId,在 /home/1 和 /home/2 之间跳转的时候, // 由于会渲染同样的 Home 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` console.log('beforeRouteUpdate被调用:在当前路由改变,但是该组件被复用时调用')
// to 和 from 分别代表转换前后的 route 对象,可以直接获取 name、path、query 等属性,此时页面还没有渲染,可以做一些事
console.log(to) next() }
编程式路由导航
// push 跳转 this.$router.push({ name:'home', params:{ id:xxx, title:xxx } }) // replace 跳转 this.$router.replace({ name:'about', params:{ id:xxx, title:xxx } })
this.$router.forward() // 前进 this.$router.back() // 后退 // 可前进也可后退,参数为一个number,1为前进1步,-2为后退两步,0为刷新页面 this.$router.go(1)
缓存路由组件
<keep-alive include="home"> <router-view></router-view> </keep-alive>
路由守卫
通常用来对路由进行权限控制
// 路由配置 routes:[ { name:"about", path:"/about", component: About, // 路由元信息,可以存点路由独有的信息 meta:{ // 是否需要路由守卫权限校验,true为需要,false或不写为不需要直接跳转 isAuth:true, } }, { name:"home", path: "/home", component:Home, meta:{ isAuth: true, } } ]
全局守卫
所有路由都要执行
全局前置守卫
切换之前 和 初始化时 被调用
下述代码中,若 a = 2,则进行切换时什么也不会发生
next() 方法不传入参数表示保持默认跳转
next( false ) 表示拒绝跳转,继续保持当前路由
next( 路由路径 ) 表示跳转到指定路由。路由路径既可以是 route 对象,也可使是 配置项中的 path
此处说一下 route 和 router:
我们在路由组件的 this 中既可以拿到 $route,也可以拿到 $router,那么这俩有什么区别呢?
其实 route 就是当前路由对象,里面包括 path,name,params,meta 等等配置项信息,而 router 就是 VueRouter 实例化的全局路由对象
const a = 1 // 全局前置路由守卫 // to:去哪, from: 来自哪, next():放行 router.beforeEach((to, from, next)=>{ console.log("to:"); console.log(to); console.log("from:"); console.log(from); if (to.meta.isAuth){ // 是否需要校验权限 // next()是正确运行路由,此处若条件不符合,则无法执行路由 if( a === 1){ next() } }else { next() } })
如下所示,to 和 from 参数分别表示上一个路由和下一个路由对象
全局后置守卫
切换之后 和 初始化时 被调用,若没有成功通过,则不会调用
// 全局后置路由守卫 // 切换之后和初始化时候被调用 // to:去哪, from: 来自哪 // 没next,因为已经切过去了 router.afterEach((to, from)=>{ // do something })
独享路由守卫
直接在配置项中,为某个路由配置路由守卫(只有前置守卫,没有后置守卫)
// 路由配置 routes:[ { name:"about", path:"/about", component: About, meta:{ isAuth:true, },
// 独享路由守卫 beforeEnter:((to, from, next)=>{ // 逻辑和全局前置一样 }) } ]
组件内路由守卫
配置在组件当中的钩子
//进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) {
// 注意,此时不能使用 this 获取组件实例,因为还没构建完成
next(vm =>{
console.log(vm) // 可以在此处使用组件实例
})
}, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) { }
路由守卫执行顺序
1.首先可以看到,刚进入时,没有进入任何路由,在初始化阶段会执行全局前置守卫和全局后置守卫
2.进入 About 展示页,可以看到,路由守卫执行顺序为:全局前置守卫 => 独享守卫 => 组件前置守卫 => 全局后置守卫
3.再进入 Home 展示页,可以看到,路由守卫执行顺序为:组件后置守卫(from) => 全局前置守卫 => 独享守卫 => 组件前置守卫 => 全局后置守卫
通过上述案例,可以总结出守卫执行流程,从 From 路由 切换为 To 路由时,执行顺序为:
1.From 组件后置守卫
2.全局前置守卫
3.To 独享路由守卫
4.To 组件前置守卫
5. 全局后置守卫
两种路由模式
Vue 中可以选择路由模式,分别为 hashRouter 和 historyRouter。
const router = new VueRouter({ routes, // mode: "hash",默认为 "hash" mode: "history" })
二者最大的不同就是 url 中是否有 #,对于url来说,#后为url的hash(参考URL API),在发送http请求时,不会保留。
hashRouter 将路由路径与 url 用#分隔开,网页刷新时可以保持正常刷新。但因为url中#的存在,在某些校验中可能会导致不合法
historyRouter 较为美观,但刷新网页时需要后端辅助,否则会出现 404
路由懒加载
路由懒加载,即按需加载,就是把路由组建单路打包成1个或多个 js 文件,从整个项目的 js 文件中分离开,在需要的时候才进行加载。
配置如下所示:
// router/index.js import VueRouter from "vue-router"
// 此情况没有指定 webpackChunkName,每个组件会打包成一个 js 文件 const School = () => import("../src/pages/School.vue") const Student = () => import("../src/pages/Student.vue") export default new VueRouter({ routes:[ { path:"/School", component: School, }, { path:"/Student", component: Student, }, ] })
添加 webpackChunkName 可以所有路由组件打包成一个 js 文件
const School = () => import(/* webpackChunkName: 'ImportFuncDemo' */ "../src/pages/School.vue") const Student = () => import(/* webpackChunkName: 'ImportFuncDemo' */ "../src/pages/Student.vue")