简介
主要介绍了vue路由的基本方方面面,本来向拆开讲的,后来发现代码是循序渐进的,所以就写在一起了。
路由的概念
何为路由:路由的本质是一组组key-value的对应关系,多个路由由路由器管理。
编码世界中的路由主要分为两类:
- 后端路由:
- key是路径,value是函数方法(function),用于根据不同的路径,使用不同的函数处理用户提交的请求。
- 工作流程是,后端服务器收到一个请求,路由器会根据请求的路径,在路由规则中找到匹配的函数来处理请求,返回数据给用户。
- 前端路由:
- key是路径,value是组件(component)。
- 工作流程是:路由器监测到路径的变化,然后根据不同的路径展示不同的组件,达到页面的多样化。
Vue中的路由也是一组组key-value的对应规则,称为路由规则,由路由器统一管理。
在Vue中,路由的key是路径,value是component组件,也就是Vue中的路由器router会随时监测到页面路径的变化,然后会根据设置好的路由规则(route),展示不同的组件(value)。
生活中的路由和路由器主要是负责网络的分发和数据的发送,而Vue中的路由和路由器主要负责的功能是单页面应用(SPA)的路由跳转。
单页面应用(SPA — Single Page Web Application):
- 整个应用只有一个完整的页面。
- 点击页面中的导航链接不会刷新页面,只会做页面的局部刷新,
- 数据需要通过ajax进行发送请求获取。
也就是应用中由始至终都只有一个html页面,页面通常分为导航区和展示区,点击导航区会导致展示区的变动,实际来来回回也就只有一个页面。
比较典型的例子就是很多后台管理系统都很适合做成单页面应用。
现实中,美团生活的页面也是一个单页面应用。
https://gz.meituan.com/shenghuo/
总的来说:路由就是一组对应关系。
Vue路由的基本使用
要在vue中实现路由,一个大前提是要按照vue的路由器 vue-router,是vue的一个插件库,专门用于实现SPA应用。
这里要说明的是,在2022年2月7日以后,vue-router的默认版本变成了4,之前是3,并且vue-router4只能在vue3中使用,而vue-router3可以在vue2中使用,如果强行把vue-router4安装在vue2中,会报如下错误。
所以2022年2月7日之后,vue2的项目安装vue-router需要指定vue-router的版本,最终过的命令是:
npm i vue-router@3
安装相关插件之后,就可以开始使用vue路由,
主要分为以下几步:
- 创建路由组件,也就是上图中的About.vue和Home.vue,通常路由组件创建在pages文件夹下,一般组件创建在components文件夹写。
About.vue:
<template>
<h2>这是About组件的内容</h2>
</template>
<script>
export default {
//定义组件的名称
name:"About"
}
</script>
<style>
</style>
Hom.vue
<template>
<h2>这是Home组件的内容</h2>
</template>
<script>
export default {
//定义组件的名称
name:"Home"
}
</script>
<style>
</style>
- 创建路由器并暴露。
通常是用项目目录下的router文件夹下创建一个index.js文件进行创建路由器并暴露。
router/index.js
//这里用于创建一个路由器,通常一个应用只需一个路由器
//引入路由器插件
import VueRouter from "vue-router";
//引入需要的路由组件About和Html
import About from "../pages/About.vue"
import Home from "../pages/Home.vue"
//创建一个路由器,使用过的是VueRouter构造函数。
//传入一个配置对象
const router = new VueRouter({
//这里就是定义一个个的路由规则
//是一个数组,里面每个元素是一个对象,也就是一个路由
routes:[
{
//路径,也就是key
path:"/about",
//组件,也就是value
component:About
},
{
path:"/home",
component:Home
}
]
})
export default router;
- 使用vue-router插件,并在创建vue实例时,把路由器配置进去,这个步骤通常在入口文件main.js中完成。
main.js:
//引入vue依赖
import Vue from 'vue'
//引入组件App
import App from './App.vue'
//引入vue-router插件依赖
import VueRouter from "vue-router"
//引入一个路由器,从刚刚创建的那个index.js文件中
import router from "./router/index.js"
// 关闭生产提示
Vue.config.productionTip = false
//使用路由插件
Vue.use(VueRouter)
//创建一个vue实例
new Vue({
render: h => h(App),
//为该vue实例配置路由器,key必须是router
router:router
//配置该vue实例管理id为app的容器
}).$mount('#app')
- 使用路由,这个实在App组件的template区配置好导航区和展示区。
App.vue:
<template>
<!-- 编写结构 -->
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- //这里不能使用a标签进行跳转,要使用vue-router插件提供的 router-link 标签,虽然最终该标签还是会转化为a标签,
//但是vue-router插件只认该标签。
//1. to属性:表示我点击该标签,要跳转的路径。
//2. active-class属性,表示当页面处于该标签时,也就是当前页面的路径时该标签to属性配置的路径时,使用某个class
// 下面的active类使用的是bootstrap.css的类。 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<!-- 组件展示区,要使用vue-router插件提供的标签 router-view标签,起一个占位左右,到时路由器
根据路由匹配到的组件会展示在这个地方。 -->
<router-view></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:"app"
}
</script>
至此,vue路由的基本使用步骤已经完成,最后,看下单页面应用的单页面index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面。 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端理想窗口 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 引入页签 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 引入bootstrapp.css依赖 -->
<link rel="stylesheet" href="./css/bootstrap.css">
<!-- 配置网页标题 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- //这个标签是,当浏览器不支持JavaScript语法的时候就渲染里面的内容,现实中几乎不存在这种情况。 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- //容器结构 -->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
使用npm run serve进行启动项目,并访问:
几个注意点
最后,讲几个注意点:
- 路由组件通常放在pages文件夹下,一般组件通常放在components文件夹下。
- 通过导航链接进行路径的切换导致组件的切换,默认会导致组件的销毁和挂载,被切换的组件会销毁,切换的组件会挂载,比如上面例子,从About导航切换到Home导航后,About组件会被销毁,Home组件会被挂载。
验证:往两个组件分别添加mounted和destroyed生命周期函数。
效果:
- 每个组件都有自己的$route对象,代表这个组件的路由信息,所有组件共享一个$router对象。
验证:
分别在两个组件的mounted函数打印这个两个对象。
结果:
验证$route的唯一性和$router的共享性:
验证方式是,先在About组件中的mounted函数里面,把两个对象存一份到window对象里面,然后在Home组件的mounted里面直接比较。
效果:
嵌套路由
嵌套路由也叫多级路由,指的是在一个路由里面设置子路由。
新加两个组件。
Message.vue:
<template>
<div>
<ul>
<li>
<a href="/message1">message001</a>
</li>
<li>
<a href="/message2">message002</a>
</li>
<li>
<a href="/message/3">message003</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name:"messages"
}
</script>
<style>
</style>
News.vue:
<template>
<div>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</div>
</template>
<script>
export default {
name:"news"
}
</script>
<style>
</style>
router/index.js做了修改,加了如下的东西
//嵌套路由,使用children属性配置,是一个数组
children:[
//里面配置子路由,配置规则于一级路由基本一样,只是,它的path配置时,开头不用加/
{
path:"messages",
component:Messages
},
{
path:"news",
component:News
}
]
Home组件的结构也修改了
<template>
<div>
<h2>这是Home组件的内容</h2>
<div>
<ul class="nav nav-tabs">
<li>
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
</li>
<li>
<router-link class="list-group-item" active-class="active" to="/home/messages">Message</router-link>
</li>
</ul>
<div>
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
//定义组件的名称
name:"Home",
mounted() {
//直接比较
console.log("route是否相等:",this.$route === window.aboutRoute);
console.log("router是否相等:",this.$router === window.aboutRouter);
}
}
</script>
<style>
</style>
效果:
总结:
嵌套路由由三个要点:
- 嵌套路由使用路由对象的children属性配置,是一个数组,里面配置与路由的配置基本一样。
- 嵌套路由的路径配置开头不能加斜杠/。
- 嵌套路由的使用时,route-link标签的to属性的值要用全路径,比如/home/messages,要把所有上级路由的路径都带上。
命名理由
命名路由指的是在配置路由规则时,可以通过name属性给路由进行命名,这个名称通常要唯一。
比如给news组件路由规则添加一个名称叫做xinwen。
然后在Home组件使用该路由时,需要使用v-bind来绑定to属性,里面表达式时一个对象,指定对象的name属性为news路由的名称。
这样可以在路径很长时,使用name代替,简便写法,并且还有一些特殊的功能,后面在介绍。
query参数和params参数:
组件的值可以使用query和params进行传参,query参数就是路径中的问好后面指定的参数,比如?name=123&age=18,而params时通过路径参数进行传递。
query:
新增一个MessageDetails组件,是在Messages组件中展示的路由子组件。
MessageDetails.vue:
<template>
<div>
<div>消息id:{{id}}</div>
<div>消息内容:{{content}}</div>
<div>a:{{a}}</div>
<div>b:{{b}}</div>
</div>
</template>
<script>
export default {
name:"messageDetails",
// props:["a","b"]
props:["id","content","a","b"]
}
</script>
<style>
</style>
配置路由规则:
由两种方式在route-link中配置对应的参数进行传参。
一种是直接像url那样传参。
这种由两个缺点:
- url书写不够简洁。
- 只能传递固定死数据。
接收这些传递的参数,使用vue组件实例的$route.query.参数名进行接收。
第二种是使用对象进行传递:
这种方式的优势是,path只需写路径,然后传递的query参数都在query配置属性中配置,并且可以使用变量,不用进行数据的写死。
params
params是靠路径变量参数进行传递的。
首先要在路由的时候进行路径变量的占位符设置。
然后在使用时,在属性中使用params进行配置。
有一点要注意的是,当配置了路径变量占位符后并使用params属性来传递相应参数,route-link的配置中,只能用name属性进行路由的标志,而不能用path。
获取参数:
上面的参数获取中,都是使用$route.query.xxx 或者 $route.params.xxx,很长,很不方便,所以有一种优化方法,就是在路由规则中配置props属性,然后组件也用props配置进行接收,这种配置由三种方式。
然后组件使用props接收。。
第二种方法是直接传递一个布尔值。
这种方式的优点是可以在route-link中使用params属性配置对应的参数,不用写死,但是缺点是这种方式只能传递params参数,不能传递query参数。
第三种方式,使用函数的形式。
route-link的replace的属性:
route路由跳转的模式有两种,第一种是push模式,默认的模式,类似于入栈,第二种是replace模式,是替换。
先说说push模式:
每当进行一次切换,就会把历史记录入栈。
replace模式:
会替换掉当前的历史记录,假设我们把about和messages的模式设置为replace,对比上面push模式,得到的replace模式的栈情况如下:
只需要一个属性进行配置,该属性就是replace。
编程式路由导航
顾名思义,就是使用使用编程的方式,来代替route-link的声明式路由导航,在一些情况下需要使用编程式路由导航,比如route-link到最后生成的是a标签,而我想要要按钮、图片等元素中使用导航,就不能使用route-link,或者我要在跳转前做一些逻辑,route-link也实现不了,主要靠$router对象进行跳转。
$router还有三个算比较常用的api:
back:后退一步。
forward:前进一步。
go(数字n):正数前进n步,负数后退n步。
缓存式路由组件和两个新的生命周期钩子:
上面讲到进行路由切换时,对应的组件会销毁和挂载,如果需要使得组件不切换和挂载,需要把route-view配置到keep-alive标签里面。
两个新的生命周期钩子:
路由守卫
通俗点就是拦截器,有三种:
全局前置路由守卫
全局+前置,全局指的是所有路由都生效,前置是指在路由进行跳转签执行,可以做一些权限校验。
这里说一个路由的配置,就是meta属性,可以配置一些用户想要携带的元数据,会在$route.meta对象中出现,类似上面,比如可以配置一个isAuth配置,当auth为true,说明该路由需要进行权限验证,那么就可以在全局前置路由守卫里面根据该元数据判断是否需要进行路由权限校验。
全局后置路由守卫
全局+后置,全局表示会在所有路由中生效,后置指的是跳转后生效。
独享路由守卫
也就是配置在组件路由中的,只属于这个路由的的路由守卫。
组件内路由守卫:
也就时在组件内配置的路由守卫。