文章目录
一、路由简介
路由(router)实际上是一种对应关系 —— 一组 key——value
的对应关系,value
可以是 函数 或者 组件
多个路由需要经过路由器的管理
前端中路由的作用:实现单页面(SPA)应用,也即实现网页里展示区(组件)根据导航区的来回切换
多页面应用:可在多个html之间跳转,以此显示不同网页的功能
.
单页面应用:只有一个页面,无法实现页面的跳转。而是通过选择页面中响应功能选项,在页面的特定区域显示响应的不同功能页面。整个切换过程中,页面不发生跳转,页面只进行局部更新。一个很形象的例子就是,一个页面内的分类导航 - 展示效果
1、vue router是vue的一个插件库,专门用来实现SPA应用
2、SPA应用:
- single page web application,单页面 Web 应用
- 整个应用只有一个完整的页面
- 点击页面中的导航链接不会刷新整个页面,只会做页面的局部更新
- 数据需要通过 AJAX 请求获取
路由分类
1、前端路由:
- value 是
component 组件
,用于展示页面内容 - 工作流程:当浏览器路径改变时,对应组件显示
2、后端路由:
- value 是客户端发送的请求
- 工作流程:当
路由安装
vue2 中使用vue-router3
npm i vue-router@3
vue3 中使用vue-router4
npm i vue-router@4
实际项目开发中,路由组件一般存放在自己创建的名为
pages
的文件夹中,以便与其他组件区分
二、路由基本使用
安装路由器并设置路由规则
main.js
中引入并应用
import vueRouter from 'vue-router'
Vue.use(vueRouter)
创建一个新文件夹router
,用于打包路由器
router/index.js
//创建整个应用的路由器
import vueRouter from 'vue-router'
//引入该路由器管理的组件
import home from '.../components/home'
import roles from '.../components/roles'
//创建路由器
const router = new VueRouter({
//一个数组,用于存放路由规则
routes:[
//每个规则都在对象中
{
path:'/home',
component:home //组件名,不用字符串写法
},
{
path:'/roles',
component:roles
}
]
})
//暴露该路由器
export default router
main.js
中引入暴露的路由器并添加入 vm
import router from './router'
new Vue({
//...
router:router
})
实现路由切换
使用router
提供的 特殊a标签 <router-link>
,其格式如下
<!--省略部分属性-->
<router-link class="xxx" to="/目标组件或路径">Content</router-link>
<!--例子-->
<router-link class="list-group-item" active-class="active" to="/roles">Roles</router-link>
<!-- active-class的作用是,当该标签被激活时,才将该标签所绑定的active-class样式暂时添加到对应组件上 -->
使用active-class
可以配置高亮样式
被切走的路由组件会被
销毁
指定组件的呈现位置
使用router
提供的特殊标签<router-view>
,换而言之,路由匹配到的组件会被渲染到<router-view>
标签所在的位置
this中的 $router 和 $route
1、$route:每个组件独有的,互不相同,存储每个组件自己的路由信息
2、$router:所有路由组件共享的,整个应用只有一个,可以通过组件的 $router
属性获取
三、命名路由
为路由设置name
配置项,所取的名称为组件的路由名
,其有以下优点
- 简化路由跳转写法(对多级路由比较明显)
- 没有硬编码的 url
parmas
自动编码解码- 防止开发者在 url 中出现打字错误
- 绕过路径排序
const routes = [
{
path: '/home/:newsPage',
name: 'News',
component: user,
}
]
使用<router-link>
标签中的:to
属性传递一个对象,连接到一个命名路由,当然也可以直接用name
代替path
传入,比如::to="News"
,但不好
使用格式为::to="{ name:xxx }"
<router-link :to="{
name: 'News',
params: { newsPage: 'education' }
}">
News
</router-link>
等同于将对象传入router.push()
router.push({name: 'News', params: { newsPage: 'education' }})
这种情况下,路由将会导航到路径/News/education
四、命名视图
五、嵌套(多级)路由
字面意思,路由组件中嵌套路由组件,类似套娃
子路由组件的路由规则以数组形式存放在父组件的children
配置项的数组中,注意子路由组件的path
不加/
此外,还可以通过name
属性为自路由组件
//组件引入省略
export default new VueRouter({
route:[
{
path:'/home',
component:Home,
//在该路由组件中嵌套子路由组件
children:[
{
//子路由组件的 path 不加 /
path:'news',//路径,不能写 '/news'
//命名子路由组件
name:'showNews'
component:News,
},
{
path:'videos',//路径,不能写 '/videos'
components:Video
}
]
}
]
})
匹配子路由组件,<router-link>
的to="xxx"
中,xxx
为/父组件路径/子组件路径
,即需要使用子级路由,并带着父级的路径
<router-link class="list-group" active-class="active" to='/home/news'>新闻</router-link>
六、给路由传参
使用query参数
路由从query
取参数,query参数接收一个对象,其中存储需要传递的参数
方法一:to的字符串写法:
参数存入 vm 的data
,然后在 router-link 标签的 to="xxx"
中使用模板字符串
和${xxx}
,将参数xxx
传入。在路径后:?=key1=${value1}&key2=${value2}
${xxx}
:解析参数xxx
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id>
<router-link :to="`/home/xxx?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Message',
data() {
return {
messageList:[
{id:'001',title:'番茄'},
{id:'002',title:'哈密瓜'},
{id:'003',title:'火龙果'}
]
}
}
}
</script>
方法二:to的对象写法 —— 使用query
格式:to="{}"
传入两个参数——目标路径path
,query
(对象,用于接收给路由传递的参数)
<router-link :to="{
path:'/目标路径',
query:{
id: m.id
title: m.title
}
}">
{{m.title}}
</router-link>
使用params参数
首先在配置路由时必须声明接收params参数
,格式为:path:'路径/:key1/:key2'
,如下
route:[
{
path:'home/:id/:title',
component:Home
}
]
以下为参数传递
写法一:to的字符串写法
to的字符串写法中,在路径后接/${xxx}/${yyy}
传入参数
<router-link :to="`/home/xxx/${m.id}/${m.title}`">{{m.title}}</router-link>
写法二:to的对象写法
使用params
时,不能传入 path 配置项,必须使用name
<router-link :to="{
name:'/目标路径',
params:{
id: m.id
title: m.title
}
}">
{{m.title}}
</router-link>
接收参数:
$route.params.keyName
props配置项——让路由组件更加方便地接收到参数
作用:解决路由里需要传入众多 key-value 的问题
第一种写法:值为对象
传递props
routes:[
{
path:'/news',
component:Home,
//第一种写法,值为对象,该对象中所有 key-value 都会以props形式传给该props所属组件
props:{
key1: value1,
id: 001
}
}
]
Home.vue
:接收
<script>
export default {
//...
props: ['key1', 'id']
}
</script>
第二种写法:值为布尔值
- 若布尔值为真,则会把该路由组件收到的
所有params参数
,。一props形式传给对应组件
props:true
第三种写法:值为函数
routes:[
{
path: '/home',
component: Home,
//写法一
props($route) {
return {
id: $route.query.id,
title: $route.query.title
}
}
//写法二,传参时解构赋值
props({query}) {
id: query.id,
title: query.title
}
//写法三:传参时使用解构赋值的连续写法
props( {query: {id, title}} ) {
return {id, title}
}
}
]
七、编程式导航 - 可操作历史记录
导航到不同位置
router.push(...)
方法:用于导航到不同的 URL,这种方法回向 history栈 添加一个新记录,所以当用户点击浏览器后退按钮时,会回到之前的 URL,即实现 后退 功能
点击<router-link>
时,后台会调用router.push()
方法,所以点击前者就相当于在后台调用后者
声明式 | 编程式 |
---|---|
<router-link :to="xxx"> | router.push(xxx) |
:to="xxx"
:在<router-link>
中的to
属性前面添加:
,其意义是将to
后引号中的内容作为js
解析
router.push()
接收一个 字符串路径,或是一个描述地址的对象作为参数
//字符串路径
router.push('/xxx/name')
//带有路径的对象
router.push({ path:'/xxx/name' })
//命名路由,并让路由建立 url
router.push({ name:'xxx', params:{ username: 'name' } })
//带参数查询, 结果是 /xxx?plan=yyy
router.push({ path:'/xxx', query:{ plan: 'yyy' } })
//带 hash, 结果是 /xxx#yyy
router.push({ path: '/xxx', hash: '#yyy' })
如果提供了path
,那么params
会被忽略,二者不可以一起使用
router.push
和其他导航方法都会返回一个promise
替换当前位置
声明式 | 编程式 |
---|---|
<router-link :to="xxx" replace> | router.replace(...) |
作用类似于router.push()
,但他的导航是取代当前条目,不会在history中添加新纪录,故而无法进行回退
这也可以通过给router.push()
的对象参数中设置replace: true
实现
router.replace({ path: '/home' })
//等效于
router.push({ path: '/home', replace: true })
横跨历史
router.go()
类似于页面的前进或后退多少步,该方法采用一个整数作为参数,表示页面在历史堆栈中前进或后退的步数,正数前进,负数后退
//向前前进一条记录,同 router.forward()
router.go(1)
//返回,或者说后退一条记录,或者说切换到上一个记录,同 router.back()
router.go(-1)
//若超出记录数量,则静默失败
router.go(1000)
八、缓存路由组件
<keep-alive>
:包裹需要展示的<router-view>
,相关组件将缓存下来,切换路由组件时路由组件不会被销毁(Destroy)
可以使用其include
属性指定对其中的某些组件缓存,没有被纳入该属性的路由组件在不展示时会被销毁。include
中接收的名字是组件名
component.vue
<router-link to="/home/news">News</router-link>
<router-link to="/home/books">Books</router-link>
<keep-alive include="news">
<router-view></router-view>
</keep-alive>
如果想要缓存多个路由组件,则需要传入数组,格式为::include="['组件1', '组件2']"
<keep-alive :include="['news','books']">
<router-view></router-view>
</keep-alive>
九、两个新生命周期钩子(路由组件独有) —— activated 和 deactiveated
activated() 激活
路由组件被激活时触发
deactivated() 失活
路由组件失活时触发
十、路由守卫 —— 用户权限相关
如果路由需要设置权限,则可以在需要的路由规则中设置配置项isAuth:true
routes:[
{
path:'/home',
component:Home,
meta:{
isAuth: true,
title: '首页'
}
}
]
全局路由守卫
全局前置路由守卫:
router.beforeEach((to,from,next) => {})
:设置全局前置路由守卫,在初始化时、每一次路由切换前被调用
接收三个参数:to-目标路由
、from-当前路由
、next
router/index.js
router.beforeEach((to,from,next) => {
if(to.meta.isAuth){ //判断是否需要进行权限控制
if(localStorage.getItem('school') === 'SC1'){ //权限控制的具体规则
//此处省略,进行相应处理
next(); //权限通过,放行,切换到目标路由组件
}else{
//权限未通过
alert('数据核对未通过,无权限查看!')
}
}else{
next()
}
})
全局后置路由守卫:
router.afterEach((to,from) => {})
:初始化时、每次路由切换后执行。因为已经是切换之后,所以不需要传入 next
可以将对目标路由组件的操作放在这里,只要切换成功,则必定触发全局后置路由守卫的内容。
router/index.js
router.afterEach((to,from) => {
if(to.meta.title){ //如果需要修改网页的title
document.title = to.meta.title //获取网页title并修改
}
})
独享路由守卫
beforeEnter(to,from,next){}
:只对某个路由组件自己单独起作用,不影响其他组件
beforeEnter(to,from,next){
if(to.meta.isAuth){
if(localStorage.getItem('school') === 'SC2'){
next()
}else{
alert('您无权查看此内容')
}
}else{
next()
}
}
组件内路由守卫
在 组件vc内 写路由守卫,而不是在路由器配置文件中写。必须通过路由规则进行路由切换才会触发 组件内路由守卫
1、beforeRouterEnter(to,from,next){}
:通过路由规则,进入组件时被调用
src/pages/test.vue
//通过路由规则,进入该组件时被调用
beforeRouterEnter(to,from,next){
if(ti.meta.isAuth){
if(localStorage.getItem('xxx') === 'yyy'){
// ...
next()
}else{
alert('无权查看')
}
}
}
2、beforeRouterLeave(to,fom,next){}
:通过路由规则,离开该组件时被调用
beforeRouterLeave(to,from,next){
//...
next()
}
十一、路由器工作模式 —— hash模式 与 history模式
在路由器文件中可以通过配置项mode
设置工作模式,有 hash模式 和 history模式 两种选择
src/router/index.js
//引入组件等省略
const router = new VueRouter({
mode: 'hash', // 'history'
routes:[
//...
]
})
hash模式:
1、hash值:在一个 url 中,#
以及它后面的内容就是 hash值
2、hash值 不会被包含到HTTP请求中,它不会被传给服务器
3、特点:
- 兼容性好
- 如果将地址通过第三方手机app分享,但app校验严格,那么地址可能会被标记不合法
- 地址中带着标志性的
#
history模式:
- 兼容性较 hash模式 略差
- 地址无
#
- 应用部署上线时需要后端人员支持。解决刷新页面时服务端404的问题