基础
前端路由
用来开发 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
},
{
// 当路径是/时,会切换到/home
path: "/",
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,
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`);
},
},