Vue-Router详解【学习Vue-Router看这一篇就够了!!!】
目录
前言
安装
Vue-Router简介
Vue-Router介绍
Vue-Router的简单使用
注意
标签
带参数的动态路由
什么是带参数的动态路由
路由参数匹配
注意
路由参数匹配的语法规则
加号“+“的使用:
乘号“*”的使用
问号“?”的使用
路由的嵌套
路由跳转
使用路由方法
路由命名
拼接参数实现路由跳转
push方法返回Promise对象
路由历史控制
页面不进入history栈的跳转
使用history灵活跳转到某个位置
路由的命名
使用名称进行路由切换
路由视图命名
默认名字default
使用别名
路由重定向
关于路由传参
同级多个子路由
固定的props值
props是一个函数
动态路由
动态添加路由
动态删除路由
获取现有路由
前言
本文默认您已了解或掌握Vue3的基本用法
Vue(3.0版本以上均可) + Vite + Vue-Router(最新版本即可)
参考资料:
- Vue官方中文文档:Vue.js - 渐进式 JavaScript 框架 | Vue.js
- Vue-Router中文官方文档:入门 | Vue Router
更多Vue系列文章,请关注博主【Vue全家桶】专栏:Vue全家桶-专栏
安装
推荐使用“npm”安装
npm install vue-router@4
官网安装速度较慢,可以使用“cnpm”替换“npm”
npm install vue-router@4
Vue-Router简介
Vue-Router介绍
学习Vue-Router之前,我们有必要了解,Vue-Router是干什么的???
Vue-Router是一个路由管理插件,它可以解析浏览器中的“URL”并跳转到不同界面,Vue-Router为Vue的单页面提供了更大的可能性!!
Vue-Router的简单使用
Vue-Router使用的简单概括为:
“创建一个路由,放置<router-view>路由页面出口,配置路由,最后改变URL即可实现切换页面!”
下面是一个简单的例子,帮我们了解Vue-Router的具体实现逻辑
- 首先创建名为“Demo1.vue”和“Demo2.vue”两个vue文件
- 在两个文件中编写如下代码:
Demo1.vue:
<script setup>
</script><template><h1>这里是Demo1页面</h1>
</template>
Demo2.vue:
<script setup>
</script><template><h1>这里是Demo2页面</h1>
</template>
- 修改App.vue(你的根组件文件)文件,代码如下:
App.vue:
<script setup>
</script>
<template><h1>这里是主页面</h1><!-- router-link是路由的入口,用来切换不同到的路由页面 --><p><router-link to="/demo1">前往页面1</router-link></p><p><router-link to="/demo2">前往页面2</router-link></p><!-- router-view是理由页面的出口 用来渲染路由页面 --><router-view></router-view>
</template>
- 修改项目的main.js文件:
main.js:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import Demo1 from './components/Demo1.vue'
import Demo2 from './components/Demo2.vue'//创建路由配置
const routerConfig = [{path:'/demo1',component:Demo1},{path:'/demo2',component:Demo2}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
至此,我们已经配置好所有的设置,打开页面后,页面已经可以正常显示,并且可以正常跳转:
注意
通过上面一个简单的小例子,相信您对Vue-Router的实现页面跳转已经有了大体的了解,下面我们来概括一下Vue-Router的具体实现方式:
- 配置根组件页面,在该组件页面中放置<router-view>路由出口,用来容纳不同页面渲染的具体位置(其实根本来说,Vue-Router实现的是将一个页面中的部分/全部区域进行页面跳转)
- 配置页面组件
- 通过切换不同的URL来实现页面跳转(切换不同的URL有三种方式,后面会提到)
<router-link>标签
<router-link>标签类似于传统HTML中的<a>标签,只不过<router-link>标签中通过“to”来指定目标URL
to的值可以是:“一个合法的URL路径字符串”和“一个合法的URL路径字符串变量(:to绑定)”
<script setup>const path = 'demo1'
</script>
<template><p><router-link to="/demo1">前往页面1</router-link></p><!-- 等同于上面的to='/demo1' --><p><router-link :to=path>前往页面1</router-link></p>
</template>
带参数的动态路由
什么是带参数的动态路由
试想一个场景,例如某个网站中的“用户中心”页面,不同用户所展示的信息固然是不同的,要想不同的用户渲染出各自的数据,我们可以通过“为路由添加参数”实现
一个利用参数URL渲染不同用户信息的例子:
一个B站UP主的个人主页URL:
- 这里的293846287就是B站用户UUID的参数,通过这个参数来渲染不同的用户主页
路由参数匹配
我们通过编写一个简单的用户中心例子,来讲解路由参数
- 我们创建一个User.vue文件,编写代码如下:
User.vue:
<script setup>
</script>
<template><h1>姓名:{{ $route.params.username }}</h1><h1>ID:{{ $route.params.id }}</h1>
</template>
- 配置“main.js”中路由:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import User from './components/User.vue' //创建路由配置
const routerConfig = [{path:'/user/:username/:id',component:User}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 最后手动输入URL
效果:
注意
我们的URL路由配置:“/user/:username/:id”,各参数作用如下:
- 冒号(':'):放在字符串前面,表示两个“/”之间的参数是一个路由变量,用户实际填写的URL参数会存储到该路由变量中
在<template>标签中通过“$route”全局变量下的params属性来使用具体路由变量
路由参数匹配的语法规则
Vue-Router允许参数内部使用【正则表达式】来进行参数匹配
由于正则表达式并不属于本文讲述内容,在这里不再赘述,只简单解释两个字符表达式:
- “*”:用来匹配多个或0个字符
- “+”:用来匹配至少一个字符
下面通过一个例子,来说明正则表达式的使用:
加号“+“的使用:
- 新建一个“UserSetting.vue”文件,编写代码如下:
UserSetting.vue:
<script setup>
</script>
<template><h1>用户设置</h1><h2>id:{{ $route.params.id }}</h2>
</template>
- 修改main.js中的路由配置
main.js:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import User from './components/User.vue'
import UserSetting from './components/UserSetting.vue'//创建路由配置
const routerConfig = [{path:'/user/:username',component:User},{// \\d+表示只匹配数字path:'/user/:id(\\d+)',component:UserSetting}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 此时我们已经完成【正则表达式】匹配路由的所有设置,尝试输入“/user/小王”与“/user/666”查看页面有何不同
乘号“*”的使用
- 创建一个“Category.vue”文件,编写代码如下:
Category.vue:
<script>
</script>
<template><h1>类别</h1><h2>{{ $route.params.cat }}</h2>
</template>
- 修改main.js中的路由配置
main.js:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import Category from './components/Category.vue'//创建路由配置
const routerConfig = [{// cat*用来匹配多级URLpath:'/category/:cat*',component:Category}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 最后我们输入URL看看页面发生了什么变化,“/category/参数1/参数2/参数3”
可以看到所有的参数变成了一个列表,“*”号的一个重要作用就是匹配多级参数
问号“?”的使用
问号“?”的作用是表示参数是可选的,可有可无
例如根据上面乘号“*”的例子,我们修改main.js中代码:
{// ?表示参数可选path:'/category/:cat?',component:Category
}
路由的嵌套
在前面,我们定义了很多路由,但是真正渲染路由的地方只有一个,即那唯一的一个“<router-view>”,这类路由实际上被叫做“顶级路由”(即路由在根组件中渲染)
但在实际开发中,我们可能需要在某个“子组件”内,单独渲染路由,此时就需要叫作“路由的嵌套”
现在假设“某组件中需要渲染好友列表”,例子如下:
- 创建一个“Friends.vue”文件,编写代码如下:
Friends.vue:
<script setup>
</script>
<template><h1>好友列表</h1><h2>好友人数:{{ $route.params.count }}</h2>
</template>
- 修改User.vue
User.vue:
<script setup>
</script>
<template><h1>用户中心</h1><h2>姓名:{{ $route.params.username }}</h2><router-view></router-view>
</template>
- 修改main.js中的路由配置
main.js:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import User from './components/User.vue'
import Friends from './components/Friends.vue'//创建路由配置
const routerConfig = [{path:'/user/:username/',component:User,// children表示该路由下的子路由// 注意:子路由的URL拼接紧凑着父路由// 例如:想访问子路由,完整的URL为:‘/user/username/friends/:count’// 子路由前面不能加一个/children:[{path:'friends/:count',component:Friends}]}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 最后,输入“/user/小王/friends/100”
路由跳转
路由跳转,除了我们已经介绍过的<router-link>进行跳转,还可以使用函数的方式进行路由跳转
使用路由方法
当我们成功向Vue注册路由后,在任何Vue实例中,都可以通过$route属性访问路由对象
通过调用路由对象的push方法可以向history栈添加一个新的记录
下面是一个例子:
- 修改App.vue,代码如下:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import User from './components/User.vue'//创建路由配置
const routerConfig = [{path:'/user/:username',component:User}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 修改App.vue,代码如下:
<script setup>import { useRouter } from 'vue-router'const router = useRouter()function toUser() {router.push('/user/小王')}
</script>
<template><h1>这里是主页面</h1><button @click="toUser">用户中心</button><router-view></router-view>
</template>
注意:上面使用的是Vue3.4版本更新后的setup语法糖,在setup语法糖中并没有“$route”属性,但是我们可以使用“useRouter”方法返回一个“route”对象
在这里,给出传统export模式的一个写法(export模式下,使用$route没有任何问题):
<script>export default {methods:{toUser(){this.$router.push('/user/小王')}}}
</script>
<template><h1>这里是主页面</h1><button @click="toUser">用户中心</button><router-view></router-view>
</template>
效果:
由此可见,使用push()方法进行路由跳转,与使用<router-link>标签进行路由跳转效果是一样的
路由命名
我们可以在定义一个路由的使用,通过指定name属性来给路由设置一个名字,这样,后面使用该路由的时候,可以直接通过路由的名字定位到该路由
{path:'/user/:username',component:User,//通过name属性指定路由名字,此时这条路由的名字就叫做'user'name:'user'
}
拼接参数实现路由跳转
在使用push()方法进行路由跳转时,除了可以直接使用URL进行跳转,我们还可以使用路由的名字进行跳转
下面是一个例子:
- 修改main.js,代码如下:
main.js:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import User from './components/User.vue'//创建路由配置
const routerConfig = [{path:'/user/:username',component:User,name:'user'}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 修改App.vue
App.vue:
<script setup>import { useRouter } from 'vue-router'const router = useRouter()function toUser() {router.push({name:'user',params:{username:'小王'}})}
</script>
<template><h1>这里是主页面</h1><button @click="toUser">用户中心</button><router-view></router-view>
</template>
通过params参数来添加拼接URL的参数,而name指定需要拼接的路由名字
但如果路有需要查询参数,此时我们可以通过query属性进行设置:
//会被处理成 /user?id=xixixi
router.push({path:'/user',query:{id:'xixixi'}
})
注意:“如果设置了path属性,那么params属性会被自动忽略”
push方法返回Promise对象
在使用push方法进行路由跳转时,push方法会返回一个promise对象,可以用其来处理跳转成功之后的逻辑
router.push({path:'/user',query:{id:'xixixi'}}).then(() => {console.log("跳转成功")
})
路由历史控制
当我们使用<router-link>标签或者push()方法进行路由跳转时,新的路由实际上会被放入history栈中,我们可以操控history栈从而实现更精确的页面跳转控制
页面不进入history栈的跳转
某些特殊的情况下,我们可能希望页面直接跳转,并且无法从新页面回退到旧页面,此时我们可以使用:“replace参数”或“replace方法”来直接替换掉当前页面
它的工作原理是:“用新页面直接替换掉当前页面,旧页面不会进入导航栈,自然无法回退”
this.$router.push({path:'/user/小王',replace:true
})
//等同于
this.$router.replace({path:'/user/小王'
})
注意:“这里使用的是this.$router实际上就是使用export模式下的导出,如果使用setup语法糖,需要自己设置router属性,上面已经提到过,这里不再赘述”
使用history灵活跳转到某个位置
//跳转到后一个页面
this.$router.go(1)
//跳转到前一个页面
this.$router.go(-1)
//跳转到前两个页面
this.$router.go(-2)
路由的命名
路由的命名前面已经简单提到过,在这里我们再详细的说一下
通过设置name属性为路由指定名字,使用name属性的优势:“避免硬编码URL”、“自动处理参数编码”
使用名称进行路由切换
- 使用push切换
this.$router.push({name:'user',params:{username:'小王' }
})
- 使用<router-link>切换
<router-link :to="{name:'user',params:{username:'小王'}}">小王</router-link>
路由视图命名
路由视图命名是指对<router-view>标签进行命名
如果某个组件下面有两个“同级子路由”,这个时候就需要对<router-view>进行命名,用来区分两个不同的子路由
使用“name”属性对路由命名
一个例子如下:
- 修改App.vue,代码如下:
App.vue:
<script setup>
</script>
<template><div><router-view name="topBar"></router-view></div><div><router-view name="main"></router-view></div>
</template>
- 修改main.js,代码如下:
main.js:
import {createApp} from 'vue'
//引入必要的路由方法
import {createRouter,createWebHashHistory} from 'vue-router'//引入根组件和页面组件
import App from './App.vue'
import User from './components/User.vue'
import UserSetting from './components/UserSetting.vue'//创建路由配置
const routerConfig = [{path:'/home/:username/:id',//注意定义多级路由时,这里使用的是components,而不是component,一个是复数一个是单数components:{topBar:User,main:UserSetting}}
]
//创建路由对象
const router = createRouter({history:createWebHashHistory(),routes:routerConfig
})
const app = createApp(App)//注册路由
app.use(router)
app.mount('#app')
- 最后,输入“/home/小王/666”
效果:
默认名字default
对于没有命名的<router-view>,其名字会被默认分配为default
因此,下面的写法与上面是一样的:
App.vue:
<script setup>
</script>
<template><div><router-view name="topBar"></router-view></div><div><router-view></router-view></div>
</template>
main.js:
//创建路由配置
const routerConfig = [{path:'/home/:username/:id',//注意定义多级路由时,这里使用的是components,而不是component,一个是复数一个是单数components:{topBar:User,default:UserSetting}}
]
使用别名
别名提供了一种“路由路径映射”的方式,也就是可以自由地将组件映射到一个任意的路由上,而不受嵌套结构的限制
使用“alias”属性来设置别名
一个例子如下:
//路由配置
const route = [{path:'/user/:id(\\d+)',component:UserSetting,alias:'/setting/:id'}
]//之后,下面两个路径的渲染效果一致
/setting/666/
/user/666/
使用alias属性,可以设置为“字符串”配置一个别名,也可以设置为“数组”配置一组别名:
//路由配置
const route = [{path:'/user/:id(\\d+)',component:UserSetting,alias:['/setting/:id','/s/:id']}
]//之后,下面三个路径的渲染效果一致
/setting/666/
/user/666/
/s/666/
路由重定向
重定向会将当前路由映射到另一个路由上,URL会改变
路由重定向与别名最大的区别就是,路由重定向,会改变URL
例如,当用户需要访问'/d/1'时,需要渲染'/demo1'的页面:
const route = [{path:'/demo1',component:Demo1,name:'Demo'},{path:'/d/1',redirect:'demo1'//也支持使用名字进行重定向//redirect:{name:'Demo'}}
]
关于路由传参
在前面我们在组件中使用$route.params的方式来获取路由参数,这种方法虽然简单,但是不利用组件的复用,因为组件与路由的耦合太强,为此,我们在这里学习“使用外部属性进行传参”
一个耦合性很强的获取用户名字的例子:
<script setup>
</script>
<template><h1>用户中心</h1><h2>姓名:{{ $route.params.username }}</h2>
</template>
现在我们使用“外部属性”来获取用户名字
<script setup>const {username} = defineProps(['username'])
</script>
<template><h1>用户中心</h1><h2>姓名:{{ username }}</h2>
</template>
main.js修改如下:
const routerConfig = [{path:'/user/:username',component:User,//使用外部属性,需要props设置为trueprops:true}
]
效果:
同级多个子路由
对于多个子路由,我们需要对多个子路由分别进行props设置
const routes = {path:'/home/:username/:id',components:{topBar:User,main:UserSetting},props:{topBar:true,main:true}
}
固定的props值
如果传入的参数是一个固定值,那么我们可以将props的值固定,此时接收到的参数的值就是一个固定值
const router = {path:'/user/:id(\\d+)',component:UserSetting,props:{id:'000'}
}此时访问 /user/6666
获取到的id是000而不是6666
props是一个函数
props还有一种更加强大的使用方式,可以将其设置为一个匿名函数,函数中返回组件的外部属性对象,这种方式动态性很好!
const routerConfig = [{path:'/user/:username',component:User,props:myroute => {return {username:myroute.params.username}}}
]
动态路由
目前为止,我们所使用的路由都是在main.js定义的静态路由,在实际开发中,我们可能需要动态的更新路由,这就叫做动态路由
动态添加路由
使用“addRoute”方法操作“$route”对象来实现动态添加路由
- 修改Demo1.vue文件,代码如下:
Demo1.vue:
<script setup>import {onMounted} from 'vue'import {useRouter} from 'vue-router'import Demo2 from './Demo2.vue'const router = useRouter()onMounted(() => {router.addRoute({path:'/demo2',component:Demo2})})function click(){router.push('/demo2')}
</script><template><h1>Demo1页面</h1><button @click="click">跳转Demo2</button>
</template>
- 修改main.js文件,代码如下:
const routerConfig = [{path:'/demo1',component:Demo1}
]
- 输入“/demo1”,观察效果
动态删除路由
- 当时用addRoute方法添加路由时,如果添加了重名的路由,那么旧路由会被删除
this.$router.push({path:'/demo1',component:Demo1,name:'Demo'
})
//此时,/demo1会失效
this.$router.push({path:'/demo2',component:Demo2,name:'Demo'
})
- 使用addRoute方法添加路由时,它会返回一个“删除回调”函数,执行该函数可以直接删除添加的路由
let call = this.$router.push({path:'/demo1',component:Demo1,name:'Demo'
})
//直接删除路由
call();
- 对于已经命名的路由,可使用removeRoute方法删除路由
this.$router.push({path:'/demo1',component:Demo1,name:'Demo'
})//使用removeRoute方法删除命名路由
removeRoute('/Demo');
- 注意:“当路由被删除时,它所有的别名和子路由也会被同步删除”
获取现有路由
使用hasRoute()方法来查询当前路由中的子路由
使用getRoutes()方法来获取$route对象上的所有路由,包括子路由,返回一个列表
//接收一个路由名字
this.$router.hasRoute("Demo2")
this.$router.getRoutes()
- 一个getRoutes()包含多个同级子路由的构造:
main.js配置:
const routerConfig = [{path:'/user',components:{topBar:User,main:UserSetting}}
]