概述
路由是通过对应url来渲染页面的一种方式,vue中主要的路由模式有俩种分别为 hash模式(默认的模式),history模式。
路由的分类及原理
hash路由
原理:通过对应的监听hash值变化来渲染不同的内容 (location.hash)
实现:通过onhashchange来监听对应的hash值的变化
window.onhashchange = ()=>{}
history路由
原理:通过监听对应的路径的变化来渲染不同的内容 (location.pathname)
实现:通过监听对应的onpopstate来处理对应的history.pushState和history.replaceState改变的路径
window.onpopstate = (){}
基础入门
导入vue的路由
- cdn:<script src="./lib/vue-router.js"></script>
- webpack构建的脚手架中进行node安装:npm i vue-router -S
构建路由对象几个步骤:
准备路由配置 (配置路由表routes),内有多个route 路由配置对象
let routes = [{
//这个对象就是一个route
name: 'hello ', //当前的路由的名字 唯一区分的
path: '/hello', //当前路由的路径 可以不唯一
component: hello //当前路由渲染的组件
}, {
//这个对象就是一个route
name: 'world ', //当前的路由的名字 唯一区分的
path: '/world', //当前路由的路径 可以不唯一
component: world //当前路由渲染的组件
}]
传入配置路由表(routes)构建路由对象(router)
let router = new VueRouter({
mode: 'hash', //设置对应的路由采用的模式,默认为hash
//routes: routes //这个地方属性名必须叫routes,可以简写为routes
routes
})
将路由对象(router)传给vue实例
new Vue({
el: "#app",
data: {},
// router: router
router
})
路由显示设置
<div id="app">
<!-- 路由链接组件,router-link是内置组件-->
<!-- 主要用于路由切换,它会被解析为a标签,to表示切换的地址 -->
<router-link to="/hello">hello切换</router-link>
<router-link to="/world">world切换</router-link>
<!-- 路由视图组件,也是内置组件 -->
<!-- 根据对应的路由自动渲染对应组件到链接router-link中 -->
<router-view></router-view>
</div>
route路由配置对象
route的属性
- name :指定路由配置的名字(标识,唯一的)
- path :指定路由配置的访问地址(访问路径唯一)
- component :指定路由渲染的组件 components(指定渲染的组件集合)
- redirect :重定向(访问当前的路由路径的时候,转换对应的路径访问)
- children :子路由 (嵌套路由)
- alias :别名
- props :向组件传值
- meta :相关配置(keepAlive:true是否缓存;auth:"user" ,具备什么权限;isAuth:true需要鉴权)
let admin = {
template: `
<div>admin内的页面切换:
<router-view></router-view>
{{name}}{{$route.meta.keepAlive}}
</div>`,
props: ['name']
}
//配置对象
let routes = [{
name: 'admin',
path: '/admin',
component: admin,
alias: 'father', //别名
//向组件传值
props() {
return {
name: '你好'
}
},
//相关配置
meta: {
keepAlive: true, //是否缓存
auth: "user", //具备什么权限
isAuth: true //需要鉴权
},
//嵌套路由配置,子页面的路径不能加/
children: [{
name: 'adminUser',
path: 'user',
component: {
template: '<div>管理用户页</div>'
}
}, {
name: 'adminList',
path: 'list',
component: {
template: '<div>管理列表页</div>'
}
}]
}]
//传入规则构建路由对象
let router = new VueRouter({
mode: 'hash', //设置对应的路由采用的模式,默认为hash
//routes: routes //这个地方属性名必须叫routes,可以简写为routes
routes
})
new Vue({
el: "#app",
data: {},
// router: router
router
})
通过$route来获取配置对象
页面中访问:{{$route.meta.keepAlive}}
方法中访问:this.$route.meta.keepAlive
router-link
router-link路由链接组件,它是一个全局内置组件,用于路由的切换,会被解析为a标签,to表示切换的地址
router-link的相关属性
to属性 :用于跳转对应的路径
tag属性 :显示什么标签(默认显示a标签)
replace属性 :采用替换的形式没有历史页面, push属性:添加形式是默认的,有历史页面
exact 精准:默认的匹配模式为模糊匹配,加上exact为精准匹配
样式名:router-link-active 匹配激活的样式、router-link-exact-active 精确匹配的样式 (路径名必须全部一致)
<style>
a {text-decoration: none;}
.router-link-active {background-color: palevioletred}
.router-link-exact-active {background-color: yellowgreen;}
</style>
<div id="app">
<!-- 基础使用 -->
<router-link to="/user">son</router-link><br>
<!-- 属性绑定使用 -->
<router-link :to="{name:'son'}">属性绑定son</router-link><br>
<router-link :to="{path:'/shop'}">属性绑定daughter</router-link><br>
<!-- tag属性 -->
<router-link to="/user" tag="button">tag属性</router-link><br>
<!-- repalce属性和push属性 -->
<router-link to="/user" replace>replace</router-link><br>
<!-- 精准exact -->
<router-link to="/shop" exact>exact</router-link><br>
<router-view></router-view>
</div>
【补充】
属性指定样式名:
- exact-active-class
- active-class
新增的自定义custom(boolean类型值)
router-view
router-view路由视图组件,它是一个全局内置组件,根据对应的路由自动渲染
router-view中的属性
- name属性:用于指定切换路由下对应的组件进行显示
- route解析进行懒加载
<!-- name指定渲染的组件 -->
<router-view name="com"></router-view>
VueRouter构造
相关传入的option属性:
- routes属性传入对应的路由表(它是一个数组)
- mode属性 指定路由的模式 (字符串)
mode属性的取值:
- hash
- history
- abstract (用于原生app的开发 完全不依赖于浏览器)
【注意事项】
vue的版本和vue-router得协调,vue2用到vue-router为3,vue3用于vue-router版本为4
编程式导航
概述:利用代码进行路由的切换
主要使用的是路由对象的方法
- push 会新增历史页面
- replace 不会新增历史页面
- 它们都支持传入path路由字符串或对象,可以使用path来指定路径名,也可以使用name来指定路由配置名
<div id="app">
<!-- 编程式导航:通过函数方法切换、使用this.$router. -->
<button @click="handlerByPath('/first')">上一个</button>
<button @click="handlerByName('last')">下一个</button>
<!-- 直接切换:使用$router. -->
<button @click="$router.push({path:'/first'})">上一个</button>
<button @click="$router.replace({path:'/last'})">下一个</button>
<button @click="$router.replace({name:'last'})">下一个</button>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script src="./lib/vue-router.js"></script>
<script>
let first = {template: `<div>上一个</div>`}
let last = {template: `<div>下一个</div>`}
let routes = [{
name: 'first',
path: '/first',
component: first
}, {
name: 'last',
path: '/last',
component: last
}]
let router = new VueRouter({routes})
new Vue({
el: "#app",
data: {},
router,
methods: {
handlerByPath(path) {
this.$router.push({ // path: path或
path})},
handlerByName(name) {
this.$router.replace(name)}
}
})
</script>
keepAlive
keepAlive是一个内置组件,它提供了缓存的作用。
keepAlive简单使用
在路由配置表routes中添加: meta: {keepAlive: true }
let routes = [{
name: 'first',
path: '/first',
component: first,
meta: {keepAlive: true }
}, {
name: 'con',
path: '/con',
component: con,
meta: {keepAlive: false }
}, {
name: 'last',
path: '/last',
component: last,
meta: {keepAlive: false }
}]
在路由视图组件使用 keepAlive 将 router-view 包起来
<div id="app">
<router-link to="/first">first</router-link>
<router-link to="/con">con</router-link>
<router-link to="/last">last</router-link>
<!-- 会缓存所有的组件 -->
<keep-alive>
<router-view></router-view>
</keep-alive>
<!-- 缓存指定组件,使用include="组件名",缓存多个使用:include="['组件名1','组件名2']"-->
<keep-alive include="tom">
<router-view></router-view>
</keep-alive>
<!-- 使用meta的keepAlive判断是否缓存 -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<!-- 其他组件不显示 -->
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
keepAlive新增的生命周期钩子函数
- activated 激活状态
- deactivated 未激活
let first = {
template: `<div></div>`,
activated() {console.log('激活了first');},
deactivated() {console.log("不激活first");},
}
keepAlive的属性
- include 需要被缓存的组件
- exclude 不需要被缓存的组件
- 它们支持字符串、传入正则、传入数组
路由传参
它有俩种传递的方式,分为 query 和 params。
query传参
主要是利用对应的 ? 拼接进行传参
<div id="app">
<!-- 多种传参方式 -->
<!-- 普通传参 -->
<router-link to="/user?name=tom&age=15">普通传参</router-link>
<!-- 绑定to -->
<router-link :to="{name:'last',query:{name:'marry',age:11}}">绑定to和name传参</router-link>
<router-link :to="{path:'/shop',query:{name:'tonny',age:11}}">绑定to和path传参</router-link>
<!-- 编程式导航传参 -->
<!-- 编程式导航传参,普通方式 -->
<button @click="$router.push('/user?name=tilavine&age=15')">编程式导航传参1</button>
<!-- 编程式导航传参,path传参 -->
<button @click="$router.push({path:'/shop',query:{name:'selena',age:11}})">编程式导航传参2</button>
<!-- 编程式导航传参,name传参 -->
<button @click="$router.push({name:'last',query:{name:'rose',age:11}})">编程式导航传参3</button>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script src="./lib/vue-router.js"></script>
<script>
let first = {
template: `<div>第一个组件:{{$route.query.name}}{{$route.query.age}}</div>`,
created() {console.log(this.$route.query); }
}
let last = {
template: `<div>第二个组件:{{name}}{{age}}</div>`,
props: {
name: {
default: '默认值',
type: String
},
age: {
default: 0,
type: Number
}
}
}
let routes = [{
name: 'first',
path: '/user',
component: first
}, {
name: 'last',
path: '/shop',
component: last,
props($route) {
return {
name: $route.query.name,
age: Number($route.query.age)
}
}
}]
let router = new VueRouter({
routes
})
new Vue({
el: "#app",
data: {},
router
})
</script>
params传参
利用path路由传参,它的数据是使用 / 后面携带的
【注意】:如果是绑定to且使用params:{参数}的对象形式传递参数传递,那么不能使用path传递,建议使用name
<div id="app">
<!-- 普通传递 -->
<router-link to="/1">普通传递</router-link>
<!-- 绑定to使用name传递 -->
<router-link :to="{name:'first',params:{id:2}}">name传递</router-link>
<!-- 编程式导航传递 -->
<button @click="$router.push('/3')">编程式导航传递1</button>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script src="./lib/vue-router.js"></script>
<script>
let first = {
// template: `<div>{{$route.params.id}}</div>`,
template: `<div>{{id}}</div>`,
props: {
id: {
default: 0,
// type: Number
}
}
}
let routes = [{
name: "first",
path: "/:id",
component: first,
props($route) {
return {id: $route.params.id}
}
}]
let router = new VueRouter({
routes
});
new Vue({
el: "#app",
data: {},
router
})
</script>
query传参和params传参的区别
- query传参是使用?拼接的形式传递,params传参是通过 / 路径的拼接
- query传参可以指定path来传递也可以指定name,params传参必须指定name来传递
- query传参不会产生丢失问题因为它会缓存数据,params不会缓存,所有页面重新编译的时候,会产生数据丢失的问题
- 要解决对应的params传参丢失的问题,要指定对应的path路径,通过:名字来规范对应的接收的数据的key
路由守卫
主要负责监听路由的进入及相关操作,守卫路由的入口。
路由守卫分类
- 全局路由守卫 (Router对象)
- 独享路由守卫 (route配置的)
- 组件路由守卫 (组件的)
全局路由守卫
相关函数:
- beforeEach 进入之前
- beforeResolve 进入成功之前
- afterEach 进入以后
let routes = [{
name: 'first',
path: '/user',
component: first,
meta: {isAuth: true}
}, {
name: 'last',
path: '/shop',
component: last,
meta: {isAuth: false}
}]
let router = new VueRouter({routes})
//to 到达的路由配置对象 from 来的路由配置对象 next下一个(放行 方法)
//进入之前 验证对应的权限
router.beforeEach((to, from, next) => {
//是否需要鉴权
if (to.meta.isAuth) {
//携带token去请求查看是否具备权限
let auto = JSON.parse(localStorage.getItem('token')).auth
if (auto == 'root1') {
//放行
next()
} else {next('/shop')}
} else {next()}
});
//进入成功
router.beforeResolve((to, from, next) => {next()});
//进入之后
router.afterEach((to, from) => {});
独享路由守卫
主要拦截对应的路由配置下的组件
相关函数:
- beforeEnter 进入之前
let routes = [{
//path 路径 name 名字 component 组件
name: 'first',
path: '/user',
component: first,
meta: {isAuth: true},
//进入之前
beforeEnter: (to, from, next) => {
// 是否需要鉴权
if (to.meta.isAuth) {
//携带token去请求查看是否具备权限
let auto = JSON.parse(localStorage.getItem('token')).auth
if (auto == 'root1') {
//放行
next()
} else { next('/shop') }
} else {next() }
}
}, {
name: 'last',
path: '/shop',
component: last,
meta: {isAuth: false}
}]
组件路由守卫
主要拦截对应的组件
生命周期钩子:
- beforeRouteEnter 进入之前
- beforeRouteLeave 离开之前
let first = {
template: `<div>拦截</div>`,
beforeRouteEnter(to, from, next) {
//是否需要鉴权
if (to.meta.isAuth) {
//携带token去请求查看是否具备权限
let auto = JSON.parse(localStorage.getItem('token')).auth
if (auto == 'root') {
//放行
next()
} else {next('/shop')}
} else {next()}
},
//进入之后才会触发
beforeRouteLeave(to, from, next) {
if (confirm('当前是否离开')) {next()}
}
}
【补充】
动态路由
根据params传参来进行相关操作
路由的添加及属性方法
addRoutes 添加路由表 ***
- 添加路由表,传入是数组
- router.addRoutes(routes)
currentRoute 获取当前路由配置对象
- router.currentRoute
go 去对应的路由页面 (和history.go一致)
- router.go() //去任何页面