Vue Router
1. 相关理解
1.1 Vue Router的理解
Vue的一个插件库,专门用来实现SPA应用
1.2 对SPA应用的理解
- 单页 Web 应用 (single page web application,SPA
- 整个应用只有一个完整的页面
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
- 数据需要通过 ajax 请求获取
1.3 路由的理解
- 什么是路由?
- 一个路由就是一组映射关系(key - value),多个路由需要路由器(router)进行管理
- key为路径,value可能是function或component
在开发中,路由分为后端路由和前端路由
2. 路由分类
1)后端路由
i. 概念:根据不同的用户URL 请求,返回不同的内容,value是function,用于处理客户端提交的请求
ii.本质:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
后端路由根据不同的URL地址分发不同的资源
2)前端路由
i. 概念:根据不同的用户事件,显示不同的页面内容,value是component,用于展示页面内容
ii. 本质:当浏览器的路径改变时,对应的组件就会显示
前端路由负责事件监听,触发事件后通过事件函数渲染不同内容
2. 基本路由
2.1 vue-router使用步骤
- 安装vue-router命令:
npm i vue-router@3
vue2版本使用vue-router的3版本
vue3版本使用vue-router的4版本 - 应用插件:
Vue.use(VueRouter)
- 编写router配置项 router/index.js
文件目录结构
Vue2的vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
//创建router实例对象,去管理一组的路由规则
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
//暴露router
export default router
Vue3的vue-router
import { createRouter, createWebHistory } from 'vue-router'
//引入组件
import Home from '../views/Home.vue'
import About from '../views/About.vue'
//等价于
//const Home = () => import('../views/Home.vue')
//const About = () => import('../views/About.vue')
//声明一个路由规则,访问home,加载Home组件
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
// 创建一个路由
const router = createRouter({
//模式为历史模式,请求过的路由会push,类似于window.history.pushState()
history: createWebHistory(process.env.BASE_URL),
//路由规则
routes
})
//缺省暴露路由
export default router
src/main.js
- 路由的三种模式
hash模式:
使用URL的hash值来作为路由,用来指导浏览器动作,对服务器端完全无用,支持所有浏览器
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
abstract模式:
支持所有JavaScript运行模式,如果发现没有浏览器的API,路由会自动强制进入这个模式。
- 路由两种自带标签
a. 实现切换<router-link></router-link>
浏览器会倍替换为a
标签active-class
可配置高亮样式
b. 展示指定位<router-view></router-view>
<router-link active-class="active" to="/">Home</router-link>
2.2 几个注意点
-
路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹
路由组件:是指由路由渲染出来的组件,由<router-view>
渲染弄出来的组件
一般组件:一般要写组件标签,如<Banner/>
比如上一节的案例就可以修改为
src/pages/Home .vue
src/pages/About.vue
src/router/index.js
src/components/Banner.vue
src/App.vue -
通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
-
每个组件都有自己的
$route
属性,里面存储着自己的路由信息。 -
整个应用只有一个
router
,可以通过组件的$router
属性获取到。
路由有两种全局属性$route
: 当前正在访问的路由$router
:代表整个大的路由器的对象
2.3 触发路由
App.vue
触发路由的几种操作
<template>
<div id="nav">
<!-- router-link与a标签相比有事件处理程序,会渲染成a标签,to指定路径,放到URL中-->
<router-link to="/">Home</router-link> |
<router-link to="/about" >关于</router-link> |
<router-link to="/about" custom v-slot="{ navigate }">
<button @click="navigate" @keypress.enter="navigate" role="link">关于</button>
</router-link> |
<button :class="{active:$route.path='/user'}" @click="$router.push('/user')">个人中心</button>|
<button @click="$router.go(-1)">返回</button>|
{{$route.path}}
</div>
<hr>
<router-view/>
</template>
若要一个页面显示三个不同的组件App.vue
<template>
<div id="nav">
<!-- router-link与a标签相比有事件处理程序,会渲染成a标签,to指定路径,放到URL中-->
<router-link to="/">Home</router-link> |
<router-link to="/about" >关于</router-link> |
<router-link to="/about" custom v-slot="{ navigate }">
<button @click="navigate" @keypress.enter="navigate" role="link">关于</button>
</router-link> |
<button :class="{active:$route.path=='/user'}" @click="$router.push('/user')">个人中心</button>|
<button @click="$router.go(-1)">返回</button>|
{{$route.path}}
</div>
<hr>
<!-- name对应组件名称,中间一个缺省为home-->
<router-view class="one" name="User"></router-view>
<router-view class="two" ></router-view>
<router-view class="three" name="About"></router-view>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
#nav a.router-link-exact-active {
color: #42b983;
}
.active {
color: red !important
}
.one {
width: 25%;
height: 300px;
float: left;
background-color: #42b983;
}
.two {
width: 50%;
height: 300px;
float: left;
background-color: cornflowerblue;
}
.three {
width: 25%;
height: 300px;
float: left;
background-color: coral;
}
</style>
index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import User from '../views/User.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
//对象格式,排序也有关系的
components: {
default: Home,
About,
User
}
// component: Home
},
{
path: '/user',
name: 'user',
component: User
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
2.4 嵌套路由
- 配值路由规则,使用
children
配置项router/index.js
路由设置
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import User from '../views/User.vue'
import About from '../views/About.vue'
import Order from '../views/UserOrder.vue'
import Setting from '../views/UserSetting.vue'
//等价于
//const Home = () => import('../views/Home.vue')
//const About = () => import('../views/About.vue')
import * as path from "path";
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/user',
name: 'user',
component: User,
//子路由,/user/order
children: [
//设置默认组件
{
path: '',
component: Order
},
{
path: 'order', //此处不要带斜杠
component: Order
},
{
path: 'setting',
component: Setting
}
]
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
- 跳转
<router-link to="/user/order">我的订单</router-link>
User.vue
<template>
<div class="about">
<h1>This is an User page</h1>
<br>
<div class="menu">
<ul>
<!-- 跳转要写完整路径 -->
<li><router-link to="/user/order">我的订单</router-link></li>
<li><router-link to="/user/setting">个人设置</router-link></li>
</ul>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: "User"
}
</script>
<style scoped>
.menu {
width: 30%;
height: 300px;
background-color: #42b983;
float: left;
}
.content {
width: 70%;
height: 300px;
background-color: floralwhite;
float: right;
}
</style>
2.5 路由传递参数方式
上个嵌套路由的例子,“我的订单”和“个人设置”是采用切换组件来实现,下边采用传递参数来实现页面的切换,即同个模板,传递参数值来切换
2.5.1 params 方式
- 传递参数
User.vue
<template>
<div >
<h1>This is an User page</h1>
<br>
<div class="menu">
……
<ul>
<li v-for="item in article">
<!-- 采用:to的形式,否则双引号的内容不生效,无法成/user/page/10的格式-->
<!-- to的字符串写法一-->
<router-link :class="{active:$route.params.pid==item.id}" :to="'/user/page/'+item.id">{{item.title}}</router-link>
<!-- to的字符串写法 -多参数写法二 -->
<router-link :class="{active:$route.params.pid==item.id}" :to="`/user/page/${item.id}/${item.title}`">{{item.title}}</router-link>
<!-- to的对象写法-->
<router-link :class="{active:$route.params.pid==item.id}"
:to="{
name:'newPage',
params: {pid:item.id}">{{item.title}}</router-link>
</li>
</ul>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: "User",
data(){
return {
article: [
{id:10, title: '这是文章一'},
{id:11, title: '这是文章二'},
{id:12, title: '这是文章三'},
{id:13, title: '这是文章四'},
{id:14, title: '这是文章五'},
]
}
}
}
</script>
<style scoped>...</style>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
添加一个UserPage.vue
<template>
<h2>这是文章的模板</h2>
<!-- $route当前路由,获取参数-->
文章ID:{{$route.params.pid}}
<br>
<h1>{{pageid}}</h1>
</template>
<script>
export default {
name: "UserPage",
computed: {
pageid() {
//获取当前路由属性的参数值pid
return this.$route.params.pid;
}
}
}
</script>
<style scoped></style>
router/index.js
路由配置
const routes = [
……
{
path: '/user',
name: 'user',
component: User,
//子路由,/user/order
children: [
//设置默认组件
……
{
//动态路由配置
name: 'newPage', //to的对象写法,要有name配置
path: 'page/:pid', //如果由多个参数则为 path: 'page/:pid/:title/:xxx' 这样的写法
component: Page
}
]
},
……
]
2.5.2 Query的方式
User.vue
<template>
<div>
<h1>This is an User page</h1>
<br>
<div class="menu">
……
<ul>
……
<br>
<!-- 三种格式-->
<!-- to的字符串写法一 -->
<li><router-link to="/user/article?name=zs&age=10">文章一</router-link></li>
<!-- to的字符串写法二 -->
<li><router-link :to="`/user/article?name=${item.name}&age=${item.age}`">文章一</router-link></li>
<!-- to的对象写法(推荐) -->
<li>
<router-link :to="{
path:'/user/article',
query:{
name:'lisi',
age:20
}}">文章二</router-link>
</li>
<br>
<li><button @click="$router.push({path:'/user/article', query: {name:'word', age:99}})">文章三</button></li>
</ul>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: "User",
data(){
return {
article: [
……
]
}
}
}
</script>
<style scoped>……</style>
添加一个Article.vue
<template>
这是文章内容
<br>
name:{{$route.query.name}}<br>
age:{{$route.query.age}}<br>
</template>
<script>
export default {
name: "Article"
}
</script>
<style scoped></style>
router/index.js
路由配置
const routes = [
……
{
path: '/user',
name: 'user',
component: User,
//子路由,/user/order
children: [
//设置默认组件
……
{
path: 'article', //正常配置
component: Article
}
]
},
……
]
2.6 命名路由
作用:可以简化路由的跳转
如何使用?
- 给路由命名
{
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>
2.7 路由的props配置
props
作用:让路由组件更方便的收到参数,不用一个个的写$route.query.xxx
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件,不会理会query参数
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props($route) {
return {
id: $route.query.id,
title:$route.query.title,
a: 1,
b: 'hello'
}
}
}
跳转去组件的具体代码
<template>
<ul>
<h1>Detail</h1>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
<li>a:{{a}}</li>
<li>b:{{b}}</li>
</ul>
</template>
<script>
export default {
name: 'Detail',
//使用props接受路由参数
props: ['id', 'title', 'a', 'b'],
mounted () {
console.log(this.$route);
}
}
</script>
<style></style>
2.8 路由跳转的replace方法
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:
push
和replace
,
-
push
是追加历史记录 -
replace
是替换当前记录,路由跳转时候默认为push
方法
- 开启
replace
模式
<router-link :replace="true" ...> News</router-link>
//简写
<router-link replace ...> News</router-link>
总结: 浏览记录本质是一个栈,默认 push
,点开新页面就会在栈顶追加一个地址,后退,栈顶指针向下移动,改为 replace
就是不追加,而将栈顶地址替换
2.9 编程式路由导航
- 声明式导航:通过点击链接实现导航的方式 例如:普通网页中的
<a></a>
链接或vue 中的<router-link></router-link>
- 编程式导航:通过调用JavaScript形式的API实现导航的方式 例如:普通网页中的location.href
编程式路由导航作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
具体参数规则:
// 字符串(路径名称)
this.$router.push('/home')
// 对象
this.$router.push({ path: '/home' })
// 命名的路由(传递参数)
this.$router.push({ name: '/user', params: { userId: 123 }})
// 带查询参数,变成/register?uname=lisi
this.$router.push({ path: '/register', query: { uname: 'lisi' }})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go(n) //可前进也可后退,n大于0前进n,小于0后退n
使用
<button @click="$router.go(-1)">返回</button>
<button @click="pushShow(m)">push查看</button>
<script>
...
methods: {
pushShow(m) {
this.$router.push({ path: '/register', query: {id: m.id, name: m.title }})
}
}
...
</script>
2.10 重定向和别名
2.11 页面回退
从a页面=>b页面=>c页面,当前在c页面,执行某方法后可以如同按了浏览器后退键一样返回b页面
- 若项目使用vue-router ,则使用
this.$router.go(-1)
this.$router.back()
window.history.go(-1)
①与②的区别是:
go(-1): 原页面表单中的内容会丢失:
1. this.$router.go(-1):后退+刷新;
2. this.$router.go(0):刷新;
3. this.$router.go(1):前进;
----
back(): 原页表表单中的内容会保留;
1. this.$router.back():后退 ;
2. this.$router.back(0):刷新;
3. this.$router.back(1):前进;
③的区别是:
history.go(-1)是返回浏览器的上一页,而由于Vue是单页面应用,有的浏览器对于hash变更不认为是两个不同的页面,在hash模式下就不会跳回浏览器上一页
2.12 两个生命周期钩子 activated、deactivated
activated
和deactivated
是i路由组件所独有的两个钩子,用于捕获路由组件的激活状态
具体使用:activated
被 keep-alive
缓存的组件激活时调用。deactivated
被 keep-alive
缓存的组件失活时调用。activated
和deactivated
是配合keep-alive
一起使用的,其余不会触发,
在存在keep-alive
的时候可以将activated
当作created
进行使用deactivated
是组件销毁的时候触发,此时的destory是不执行的
2.13 keep-alive缓存路由
keep-alive
作用:主要用于保留组件状态或避免重新渲染。 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
Props:
- include:只有名称匹配的组件会被缓存。
- exclude:任何名称匹配的组件都不会被缓存。
- max:最多可以缓存多少组件实例。
router-view
是vue-router内置组件,如果直接包含在keep-alive
里面,所有路径匹配到的组件都会被缓存
场景:用户在表单输入的时候,如果切换了页面再返回,表单输入的内容都被清空了,这是因为组件在切换时,会已处在创建-销毁的过程中,为节省时间和资源,采用keep-alive
使其保存在缓存中
created() {
console.log('About---组件创建');
},
unmounted() {
console.log('About---组件被销毁');
},
activated() {
console.log('About---keep-alive 缓存的组件激活时调用');
},
deactivated() {
console.log('About---keep-alive 缓存的组件停用时调用');
}
include
设置想要缓存的组件名
//缓存一个路由组件
<keep-alive include="News"> //include中写想要缓存的组件名,不写表示全部缓存
<router-view></router-view>
</keep-alive>
//缓存多个路由组件
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
只要将router-view
修改如下代码,就可实现缓存效果
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
这种方法会触发actived()
和deactivated()
方法
可单独设置exclude
使得某些组件不缓存
<router-view v-slot="{ Component }">
<transition>
<keep-alive exclude="About">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
为了保证离开一个页面后再返回仍停留在离开时的状态,可在组件内设置前置守卫,将离开时的路径保存下来,待缓存激活active时再设置全局路由
created() {
console.log('User---组件创建');
},
unmounted() {
console.log('User---组件被销毁');
},
activated() {
console.log('User---keep-alive 缓存的组件激活时调用');
//回到离开时的状态
this.$router.push(this.path)
},
deactivated() {
console.log('User---keep-alive 缓存的组件停用时调用');
},
beforeRouteLeave(to,from) {
//获取离开的路由
this.path = from.fullPath
console.log('to----'+to.fullPath);
console.log('from----'+from.fullPath);
}
2.14 路由守卫
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
- 全局守卫
前置守卫:从一个路由跳转另一个路由之前有一个前置守卫,前置守卫会调用一个方法,这个方法可以处理跳转之前的事情。如登录访问
后置守卫:跳转过去之后,在内容没显示之前,有一个后置钩子
router/index.js
const routes = [
{
path: '/home',
name: 'Home',
//重定向到根 两种方式
// redirect: '/',
redirect: {name: 'root'},
component: Home,
meta: {
title: '首页'
},
}]
const router = new VueRouter({
routes
})
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from)=>{
console.log('前置守卫');
//设置即将到达的页面标题,meta是上方各个路由设置的meta属性
document.title = to.meta.title
console.log('title---'+to.meta.title);
//打印即将到达的路径
console.log('to---'+to.fullPath);
//false不准跳转,true则放行
return true;
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,form)=>{
console.log('后置钩子');
console.log('to---'+to.fullPath);
console.log('form---'+form.fullPath);
})
export default router
const routes = [
{
path: '/home',
name: 'Home',
component: Home,
meta: {
isAuth: true,
title: '首页'
},
}]
const router = new VueRouter({
routes
})
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to, from, next) => {
if(to.meta.isAuth) { //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'zhejiang'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
- 独享守卫
独享守卫是在routes 子路由内写守卫
beforeEnter(to, from, next){
if(to.meta.isAuth) { //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'zhejiang'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
}
文章来源:https://www.toymoban.com/news/detail-489577.html
- 组件内守卫
组件内守卫是在具体组件内写守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
文章来源地址https://www.toymoban.com/news/detail-489577.html
到了这里,关于Vue全家桶(四):Vue Router 路由的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!