入门
介绍
- Vue Router 是Vue的官方路由插件,用于构建单页应用
- 页面不刷新,配合ajax实现页面的局部更新,并响应式更新地址栏URL
- 在工程中,普通组件一般放在components下,而路由组件放于pages目录下
- 切换组件时,未调用的组件是被销毁的,调用时被挂载
- 整个应用由一个router(路由器)统一管理,通过$router调用
- 但每个路由组件都有自己的$route属性
安装vue router
vue2的工程使用vue-router3,vue3的工程使用vue-router4,版本需要对应,这里以安装vue-router3为例
在脚手架中执行:npm i vue-router@3
路由的使用
案例:通过组件嵌套,实现功能
- 在app单个页面中通过按钮实现Home和About页面的切换
- About页面中嵌套aboutMe和aboutWeb页面
1. 新建pages目录,存放Home.vue、About.vue、aboutMe、aboutWeb组件
Home.vue文件:
<template>
<div class="home">
<h2>Home页面</h2>
</div>
</template>
<script>
export default {
name:'pageHome'
}
</script>
About.vue文件:
<template>
<div class="about">
<h2>About页面</h2>
<div class="about-btn">
<!--router-link标签路由切换按钮的两种写法,该标签最后会被解析为a标签-->
<!--1. 根据路径调用组件-->
<router-link to="/about/aboutWeb">关于网页</router-link>
<router-link to="/about/aboutMe">关于我</router-link>
<!--2. 根据路由规则中的name属性调用组件-->
<router-link :to="{name: 'aboutme'}">关于网页</router-link>
<router-link :to="{name: 'aboutweb'}">关于我</router-link>
</div>
<div class="about-content">
<!--子组件显示的位置-->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'pageAbout'
}
</script>
aboutWeb.vue文件 (aboutMe.vue类似)
<template>
<div>
<ul>
<li v-for="(t,index) in tech" :key="index">{{t}}</li>
</ul>
</div>
</template>
<script>
export default {
name:'aboutWeb',
data(){
return{
tech:{
前端:'html/css/javaScript',
后端:'java',
系统:'CentOS7',
服务器:'nginx'
}
}
}
}
</script>
2. 新建router目录,新建index.js文件用于创建路由器
index.js文件:
// 引入vueRouter插件
import VueRouter from "vue-router";
//引入路由组件
import Home from '../pages/pageHome'
import About from '../pages/pageAbout'
import aboutMe from '../pages/aboutMe'
import aboutWeb from '../pages/aboutWeb'
// 创建路由器
export default new VueRouter({
// 创建路由规则一个{}内为一条路由
routes:[
{
name: 'home', //路由规则指定的组件别名
path:'/home', //路由路径
component:Home //调用的组件
},
{
name: 'about',
path:'/about',
component:About,
children:[ //组件嵌套
{
name:'aboutme',
path:'aboutMe',
component:aboutMe
},
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb
}
]
}
]
})
3. 在main.js中引入路由器
import Vue from 'vue'
import App from './App.vue'
// 引入vue-router
import VueRouter from 'vue-router'
// 使用vue-router
Vue.use(VueRouter)
// 引入创建的路由器
import router from './router'
new Vue({
render: h => h(App),
router:router //配置路由器
}).$mount('#app')
4. 在App中调用路由组件
<template>
<div id="app">
<div class="btn">
<!-- active-class用于指定该链接按钮被选中时生效的样式-->
<router-link class="link" active-class="active" to="/home">Home</router-link>
<router-link class="link" active-class="active" to="/about">About</router-link>
<!-- 同样,to可以写为对象形式 -->
<router-link class="link" active-class="active" :to="{name:'home'}">Home</router-link>
<router-link class="link" active-class="active" :to="{name:'about'}">About</router-link>
</div>
<div class="content">
<!-- 指定组件显示的位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'App',
}
</script>
路由传参
query传参
父组件可通过向 to 属性添加参数进行传参,类似于带参数的http请求,子路由可通过$route.query.参数名读取数据
案例:为aboutWeb组件添加子组件webTech,并由aboutWeb向webTech传参
方法1:通过字符串传参
在路由路径后添加查询字符串 ? ,并在之后以键值对的方式传参。
父组件 aboutWeb.vue
<template>
<div>
<ul>
<li v-for="(t,index) in tech" :key="index">
<!-- 字符串写法 -->
<!-- 注意:由于to前使用了:解析js语法,因此要添加``将语句解析为字符串 -->
<router-link :to="`/about/aboutWeb/webTech?name=${t.name}&lang=${t.language}`">{{t.name}}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'aboutWeb',
data(){
return{
tech:[
{name:'前端',language:'html/css/javaScript'},
{name:'框架',language:'vue'},
{name:'后端',language:'java'},
{name:'系统',language:'CentOS7'},
{name:'服务器',language:'nginx'},
]
}
}
}
</script>
方法2:通过对象传参
<template>
<div>
<ul>
<li v-for="(t,index) in tech" :key="index">
<!-- 对象写法 -->
<router-link :to="{
path:'/about/aboutWeb/webTech',
query:{
name:t.name,
lang:t.language
}
}">
{{t.name}}
</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'aboutWeb',
data(){
return{
tech:[
{name:'前端',language:'html/css/javaScript'},
{name:'框架',language:'vue'},
{name:'后端',language:'java'},
{name:'系统',language:'CentOS7'},
{name:'服务器',language:'nginx'},
]
}
}
}
</script>
子路由读取参数
webTech.vue文件:
<template>
<div class="show">
<!-- 读取数据 -->
<span class="tech-name">{{$route.query.name}}:</span>
<span class="tech-lang">{{$route.query.lang}}</span>
</div>
</template>
<script>
export default {
name:'webTech'
}
</script>
params传参
方法1:通过字符串传参
通过字符串传参时,必须在配置路由规则时在path路径中指定解析的参数
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb,
children:[{
path:'webTech/:name/:lang', //在路径中解析参数
conpontent:webTech
}]
}
父组件通过附带参数的路径传输参数
<router-link :to="`/about/aboutWeb/webTech?name=${t.name}&lang=${t.language}`">{{t.name}}</router-link>
子组件通过$route.params.参数名读取参数
<span class="tech-name">{{$route.params.name}}</span>
<span class="tech-lang">{{$route.params.lang}}</span>
方法2:通过对象传参
通过对象传参时,to属性不能再使用path调用组件,而需要使用name属性,因此路由规则中一定需要给予组件name属性
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb,
children:[{
name:'webtech' //必须
path:'webTech/:name/:lang', //必须
conpontent:webTech
}]
}
传参的父组件
<router-link :to="{
name:'webtech', //不能使用path
params:{
name:t.name,
lang:t.language
}}">
{{t.name}}
</router-link>
子组件通过$route.params.参数名读取参数,代码同上
props属性
当需要大量调用参数时,频繁使用$route.query增加了代码量,可以在路由中配置props属性简化代码。props属性支持三种配置方法。
1. 对象形式,传输固定参数
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb,
children:[{
name:'webtech'
path:'webTech',
conpontent:webTech,
//对象形式,传输固定参数
props:{ name:'框架',
lang:'Vue'}
}]
}
2. 函数形式,传输指定参数(用query)
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb,
children:[{
name:'webtech'
path:'webTech',
conpontent:webTech,
//对象形式,传输固定参数
pros(route){
return {
name: route.query.name,
lang: route.query.language
}
}
}]
}
3. 布尔值,自动传输所有params参数
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb,
children:[{
name:'webtech'
path:'webTech/:name/:lang', //自动传输所有参数
conpontent:webTech,
//布尔值形式
pros: true
}]
}
接收参数
子组件需要通过props属性接收参数
<span>{{name}}</span> //直接调用
<span>{{lang}}</span>
export default{
name:'webTech',
pros:['name','lang'] //接收参数
}
路由导航
路由导航分为声明式和编程式
- 声明式:通过<router-link>配置to属性实现跳转
- 编程式:通过调用push、back等API跳转组件
路由导航与浏览器历史记录
浏览器历史记录默认为push状态,组件的跳转会记录到历史记录中,通过浏览器的前进/后退按钮,或者调用go、back等API,能根据历史记录实现页面的前进、回退。当浏览器历史记录切换为replace状态时,浏览器会使用当前页面的记录替换掉上一条历史记录。
<--跳转到About页面后,About页面的历史记录会替换掉该页的历史记录,此时无法再使用浏览器的回退按钮返回该页面了-->
<router-link replace to="...">跳转到About页面</router-link>
编程式路由导航
不借助<router-link>标签,实现路由跳转
API:
- push:跳转到指定路由组件
- replace:跳转到指定路由组件并替换浏览器历史记录
- back:回退
- forward:前进(需要之前回退过)
- go(值):根据值前进/回退指定次数
eg:通过按钮实现(或div)实现路由跳转
<template>
<div>
<button class="link" @click="backTo">回退</button>
<button class="link" @click="forwardTo">前进</button>
<button class="link" @click="goTo">跳转</button>
<ul>
<li v-for="(t,index) in tech" :key="index">
<button @click="pushTo(t)">push</button>
<button @click="replaceTo(t)">replace</button>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'aboutWeb',
data(){
return{
tech:[
{name:'前端',language:'html/css/javaScript'},
{name:'后端',language:'java'},
{name:'系统',language:'CentOS7'},
{name:'服务器',language:'nginx'},
]
}
},
methods:{
backTo(){
this.$router.back() //回退一次
},
forwardTo(){
this.$router.forward() //前进一次
},
goTo(){
this.$router.go(-2) //,回退2步,正数前进,负数后退
},
pushTo(m){ //跳转到指定组件
this.$router.push({
path:'/about/aboutWeb/webTech',
query:{
name:m.name,
lang:m.language
}
})
},
replaceTo(n){ //跳转到指定组件并替换上一个历史记录
this.$router.replace({
path:'/about/aboutWeb/webTech',
query:{
name:n.name,
lang:n.language
}
})
}
}
}
</script>
路由组件的缓存
当组件进行切换时,原组件会被销毁,原组件中未保存的表单内容等数据同时也会被删除,此时,可以使用<keep-alive>标签阻止组件被销毁,保留数据。
//缓存多组件使用 :include="['组件1','组件2',...]"
<keep-alive include="aboutMe">
<router-view></router-view>
</keep-alive>
路由独有生命周期
- activated:当组件被激活时(显示到页面)
- deactivated:当组件失活时
当组件被<keep-alive>时,组件不会被销毁,在切换组件时created、destory等钩子函数不会执行,此时可以使用activated、deactivated替代。
eg:使某组件在激活时启用定时器,失活时关闭
export default {
name:'webTech',
data(){.....},
activated(){
let timer=setInterval(....);
},
deactivated(){
clearInterval(this.timer)
}
}
路由守卫
在切换组件时,可借助路由守卫来阻止/放行组件的跳转,常用于判断用户是否有权限访问,无权限时阻止访问,也可用于在组件跳转前后执行某些功能。
前置、后置路由守卫(全局)
该路由守卫写于路由器配置文件中
- beforeEach((to,from,next)=>{ }):前置路由守卫,在初始化、路由跳转前执行,接收to,from,next三个参数,to代表即将跳转的组件,from为来源组件,next代表放行
- beforeEach((to,from)=>{ }):后置路由守卫,在路由跳转后执行,接收to,from两个参数
eg: 检查用户是否为指定用户,不是则禁止访问aboutWeb和aboutMe组件
import VueRouter from "vue-router";
import Home from '../pages/pageHome'
import About from '../pages/pageAbout'
import aboutMe from '../pages/aboutMe'
import aboutWeb from '../pages/aboutWeb'
import webTech from '../pages/webTech'
const router= new VueRouter({
routes:[
{
name:'home',
path:'/home',
component:Home,
meta:{title:'首页'}//meta为自定义属性,程序员可自定义添加任何参数
},
{
name:'about',
path:'/about',
component:About,
meta:{title:'关于'},
children:[
{
name:'aboutme',
path:'aboutMe',
component:aboutMe,
meta:{isAuth:true,//标识该组件是否需要鉴别权限
title:'关于我'}//用于组件跳转后修改地址栏标题
},
{
name:'aboutweb',
path:'aboutWeb',
component:aboutWeb,
meta:{isAuth:true,
title:'关于网站'},
children:[
{
name:'webTech',
path:'webTech',
component:webTech,
meta:{title:'关于本站采用的技术'}
}
]
}
]
}
]
})
// 全局前置路由守卫
router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){//确认是否鉴权,避免对所有组件都进行鉴权消耗性能
// 当组件不多时,可以使用下面的代码通过判断路径来识别哪些组件需要权限认证
// if(to.name==='aboutme'||to.path==='/about/aboutWeb')
//只有localStorage中的user为zhoujielun才能访问
if(localStorage.getItem('user')==='zhoujielun'){
next() //放行访问
}
else{
alert('无权限访问')
}
}
else{
next() //对不需要权限认证的页面直接放行
}
})
// 全局后置路由守卫
//用于切换完毕后,修改网页地址栏的标题
router.afterEach((to)=>{
document.title=to.meta.title
})
export default router
独享路由守卫
只为某个路由组件配置的路由守卫, 同样写于路由器配置文件中
beforeEnter:(to,from,next)=>{ }只对针对某个组件配置守卫,同样接收to,from,next三个参数,to代表即将跳转的组件,from为来源组件,next代表放行
eg: 判断用户是否为指定用户,不是则禁止访问aboutMe组件
{
name:'about',
path:'/about',
component:About,
meta:{title:'关于'},
children:[
{
name:'aboutme',
path:'aboutMe',
component:aboutMe,
meta:{isAuth:true,//鉴别权限
title:'关于我'},
//独享路由守卫
beforeEnter:(to,from,next)=>{
if(to.meta.isAuth){
//只有localStorage中的user为zhoujielun才能访问
if(localStorage.getItem('user')==='zhoujielun'){
next()
}
else{
alert('无权限访问')
}
}
else{
next()
}
}
},
组件内路由守卫
无法修改路由器配置文件时,可以将路由守卫设于组件.vue的文件中。组件内的路由守卫只会在通过路由规则进入组件时才会执行,如果将组件标签直接添加到页面中,该组件会被调用,但此时不属于通过路由规则进入组件,不会执行下述路由守卫函数。
- beforeRouteEnter(to,from,next){ }:进入组件前执行
- beforeRouteLeave(to,from,next){ }:离开组件前执行
- beforeRouteUpdate(to,from,next){ }:组件更新时执行
<template>
<div class="about">
<h2>About页面</h2>
<div class="about-btn">
<router-link to="/about/aboutWeb">关于网页</router-link>
<router-link to="/about/aboutMe">关于我</router-link>
</div>
<div class="about-content">
<keep-alive include="aboutMe">
<router-view></router-view>
</keep-alive>
</div>
</div>
</template>
<script>
export default {
name:'pageAbout',
// 通过路由规则进入组件时调用
beforeRouteEnter(to,from,next){
//只有localStorage中的user为zhoujielun才能访问
if(localStorage.getItem('user')==='zhoujielun'){
next()
}
else{
alert('无权限访问')
}
},
// 通过路由规则离开组件时调用
beforeRouteLeave(to,from,next){
console.log('即将离开组件')
next();//放行
}
}
</script>