前端路由介绍
- 前端路由库
- 状态管理库
- 前端组件库
Vue 生态中选择这三个最最最重要的生态库来介绍。
前端路由
实际上在最早的多页应用时代,并不存在前端路由这么一说,那个时候路由是属于后端(服务器端)的东西,后端会根据不同的请求路由返回不同的页面。

在此开发时期有两个特点:
- 整个项目的前后端代码是杂糅在一起的。
- 多页应用时代,每次切换一个页面,都需要重新请求服务器。
后面慢慢就到了单页应用时代,该时代的特点就是只有一个 HTML 页面,以前视图的切换是整个 HTML 页面的切换,而现在视图的切换是页面上某个模块的切换。

上图中的模块其实就是对应 Vue 中不同的组件,这种组件我们称之为页面级组件。有了页面级组件,需要和路由产生一个映射关系,这个其实就是前端路由。
虽然有了前端路由,但是后端路由仍然存在,只不过从之前的路由和页面的映射关系变成了路由和数据接口之间的映射关系。
1.Vue-router快速上手
Vue生态的前端路由是由 Vue 官方推出的,叫做 Vue Router:https://router.vuejs.org/zh/
首先第一步,需要安装该路由库:
npm install vue-router@4
快速入门
- 我们需要创建两个页面级别的组件,放在 views 目录下面。
- 在 src 下面创建一个 router 目录,用于存放前端路由配置,然后在该目录下面创建一个 index.js,该文件书写具体的路由配置
// 前端路由配置文件
import { createRouter, createWebHistory } from 'vue-router'
// 页面组件
import Home from '../views/Home.vue'
import About from '../views/About.vue'
// 该方法会创建一个路由的实例
// 在创建路由实例的时候,可以传入一个配置对象
const router = createRouter({
history: createWebHistory(), // 指定前端路由的模式,常见的有 hash 和 history 两种模式
// 路由和组件的映射
routes: [
{
path: '/', // 路由的路径
name: 'Home',
component: Home // 路由对应的组件
},
{
path: '/about',
name: 'About',
component: About
}
]
})
export default router
- 需要将该配置所导出的路由实例在 main.js 入口文件中进行挂载
// main.js
// 引入路由实例
import router from '@/router'
// ...
// 挂载
app.use(router).mount('#app')
- 接下来就可以在组件中使用了
<template>
<div id="app">
<h1>欢迎来到Vue-router快速入门示例</h1>
<nav>
<!-- 该组件由 vue-router 这个库提供的 -->
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<!-- 由 vue-router 这个库提供的 -->
<!-- 路由所匹配上的组件,会渲染到这个位置 -->
<router-view />
</div>
</template>
<script setup></script>
<style scoped>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
}
nav a {
padding: 15px;
}
</style>
上面会用到两个由 vue-router 库为我们提供的组件:
- router-link:指示具体的跳转路由路径
- router-view:显示匹配的路由所对应的组件
2. 路由重定向和别名
//1.路由重定向
{
path: '/',
redirect:"/home" //写法1
redirect:{ //写法2
name:"/home"
}
},
//2.命名路由+路由别名
{
path: '/home',
name:"Home", //命名路由
alias:"/wode" //路由别名,通过此路径也可以访问
},
//3.404路由:当以上路由都匹配不到的时候,就会进入到404路由。
{
path: '/:pathMatch(.*)*',
component:NotFoundPage,
},
3.声明式导航
1.使用router-link进行导航
//1.to属性(跳转到哪个路由)
<router-link to="/home"></router-link>
//2. active-class:选中时候的样式
<router-link to="/home" active-class="active"></router-link>
.active{
background:#f55;
}
//3.新版本的vue-router中取消了tag属性,如果想要自己定制router-link就需要借助插槽技术。
custom:告知vue-router要自己定制router-link
navigate:跳转函数,点击的时候执行
<router-link to="/foo" custom v-slot="{ href, route, navigate, isActive, isExactActive}">
<li :class="isActive?'active':''" @click="navigate">首页</li>
</router-link>
4.嵌套路由
二级路由为什么不能写/?
如果写'/',那么就可以通过localhost:9527/nowplaying直接访问到了,我们应该是通过localhost:9527/film/nowplaying来进行访问。
二级路由就相当于在相对应的组件中,再放置一个插槽(routerview),然后可以配合routerlink,进行路由的切换
{
path:"/film",
component:FilmView
redirect:"/film/nowplaying",
//二级路由
children:[
{
path:"nowplaying",//不要加'/'
component:NowPlaying
}
]
}
案例
<template>
<div>
//轮播图
<div style="height:100px;line-height: 100px;text-align: center;">大轮播</div>
<ul class="header">
//tabs栏,用router-link实现
<router-link custom to="/films/nowplaying" v-slot="{isActive,navigate}">
<li @click="navigate">
<span :class="isActive?'kerwinactive':''">正在热映</span>
</li>
</router-link>
<router-link custom to="/films/comingsoon" v-slot="{isActive,navigate}">
<li @click="navigate">
<span :class="isActive?'kerwinactive':''">即将上映</span>
</li>
</router-link>
</ul>
//放置router-view二级路由入口
<router-view></router-view>
</div>
</template>
<style scoped lang="scss">
.header{
display: flex;
height: 50px;
line-height: 50px;
text-align: center;
li{
flex:1;
span{
padding:10px 0;
}
}
}
.kerwinactive{
color:red;
border-bottom: 3px solid red;
}
</style>
5.编程式导航
写法1.router.push("路径");
写法2:router.push({对象})
//方法一:字符串形式(常用)
router.push("/detail");//跳转到detail页面
//方法二:对象形式
router.push({
path:"/detail"
})
6.动态路由传参
6.1 params传参
router.js
//写法一:(常用)
{
path:"/detail/:id",
name:"Detail",
component:Detail
}
//写法二:
{
path:"/detail/:id",
component:Detail
}
使用:
//写法1:
//跳转路由的时候带着参数,如果路由配置项不带参数,那么跳转到detail页面就会白屏
router.push("detail/1");
//写法2:
router.push({
name:"Detail",//对象形式的params传参必须使用name,否则会出错,不能使用path
params:{
id:1,
}
})
//当跳转到详情页之后:localhost:9527/detail/200
//获取动态id,注意此处是route 而不是router
console.log(route.params.id);//200
6.2 query传参
router.js
{
path:"/detail",
component:Detail
}
使用:
//格式:localhost:9527/detail?id=200
//传参:
router.push({
name:"Detail",
query:{
id:200
}
})
//接收
console.log(route.query.id)//200
6.3 router的一些常用方法:
//router对象其实就是对浏览器的history对象进行了二次封装
//1.返回上一级路由,常用在返回按钮上
router.back();
//2.返回指定层数的路由
router.go(-1);//返回上一层
router.forward()//前进路由
//注意:router.push()是相当于一直在压栈操作,所以适当的时候需要history对象来进行操作
7. 监听路由
在有些应用中,会在detail详情页做猜你喜欢模块,这种模块实质上是从detail页面跳转到本页面,只是动态参数id进行了改变。但是由于Detail组件已经挂在,mounted生命周期只会执行一次,mouted中我们有根据动态id进行接口的调用,这样的话,页面就不会再次执行mounted。
根据这个问题,官方给出了方案,就是监听路由
//发一:监听路由
watch(()=>route.params,()=>{
console.log("路由动态参数改变了",route.params.id);
//根据动态id进行接口的调用
getDetail(id);
})
//法二:路由守卫
const User = {
template: '...',
async beforeRouteUpdate(to, from) {
// 对路由变化做出响应...
getDetail(to.params.id);
},
}
8.路由模式
面试题:如何去除url中的#
vue-router提供的路由模式有两种:hash模式和history模式
hash:路径带#,发送请求不会带上#后的内容
history:让路径没有#,看起来更好看,但是需要后端配合。否则会出现打包之后刷新丢失。
原因:work.cs:3000/api/v1/home
因为打包之后,一刷新页面,就相当于在后端进行了发送请求资源,而后端没有home这个路径,所以就会报404,可以让后端做一些处理,来判断是前端路由还是后端资源,从而进行不同的处理。
import { createRouter, createWebHistory } from 'vue-router'
//引入组件
import HomeView from '../views/HomeView'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue');//路由懒加载
}
]
const router = createRouter({
history: createWebHistory(),//history模式
routes,
})
export default router
9. 全局路由守卫
全局前置路由守卫:
router.beforeEach() 在进入每个路由前都会进行拦截,通常会做一些权限的处理验证。
全局后置路由守卫:
router.afterEach() 在进入路由后进行的拦截,通常会做一些辅助功能,比如记录log和用户信息,修改网站标题等操作。
9.1 对所有的页面进行授权验证
// 全局拦截 - 后台系统,除了登录页面,其他页面都必须授权才能访问。
router.beforeEach( (to, from, next) => {
//获取token
let token =localStorage.getItem("token")
//不是login页面并且没有token,就返回登录页面
if (to.path !== '/login' && !token){
next("/login")
}else{
next() ;//放行
}
})
9.2 对部分页面进行授权验证
在一些网站中,只有部分页面需要进行权限的验证,其他也买你都是可以访问的。
这种情况我们通常有两种做法:
1.在beforeEach中将需要拦截的路径放入在一个数组中,判断去往的路径在不在这个数组中,在的话就进行验证,不在的话就放行.
2.借助vue-router的路由元信息进行判断。
// 全局拦截 - 某几个页面都必须授权才能访问。
let routes=[
{
path: "/center",
alias:"/wode",// 别名
component: Center,
meta:{
requiredAuth:true,//路由元信息--该路由是否需要验证
}
},
]
router.beforeEach( (to, from, next) => {
let token = localStorage.getItem("token");
//法一:放置数组
// var arr = ['/center','/card']
//法二:路由元信息
//如果不是跳转login页面,并且没有token,并且该路由需要验证
if (to.path !== '/login' && !token && to.meta.requiredAuth) next("/login")
else next()
})
10. 组件内的路由守卫
相当于在组件内部的路由的生命周期:
beforeRouteEnter(to,from) //进入前
beforeRouteUpdate(to,from) //路由更新前 一般都是自己跳自己(/detail的猜你喜欢)
beforeRouteLeave(to,from)//路由离开前
//在组件中写:
//进入当前路由前
async beforeRouteEnter(to,from,next){
let isAuthenticated = await localStorage.getItem("token")
if(isAuthenticated){
next()
}else{
next({name:"Login"})
}
}
//当前路由更新时:猜你喜欢
beforeRouteUpdate(to,from){
//当点击猜你喜欢后,之前的处理方式是监听路由变化,现在可以使用该钩子函数。
//需要注意的是:此时是更新前,this中携带的params.id是旧的,我们可以通过to来机型获取
let id=to.params.id;
log("拿着新id去请求接口数据")
}
//当前路由离开前:一般会做弹窗提示用户是否离开该页面
beforeRouteLeave(){
const answer = window.confirm("你确定要离开吗?")
if(!answer) return false
}
11. VCA中的路由守卫--组件内
在组合式API中,组件内的路由守卫,由于setup没有this,u在路由beforeRouteEnter的时候获取不到this,所以只有onBeforeRouteUpdate()和onBeforeRouteLeave()函数。
全局路由守卫没有改动。
<script setup>
onBeforeRouteUpdate((to,from)=>{
console.log("当前路更新");
})
onBeforeRouteLeave((to,from)=>{
console.log("当前路由离开")
})
</script>
注意:如果真的想使用beforeRouteEnter那么只能写setup()函数的形式了:
<script>
export default {
//setup函数
setup(){
//组件内路由守卫hooks
onBeforeRouteUpdate((to,from)=>{
console.log("当前路更新");
})
onBeforeRouteLeave((to,from)=>{
console.log("当前路由离开")
})
},
//setup同级
beforeRouteEnter(to,from,next){
consolo.log("当前路由进入")
}
}
</script>
12.在 setup
中访问路由和当前路由
因为我们在
setup
里面没有访问this
,所以我们不能再直接访问this.$router
或this.$route
。作为替代,我们使用useRouter
函数:
<script setup>
//引入hooks函数
import { useRouter, useRoute } from 'vue-router'
//使用hooks函数
const router = useRouter();//路由对象
const route = useRoute();//当前路由的信息
//访问动态路由参数
let id=route.params.id;
//访问query参数
let id=route.query.id;
//路由跳转
router.push("/home");
</script>
注意:
1.route
对象是一个响应式对象,所以它的任何属性都可以被监听,但是要避免监听整个route,应该直接监听你期望改变的参数