vue-router
一、简述
理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
前端路由:key是路径,value是组件。
安装vue-router
// vue2.x == vue-router 3.x vue3.x ==> vue-router 4.x
yarn add vue-router
路由的两种模式:
hash:监听url中hash值的改变,也就是使用href的锚点来改变location.href属性
<a href="#/home">home</a>
<a href="#/about">about</a>
<div class="container">
</div>
<script>
const contentEl = document.querySelector('.container');
window.addEventListener('hashchange', () => {
let content = '';
const hash = window.location.hash.slice(1);
switch (hash) {
case '/home':
content = '<h1>home</h1>';
break;
case '/about':
content = '<h1>about</h1>';
break;
default:
content = '<h1>404</h1>';
break;
}
contentEl.innerHTML = content;
});
</script>
history模式:使用h5新出的api完成url的改变,而不刷新页面
<a href="/home">home</a>
<a href="/about">about</a>
<div class="container">
</div>
<script>
const contentEl = document.querySelector('.container');
const aEls = document.getElementsByTagName('a');
for (let aEl of aEls) {
aEl.addEventListener('click', function (e) {
e.preventDefault();
const path = this.getAttribute('href');
// 修改url,而不刷新页面
// history.pushState({},'',path) // push,可popstate
history.replaceState({},'',path) // replace,不可popstate
switch (location.pathname) {
case '/home':
contentEl.innerHTML = 'home';
break;
case '/about':
contentEl.innerHTML = 'about';
break;
default:
contentEl.innerHTML = '404';
}
})
}
</script>
二、基本路由
1.基本使用
main.js文件中配置
import VueRouter from 'vue-router'
import router from './router'
Vue.use(VueRouter);
new Vue({
render: h => h(App),
router
}).$mount('#root')
创建src/router/index.js文件并配置
import VueRouter from 'vue-router';
import Home from '../components/Home';
import About from '../components/About';
const routes = [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About,
meta: { // 配置路由的元数据,自定义数据配置在这里
isAuth: true,
title:'消息'
}
},
]
export default new VueRouter({
routes
})
在页面中使用路由标签
<!-- 原链接
<a class="list-group-item active" href="./about.html">About</a>
<a class="list-group-item" href="./home.html">Home</a> -->
<!-- 路由链接 router-link 后续会渲染成a标签 -->
<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>
<!-- active-class 激活选中时添加的类名 -->
<!-- 路由组件展示的位置 -->
<router-view></router-view>
router-link标签属性:
2.注意点
1. 路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹。
2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3. 每个组件都有自己的 $route 属性,里面存储着自己的路由信息。
4. 整个应用只有一个router,可以通过组件的 $router 属性获取到。
3.路由懒加载
使用webpack的分包,建议所有路由都使用懒加载:
import { createRouter,createWebHistory} from 'vue-router'
const routes = [
{
path: '/',
redirect:'/home'
},
{
path: '/home',
name: 'home',
// magic comment 魔法注释,分包后的名字
component: ()=>import(/* webpackChunkName:"home-chunck" */'../views/Home.vue')
}
]
4.router-link的v-slot
可以决定router-link到底渲染成什么元素,我们需要使用custom表示我们整个元素要自定义
如果不写,那么自定义的内容会被包裹在一个 a 元素中
使用:
<router-link to="/home" v-slot="props" custom>
<!--
custom: 取消a标签的包裹
props: href 跳转的连接
props: route route对象
props: navigate 导航函数
props: isActive 是否当前处于活跃状态
props: isExactActive 是否当前处于精确活跃状态
-->
<button>{{ props.route}}</button> <!-- 点击没效果了就 -->
<button @click="props.navigate" :class="props.isActive?'custom_active':''">toHOme</button>
</router-link>
5.router-view的v-slot
使用router-view的v-slot可达到路由页面切换时,有动画的效果,依赖于依赖于动态组件
使用用于 和 组件来包裹你的路由组件,
作用域插槽的传递的参数:
Component:要渲染的组件;
route:解析出的标准化路由对象
综合使用:
<div class="container">
<router-view v-slot="{ Component }">
<transition name="zpj" appear mode="out-in">
<keep-alive max="5">
<component :is="Component"></component>
</keep-alive>
</transition>
</router-view>
</div>
<style>
@keyframes show {
0% {
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
opacity: 1;
}
}
.zpj-enter-active {
transform-origin: left top;
animation: show 0.3s;
}
.zpj-leave-active {
transform-origin: left top;
animation: show 0.3s reverse;
}
.container {
overflow: hidden;
}
</style>
6.动态添加路由
在生成路由时,时动态添加的
使用:
// 添加顶级路由
router.addRoute({
path: "/dynamic",
name: "dynamic",
component: () => import("../views/DynamicComponent.vue"),
});
// 添加二级路由
router.addRoute('home',{
path:'/home/moment',
name:'moment',
component:()=>import('../views/Home/Moment.vue')
})
/**
* 动态删除路由
* 方法一: 添加一个与已经有路由的相同name的路由
* 方法二: 删除一个已经存在的路由,传入路由的name
* 方法三: 执行添加路由方法返回的函数
*/
const removeThisRoute = router.addRoute({path:'/home/time',name:'moment',component:()=>import('../views/Home/Moment.vue')}) // 方法一
router.removeRoute('dynamic'); // 方法二
removeThisRoute(); // 方法三
/**
* router方法
* hasRoute(name) 检查路由是否存在
* getRoutes() 获取所有路由表
*/
三、嵌套路由
配置路由规则,使用children配置项:
const routes = [
{
path:'/',
redirect:'/home'
},
{
path: '/home',
component: Home,
children:[ //通过children配置子级路由
{
path: "",
redirect: "/home/message",
},
{
path:'news', //此处一定不要写:/news
component:News
},
{
path:'message',
component:Message
}
]
},
{
path: '/about',
component: About
},
]
跳转(要写完整路径):
<!-- 写完整路径 -->
<router-link to="/home/news">News</router-link>
四、路由传参
1.query参数
传递参数:
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/detail?id=${message.id}&title=${message.title}`"> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:message.id,
title:message.title
}
}">
{{ message.title }}
</router-link>
接收参数:
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
2.命名路由
简化路由的跳转
给路由命名:
{
name:'shouye', // 路由命名
path: '/home',
component: Home,
children: [
{
path: 'news',
component: News
},
{
path: 'message',
component: Message,
children: [
{
name:'xiangqing', // 路由命名
path: 'detail',
component: Detail
}
]
}
]
},
简化路由跳转:
<!--简化前,需要写完整的路径 -->
<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>
3.params参数
传递参数:
<!-- 跳转路由并携带params参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/detail/${message.id}/${message.title}`"> -->
<!-- 跳转路由并携带params参数,to的对象写法,必须使用路由命名 -->
<router-link :to="{
name:'xiangqing',
params:{
id:message.id,
title:message.title
}
}">
{{ message.title }}
</router-link>
接收参数:
// 路由配置
{
name:'xiangqing',
path: 'detail/:id/:title', // 占位符声明接收
omponent: Detail
}
// 在组件中使用
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
4.路由的props配置
让路由组件更方便的收到参数
{
name:'xiangqing',
path: 'detail/:id/:title',
component: Detail,
// props的第一种写法,值为对象,该对象中的所有ke-value都会以props的形式传给Detail组件
// props:{
// a:1,
// b:'hello'
// }
// props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,一props的形式传递给Detail组件
// props:true
// props的第三种写法,值为函数,参数为route,可以同时处理query参数与params参数
props($route){
console.log($route);
return {
...$route.params
}
}
}
5.<router-link>
的replace属性
简述:
控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为 push 和 replace , push 是追加历史记录, replace 是替换当前记录。路由跳转时候默认为 push
切换跳转模式:
<router-link replace .......>News</router-link>
6.编程式路由导航
不借助<router-link>
实现路由跳转,让路由跳转更加灵活
编码:
this.$router.push({
name: "xiangqing",
params: {
id: m.id,
title: m.title,
},
});
this.$router.replace({
name: "xiangqing",
params: {
id: m.id,
title: m.title,
},
});
this.$router.forward();
this.$router.back();
this.$router.go(-2)
7.缓存路由组件
作用:让不展示的路由组件保持挂载,不被销毁。
编码:
<!-- 缓存全部 -->
<!-- <keep-alive> -->
<!-- 缓存一个 -->
<!-- <keep-alive include="News"> -->
<!-- 缓存多个 -->
<keep-alive :include="['News', 'Message']">
<router-view></router-view>
</keep-alive>
8.路由钩子actived
与deactived
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
编码:
activated() { // 组件激活
this.timer = setInterval(() => {
this.opacity -= 0.1;
console.log('jkfdsjk')
if (this.opacity < 0) this.opacity = 1;
}, 16);
},
deactivated(){ // 组件失活
clearInterval(this.timer);
}
9.路由守卫
a.前置路由守卫
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
// router 4.x之后不推荐使用next了,根据返回值决定是否跳转,没有返回值时,可以跳转
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'JIang'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
router 4.x之后使用:
/**
* 返回值:
* 1.返回false,取消默认导航
* 2.返回一个对象,指定路由跳转 {paht:'/home/message',params:{id:123},query:{name:'张三'}}
* 3.返回一个字符串,指定路由跳转
* 4.不返回或者undefined:进行默认导航
*/
router.beforeEach((to, from)=>{
return true;
})
b.独享路由守卫
进入路由组件之前,的守卫
beforeEnter: (to, from, next) => {
if (to.meta.isAuth) {
if (localStorage.getItem('school') == 'JIng') {
next();
} else {
alert('学校名称不对,无权查看');
}
} else {
next();
}
}
c.组件内路由守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
10.路由器工作模式
- hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
- history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
- 在node服务器中可使用 connect-history-api-fallback 中间件解决
修改路由器工作模式:
const router = new VueRouter({
routes,
mode:'hash'
});