基础
前端路由
用来开发 SPA(单页面应用)
单页面应用
- 整个应用只有一个完整页面,页面变化都是在这一个页面更新的
- 点击链接不会刷新整个页面,会局部更新,也会更新浏览历史(地址)
- 点击链接也不会发送请求,自己写 ajax 代码发送请求
前端路由的原理
- 点击链接不会刷新整个页面 --> 给 a 标签绑定点击事件,阻止其默认行为
- 会更新浏览历史(地址) --> 调用 history.push(path),就可以更新了
- 会局部更新 --> 内部会监听浏览历史的变化(history.listen(listener)),一旦发生变化就会遍历路由的所有配置,看当前路径(浏览地址)是否匹配上路由路径(path),匹配上就加载 component
vue-router 提供组件
<router-link>用来路由链接导航<router-view>用来显示当前路由组件
$route和$router
$route
route用来获取路由参数(params/query)和路径(path)
$router
用来编程式导航(push/replace/go/back/forward)

路由传参方式总结
params参数
- 路由配置
{path:"/xxx/:id", //:id 动态路由匹配,能匹配多个地址component:Xxx
}
- 跳转路由路径
<router-link to="/xxx/xxx/1">xxx</router-link>
- 子路由接收 :id的参数
this.$route.params.id // 当 :id 的参数发生变化时,需要使用watch监视属性的变化,来更新数据 watch: {$route: {handler(newVal) {const id = +newVal.params.id;this.message = this.messages.find((message) => message.id === id);},// 正常情况下,watch只有值发生变化的时候才会调用// 一上来会调用一次immediate: true,},},
query
- 路由链接设置
<router-link to="/xxx?brand='rolls'&engine='6.75'"></router-link>
- 子组件的获取
this.$route.query
props
-
将原先的params参数和query参数以props方式传递给组件
-
子组件路由配置
props(route) {return {...route.params,...route.query,};
},
- 子组件声明接收
props: ['id', 'name', 'age']
- 子组件使用
this.xxx
命名路由
- 路由配置,需要name属性
{name: "Detail", // 命名路由path: "detail/:id",component: Detail,
}
- 路由路径
<router-link:to="{name: 'Detail', // 跳转哪个命名路由params: {id: message.id,},query: {name: 'jack',age: 18,},}"
>xxx</router-link>
相同层级路由传递
- 传参
<router-view key="value"></router-view>
- 路由组件声明接收
props: ['key']
- 路由组件使用
this.xxx
最基础使用
配置
下载包
yarn add vue-router
views组件
- About和Home组件
<template><h1>About</h1>
</template><script>
export default {name: 'About',
};
</script><style>
</style>
src下定义router文件夹
// 因为要安装到Vue上面
import Vue from "vue";
import VueRouter from "vue-router";// 引入组件
import About from "../views/About/index.vue";
import Home from "../views/Home/index.vue";// 安装插件
// 一旦安装插件,就会给全局注册两个组件:router-link router-view
// 还会给原型上添加一个属性
Vue.use(VueRouter);const router = new VueRouter({// 定义路由的配置routes: [{path: "/about", // 路由路径component: About // 路由组件},{path: "/home",component: Home},{// 当路径是/时,会切换到/homepath: "/",redirect: "/home" // 重定向}]
});export default router;
main.js应用路由
import Vue from "vue";
// 引入路由
import router from "./router/index";
import App from "./App.vue";Vue.config.productionTip = false;new Vue({render: h => h(App),// 应用路由router
}).$mount("#app");
app.vue显示
- router-link 路由链接导航
切换地址 - router-view 显示对应的路由组件
内部会根据当前的地址,遍历路由中routes配置,找到相应的组件显示
<template><div class="container"><h1>Router Page</h1><div class="row"><div class="col-md-4"><ul class="nav nav-pills nav-stacked"><li><router-link to="/about">About</router-link></li><li><router-link to="/home">Home</router-link></li></ul></div><div class="col-md-8"><!-- 显示对应的路由组件 --><router-view></router-view></div></div></div>
</template><script>
export default {name: 'App',
};
</script><style>
</style>

嵌套路由
新建子组件

<template><h2>Message</h2>
</template><script>
export default {name: 'Message',
};
</script><style>
</style>
引入组件,配置路由
import Message from '../views/Home/Message/index.vue'
import News from '../views/Home/News/index.vue'{path: "/home",component: Home,// 子路由,即使只有一个值也应该是数组children: [{path: "/home/message",component: Message},{// 当路径不是 / 开头,就会已父路由路径补全,也是一种简写// 注意,简写一定不要再加 /path: "news",component: News}]
},
home组件显示
<template><div><h1>Home</h1><ul class="nav nav-tabs"><li><router-link to="/home/message">message</router-link></li><li><router-link to="/home/news">news</router-link></li></ul><router-view></router-view></div>
</template>

路由传参
params参数
message添加元素
- 效果图

<template><div><h2>Message</h2><ul><li v-for="item in messageData" :key="item.id"><!-- to后面必须跟字符串,而:绑定以后字符串才会被当做js去解析 --><router-link :to="`/home/message/detail/${item.id}`">{{item.content}}</router-link></li></ul><router-view></router-view></div>
</template><script>
export default {name: 'Message',data() {return {// 定义一个假数据messageData: [],};},// 模拟请求数据mounted() {setTimeout(() => {this.messageData = [{ id: 1, content: 'message01' },{ id: 2, content: 'message02' },{ id: 3, content: 'message03' },];}, 1000);},
};
</script><style>
</style>

创建Deail
<template><ul><li>id:{{ chilData.id }}</li><li>name:{{ chilData.name }}</li><li>content:{{ chilData.content }}</li></ul>
</template><script>
export default {name: 'Detail',data() {return {baseData: [{ id: 1, name: 'name111', content: 'content111' },{ id: 2, name: 'name222', content: 'content222' },{ id: 3, name: 'name333', content: 'content333' },],chilData: {},};},watch: {$route: {handler(newVal) {const id = +newVal.params.id;this.chilData = this.baseData.find((item) => item.id === id);},// 正常情况下,watch只有值发生变化的时候才会调用// 一上来会调用一次immediate: true,},},
};
</script><style>
</style>
动态路由配置
import Detail from "../views/Home/Message/Detail/index.vue";{path: "/home",component: Home,children: [{path: "/home/message",component: Message,children: [{// 动态路由,能够匹配多个路由,简写,省略 /path: "detail/:id",component: Detail}]},{path: "news",component: News}]
},

Detail
- 在其this原型链上有一个$route
mounted() {console.log(this);},
<template><div><ul><li>{{ showData.id }}</li><li>{{ showData.brand }}</li><li>{{ showData.color }}</li></ul></div>
</template><script>
export default {name: 'Detail',data() {return {detailData: [{ id: 1, brand: '劳斯', color: 'green' },{ id: 2, brand: '宾利', color: 'black' },{ id: 3, brand: '法拉利', color: 'red' },],showData: {},};},// mounted() {// console.log(this);// },watch: {// 监视this原型链行的$route$route: {handler(newRouder) {// console.log(newRoder); 见图const id = +newRouder.params.id;this.showData = this.detailData.find((item) => item.id === id);},// 正常情况下,watch只有值发生变化的时候才会调用// 一上来会调用一次immediate: true,},},
};
</script><style>
</style>

- wantch监视结果

immediate
正常情况下,watch只有值发生变化的时候才会调用
增加这个属性一上来会调用一次
immediate: true,

watch监视 属性官方文档

done

query
路由链接设置
<template><div><h2>Message</h2><ul><li v-for="item in messageData" :key="item.id"><!-- query方法就是在之前基础上增加查询字符串 --><router-link:to="`/home/message/detail/${item.id}?brand='rolls'&engine='6.75'`">{{ item.content }}</router-link></li></ul><router-view></router-view></div>
</template>
子组件获取
this.$route.query

props
路由传递
<!-- 将原先的params参数和query参数以props方式传递给组件 -->
<router-link:to="`/home/message/detail/${item.id}?brand='rolls'&engine='6.75'`">{{ item.content }}</router-link
>

#3# 子组件配置
{path: "detail/:id",component: Detail,// 增加props方法props(routeQuery) {// console.log(routeQuery);return {...routeQuery.params,...routeQuery.query}}}
子组件声明接收
-没有props接收,可以在$attrs上面看到,也可以在组件挂载的时候打印this查看

- props接收了,$attrs就没有了
watch: {$route: {handler(newRouder) {const id = +newRouder.params.id;this.showData = this.detailData.find((item) => item.id === id);},immediate: true,},},// props接收props: ['id', 'brand', 'engine'],
3
this使用
this.xxx
命名路由
- 路由取个名字
路由传递
<template><div><h2>Message</h2><ul><li v-for="item in messageData" :key="item.id"><!-- 是个对象,有个name属性,就是要去的组件名称,在路由中配置 --><router-link:to="{name: 'Detail',params: {id: item.id,},query: {brand: 'rolls',engine: 6.75,},}">{{ item.content }}</router-link></li></ul><router-view></router-view></div>
</template>
路由配置
{path: "/home/message",component: Message,children: [{// 名称,和组件名称一样,一定要有name: "Detail",path: "detail/:id",component: Detail,props(routeQuery) {return {...routeQuery.params,...routeQuery.query};}}]
},
接收
- 同上面一样,可以props接收

相同层级组件传递
- 给相同层级的路由组件一起传递公共参数传参
传参
<template><div class="container"><h1>Router Page</h1><div class="row"><div class="col-md-4"><ul class="nav nav-pills nav-stacked"><!-- 给相同层级的路由组件一起传递参数 --><li><router-link to="/about">About</router-link></li><li><router-link to="/home">Home</router-link></li></ul></div><div class="col-md-8"><!-- 这样去传参,只要显示哪个组件,就给哪个组件传参 --><router-view rolls="royce"></router-view></div></div></div>
</template>
路由组件声明接收
<template><h1>About</h1>
</template><script>
export default {name: 'About',// 接收公共组件数据props: ['rolls'],
};
</script><style>
</style>
路由组件使用
- this直接使用即可

命名视图
命名路由
缓存路由组件
组件卸载与加载
- 一个组件加载,另一个组件会卸载,这样性能不哈


路由组件缓存

<!-- app组件,要缓存哪个路由,就要在外面包一个keep-alive --><keep-alive include="Home"><router-view rolls="royce"></router-view></keep-alive>
- 缓存了,所以还能看到

- 没缓存,直接看不到

缓存多个 include
数组也可以
<keep-alive include="Home,About">


排除法 exxlude
<keep-alive exclude="About">

max 最多缓存数量
<keep-alive exclude="About" max="1">
缓存组件问题
<keep-alive exclude=""><router-view rolls="royce"></router-view>
</keep-alive>
// 这两个声明周期函数只针对keep-alive缓存的组件触发// 已激活,在显示activated() {console.log('About activated ');},// 未激活deactivated() {console.log('About deactivated');},
- 优点:性能好,不会重新发请求
- 缺点:数据一致是旧的
== 缓存起来以后不会再走mounted,以前发送请求都在mounted发,但是现在缓存起来了不会再走mounted,不会再请求最新数据,不能保证数据最新的 ==
<template><h1>About</h1>
</template><script>
export default {name: 'About',props: ['rolls'],mounted() {console.log('about mounted');},beforeDestroy() {console.log('about beforeDestroy');},// 已激活,在显示activated() {console.log('About activated ');},// 未激活deactivated() {console.log('About deactivated');},
};
</script><style>
</style>

路由跳转
<router-link to=""
- 路由链接导航
- 如果点击链接或者按钮只需要进行路由跳转,
- 例如:导航链接
编程式导航
- 编程式导航
- 如果点击链接或者按钮需要做一些其他事,再进行路由跳转,例子:登录按钮、修改按钮
push()
replace()
back()
forward()
<template><div><h2>Message</h2><ul><li v-for="item in messageData" :key="item.id"><!-- 是个对象,有个name属性 --><router-link:to="{name: 'Detail',params: {id: item.id,},query: {brand: 'rolls',engine: 6.75,},}">{{ item.content }}</router-link><!-- 增加按钮,可以回退 --><button @click="push(item.id)">push</button><!-- 替换,不可以回退 --><button @click="replace(item.id)">replace</button></li></ul><!-- 后退,简写 --><button @click="$router.back()">goBack</button><!-- 前进,简写 --><button @click="$router.forward()">goForward</button><router-view></router-view></div>
</template>
methods: {// 编程式导航// 添加push(id) {// console.log(this);this.$router.push(`/home/message/detail/${id}?brand=rolls&engine=675`);},// 替换replace(id) {this.$router.replace(`/home/message/detail/${id}?brand=rolls&engine=675`);},},
















