一、前端路由的概念与原理
路由:本质就是对应关系。SPA项目中,不同组件之间的切换需要通过前端路由完成
路由分类
- 前端路由:Hash地址与组件之间的对应关系
- 后端路由:请求方式、请求地址与function处理函数之间的对应关系
1、前端路由的工作方式
前端路由工作步骤
- 用户点击页面上的路由链接
- 导致URL地址栏中的Hash值发生变化
- 前端路由监听到了Hash地址的变化
- 前端路由把当前Hash地址对应的组件渲染到浏览器中
实现简易前端路由步骤(实际开发中使用vue-router)
- 导入并注册三个需要配置路由的组件
- 通过<component>标签的is属性,动态切换要显示的组件
- 在根组件中声明跳转路由的<a>链接,通过点击不同的a链接,切换浏览器地址栏中的Hash值
- 在created生命周期函数中监听浏览器地址栏中的Hash地址的变化(window.onhashchange),动态切换要展示的组件的名称
示例
App.vue
<template>
<div>
<h1>App 根组件</h1>
<!-- 放三个能切换路由的连接 ,hash地址随便起就行-->
<a href="#/home">Home</a>
<a href="#/movie">Movie</a>
<a href="#/about">About</a>
<hr />
<component :is="comName"></component>
</div>
</template>
<script>
// 导入三个组件
import MyHome from './MyHome.vue'
import MyMovie from './MyMovie.vue'
import MyAbout from './MyAbout.vue'
export default {
name: 'MyApp',
data() {
return {
comName: 'MyHome',
}
},
created() {
// 监听 hash 值变化的事件
window.onhashchange = () => {
// 通过 location.hash 获取到最新的 hash 值,并进行匹配
switch (location.hash) {
case '#/home':
this.comName = 'MyHome'
break
case '#/movie':
this.comName = 'MyMovie'
break
case '#/about':
this.comName = 'MyAbout'
break
}
}
},
// 注册组件
components: {
MyHome,
MyMovie,
MyAbout,
},
}
</script>
二、vue-router的基本使用
vue-router是vue.js官方给出的路由的解决方案
注
- vue-router 3.x只能结合vue2进行使用,vue-router 4.x只能结合vue3进行使用
vue-router 4.x 官方地址:https://next.router.vuejs.org/
1、 vue-router 4.x的基本使用
使用步骤
- 在项目中安装 vue-router
- 定义路由组件
- 声明路由链接和占位符
- 创建路由模块
- 导入并挂载路由模块
在项目中安装 vue-router
npm install vue-router@next -S
定义路由组件
定义需要路由展示的组件,eg: Home.vue About.vue
声明路由连接和占位符
使用<router-link> 标签来声明路由链接,使用<router-view>标签来声明路由占位符
<template>
<div>
<h1>vue-router 的基本使用</h1>
<!-- 声明路由链接,hash地址要用to属性进行声明,且不再需要以#开头 -->
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<hr />
<!-- 路由的占位符 -->
<router-view>匹配到的组件在这个位置渲染</router-view>
</div>
</template>
创建路由模块
在项目中创建router.js路由模块,并按照以下步骤创建并得到路由的实例对象
- 从vue-router中按需导入两个方法
- 导入需要使用路由控制的组件
- 创建路由实例对象
- 向外共享路由实例对象
router.js
// 按需导入两个方法
// createRouter用于创建路由的实例对象
// createWebHashHistory用于指定路由的工作模式
import { createRouter, createWebHashHistory } from 'vue-router'
//导入需要使用路由控制的组件
import Home from './MyHome.vue'
import About from './MyAbout.vue'
// 创建路由对象
const router = createRouter({
// 指定路由的工作模式为hash
history: createWebHashHistory(),
// 声明路由的匹配规则
routes: [
{ path: '/home', component: Home },
{ path: '/about', component: About },
],
})
// 导出路由对象
export default router
在main.js中导入并挂载路由模块
main.js
import App from './components/App.vue'
import router from './components/router'
const app = createApp(App)
// 挂载路由模块
app.use(router)
app.mount('#app')
三、vue-router的高级用法
1、路由重定向
当用户在访问地址A的时候,强制用户跳转到地址C,从而展示特性的组件页面
通过路由规则的redirect属性,指定一个新的路由地址
router.js中
// 声明路由的匹配规则
routes: [
// path表示需要被重定向的原地址,redirect表示将要被重定向到的新地址
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{ path: '/about', component: About },
],
2、为激活的路由链接设置高亮的效果
被激活的路由链接,默认会应用一个叫做router-link-active的类名,可以使用此类名选择器,为激活的路由链接设置高亮样式
法一:直接修改router-link-active样式
index.css
.router-link-active {
background-color: red;
color: white;
font-weight: bold;
}
法二:自定义路由高亮的class类
基于linkActiveClass属性,自定义路由链接被激活时的类名
router.js
const router = createRouter({
// 指定路由的工作模式
history: createWebHashHistory(),
// 自定义路由高亮的 class 类
linkActiveClass: 'active-router',
// 声明路由的匹配规则
routes: [
// path表示需要被重定向的原地址,redirect表示将要被重定向到的新地址
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{ path: '/about', component: About },
],
})
index.css中
.active-router {
background-color: blue;
color: white;
font-weight: bold;
}
3、嵌套路由
嵌套路由:通过路由,实现组件的嵌套展示
步骤:
- 声明子路由链接和子路由占位符
- 在父路由规则中,通过children属性嵌套声明子路由规则
示例:以在About组件内部嵌套为例
About.vue
声明tab1和tab2的子路由链接以及子路由占位符
<template>
<div>
<h3>About 组件</h3>
<!-- 声明子路由链接 -->
<router-link to="/about/tab1">Tab1</router-link>
<router-link to="/about/tab2">Tab2</router-link>
<hr>
<!-- 声明子路由占位符 -->
<router-view></router-view>
</div>
</template>
router.js
//上面导入 Tab1、Tab2;两个组件,这里省略了
// 声明路由的匹配规则
routes: [
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{
path: '/about',
component: About,
// 通过 children 属性嵌套声明子级路由规则
children: [
// 子路由规则中的path不要以斜线开头
{ path: 'tab1', component: Tab1 },
{ path: 'tab2', component: Tab2 },
],
},
],
嵌套路由中实现路由重定向
进入/about时,自动重定向至/about/tab1
routes: [
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{
path: '/about',
component: About,
// 嵌套路由的重定向
redirect: '/about/tab1',
// 通过 children 属性嵌套声明子级路由规则
children: [
// 子路由规则中的path不要以斜线开头
{ path: 'tab1', component: Tab1 },
{ path: 'tab2', component: Tab2 },
],
},
],
4、动态路由匹配
将hash中可变的部分定义为参数项,从而提高路由规则的复用性
在vue-router中使用英文: 来定义路由的参数项
{ path: '/movie/:id', component: Movie },
示例
App.vue中
<template>
<div>
<h1>vue-router 的基本使用</h1>
<!-- 声明路由链接 -->
<router-link to="/home">首页</router-link>
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>
<hr />
<!-- 路由的占位符 -->
<router-view></router-view>
</div>
</template>
router.js
// 声明路由的匹配规则
routes: [
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
// 动态路由匹配
// 路由中的动态参数以:声明,冒号后面是动态参数的名称
{ path: '/movie/:id', component: Movie},
],
获取动态路由中匹配的参数值
法一:$route.params
可直接使用 $route.params对象访问到动态匹配的参数值
$route.params.动态参数
示例:Movie组件中
<h3>Movie 组件 --- {{ $route.params.id }} </h3>
法二:使用props接收路由参数
在路由规则中可开启props传参
步骤:
- 在路由规则中声明props,值为true
- 在Movie组件中,使用props与动态参数接收路由规则匹配到的参数项
示例:
router.js中
routes: [
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
// 动态路由匹配
{ path: '/movie/:mid', component: Movie, props: true },
],
Movie组件中
<template>
<div>
<h3>MyMovie 组件 --- {{ mid }}</h3>
</div>
</template>
<script>
export default {
name: 'MyMovie',
//使用props与动态参数接收路由规则匹配到的参数项
props: ['mid'],
}
</script>
5、编程式导航
通过调用API实现导航的方式,叫做编程式导航(location.herf)
通过点击链接实现导航的方式,叫做声明式导航(<a>链接、<router-link>)
vue-router中提供的编程式导航API
常用API
this.$router.push('hash地址')
- 跳转到指定的hash地址,从而显示对应的组件
<template>
<div>
<h3>MyHome 组件</h3>
<button type="button" @click="goToMovie(3)">导航到Movie页面</button>
</div>
</template>
<script>
export default {
name: 'MyHome',
methods: {
goToMovie(id) {
this.$router.push('/movie/' + id)
},
},
}
</script>
this.$router.go(数值n)
- 实现导航历史的前进,后退
<template>
<div>
<h3>MyMovie 组件</h3>
<button type="button" @click="goBack">后退</button>
</div>
</template>
<script>
export default {
name: 'MyMovie',
props: ['mid'],
//
methods: {
goBack() {
// 后退到上一个组件页面
this.$router.go(-1)
},
},
}
</script>
6、命名路由
通过name属性为路由规则定义名称的方式,叫做命名路由
{ path: '/home',
component: Home ,
// 使用name为当前路由定义名称
name:'Hom'
},
注:
- 命名路由的name值不能重复,必须保证唯一性
- 当hash地址特别长时,这种方法更方便
使用命名路由实现声明式导航
为<router-link>标签动态绑定to属性,并通过name属性指定要跳转到的路由规则
可以用params属性指定跳转期间要携带的路由参数
路由规则
{ name: 'mov', path: '/movie/:mid', component: Movie, props: true }
跳转路由
<router-link :to="{ name: 'mov', params: { mid: 2 } }">go to movie</router-link>
使用命名路由实现编程导航
调用push函数,期间指定一个配置对象,name是要跳转到的路由规则,params是携带的路由参数
路由规则
{ name: 'mov', path: '/movie/:mid', component: Movie, props: true }
跳转路由
<template>
<div>
<button type="button" @click="goToMovie(1)">go to movie</button>
</div>
</template>
<script>
export default {
name: 'MyHome',
methods: {
goToMovie(id) {
this.$router.push({ name: 'mov', params: { mid: id},})
},
},
}
</script>
7、导航守卫
导航守卫可以控制路由的访问权限
声明全局导航守卫
全局导航守卫会拦截每个路由规则 ,从而对每个路由进行访问权限控制。
步骤:
- 创建路由实例对象
- 调用beforeEach方法声明全局导航守卫
fn方法为守卫方法,必须是一个函数,每次拦截到路由请求,都会调用fn进行处理
// 创建路由对象
const router = createRouter({
})
// 声明全局的导航守卫
router.beforeEach(() => {
console.log('ok')
})
守卫方法的三个形参
to 目标路由对象
from 当前导航正要离开的路由对象
next 是一个函数,表示放行
router.beforeEach((to, from, next) => {
console.log('ok')
next()
})
注:
- 在守卫方法中如果不声明next形参,则默认允许用户访问每一个路由
- 在守卫方法中,如果声明了next形参,则必须调用next函数,否则不允许用户访问任何一个路由
next函数的三种调用方式
- 直接放行:next()
- 强制停留在当前页面 next(false)
- 强制跳转到指定页面next(hash地址)
// 全局路由导航守卫
router.beforeEach((to, from, next) => {
if (to.path === '/main') {
// 证明用户要访问后台主页
next('/login')
} else {
// 访问的不是后台主页
next()
}
})
结合Token控制后台主页的访问权限
// 全局路由导航守卫
router.beforeEach((to, from, next) => {
const tokenStr = localStorage.getItem('token')//读取Token
if (to.path === '/main' && !tokenStr) {//想要访问主页且Token不存在
// next(false) //不允许跳转
next('/login')//强制跳转到登录页
} else {
// 访问的不是后台主页
next()//放行
}
})