1.前言
上篇文章说了vue-router的路由的重定向,路由嵌套,命名路由以及路由的别名的使用,接下来再看看其他的用法
2.路由命名视图
有时候想同时 (同级) 展示多个视图,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了,如果 router-view 没有设置名字,那么默认为 default。还是用之前的例子,在home路由上添加一个sidebar视图,先修改下路由配置!
import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import Sidebar from '@/components/Sidebar'
Vue.use(Router)
export default new Router({
linkActiveClass: "active",
mode: 'history',
routes: [
{
path: '/',
name: 'home',
components: {
default: Home,
sidebar: Sidebar //添加一个sidebar视图
}
},
{
path: '/about',
component: About,
children: [
{
path: '',
name: 'work',
component: Work
},
{
path: 'company',
name: 'company',
component: Company
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs
}
]
},
{
path: '/help',
name: 'help',
alias: "/123",
component: Help
}
]
})
然后再对应的html中添加一个< router-view >,并且设置name为sidebar与组件Sidebar相对应,修改App.vue如下!
<template>
<div class="router-class">
<div>
<router-link to="/" exact>home页面</router-link>
<router-link to="/about" active-class="about-router">about页面</router-link>
<router-link to="/help" active-class="help-router">help页面</router-link>
</div>
<router-view name="sidebar" class="sidebar"></router-view>
<router-view class="default"></router-view>
</div>
</template>
<script>
export default {
name: "App"
}
</script>
<style>
.active {
background-color: yellow;
}
.about-router {
background-color: pink;
}
.help-router {
background-color: skyblue;
}
.sidebar {
width: 250px;
height: 2000px;
float: left;
border: 1px solid #aaa;
}
.default {
height: 2000px;
border: 1px solid #aaa;
}
</style>
可以看到home里面有2个同级的组件,并且2个组件分别对应自己的一个视图!
3.路由滚动行为
3.1 scrollBehavior函数
什么意思呢?当切换到新路由,你想保持之前的滚动位置,这个时候就会用到路由的滚动,它可以自定义路由切换时页面如何滚动!先来看下这个路由scrollBehavior滚动函数,我们来打印下这几个参数,看下到底是什么?
import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import Sidebar from '@/components/Sidebar'
Vue.use(Router)
export default new Router({
linkActiveClass: "active",
mode: 'history',
scrollBehavior (to, from, savedOPosition) {
//to为当前的路由对象
//from为上个路由对象
//savedOPosition 为路由的滚动条的位置
console.log(to, from, savedOPosition)
},
routes: [
{
path: '/',
name: 'home',
components: {
default: Home,
sidebar: Sidebar
}
},
{
path: '/about',
component: About,
children: [
{
path: '',
name: 'work',
component: Work
},
{
path: 'company',
name: 'company',
component: Company
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs
}
]
},
{
path: '/help',
name: 'help',
alias: "/123",
component: Help
}
]
})
to为当前的路由对象,from为上个路由对象,savedPosition 为路由的滚动条的位置,当我点击about页面时候,先打印name为wrok的路由(work是about的子路由,并且是直接匹配到),然后再打印name为home的路由对象,再点击home页面,输出和之前相反,并且两个点击savedPosition 都是为null(因为点击是不会触发savedPosition ,只能通过浏览器的前进/后退按钮才触发)
再看下我点击之后,滚动了滚动条({x:0,y:900})再后退,再前进,注意我下面的操作和打印出来的内容!
说明savedPosition可以记录路由的滚动坐标的位置,点击前进后退时候会记录值,那么我们可以改成这样:
export default new Router({
linkActiveClass: "active",
mode: 'history',
scrollBehavior (to, from, savedPosition) {
//如果有保存滚动坐标位置就回到这个位置
if (savedPosition) {
return savedPosition
} else {
//没有就回到顶部
return { x: 0, y: 0 }
}
}
//.......
})
再看来下效果,注意看滚动条位置:
3.2 hash定位
下面再来看下hash定位法,什么意思呢,可以利用scrollBehavior 方法可以直接定位到锚点(滚动到锚点),首先我们修改App.vue里面的help的path路径,让它指向一个锚点hash!
<template>
<div class="router-class">
<div>
<router-link to="/" exact>home页面</router-link>
<router-link to="/about" active-class="about-router">about页面</router-link>
<router-link to="/help#hash" active-class="help-router">help页面</router-link>
</div>
<router-view name="sidebar" class="sidebar"></router-view>
<router-view class="default"></router-view>
</div>
</template>
然后在help组件中去定义这个hash的位置,我把它定义到1000px位置
<template>
<div>
help
<div id="hash">我是锚点</div>
</div>
</template>
<script>
export default {
name: "Help"
}
</script>
<style>
#hash{
margin: 1000px auto;
}
</style>
最后修改路由的配置
export default new Router({
linkActiveClass: "active",
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
// behavior: 'smooth' 平滑滚动,滚动有个缓慢的过程
}
}
}
//.....
再来看下效果,当激活help路由直接跳转到hash的位置
3.3 异步滚动(promise)
当然你还可以返回一个 Promise 来得出预期的位置描述
export default new Router({
linkActiveClass: "active",
mode: 'history',
scrollBehavior (to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(
{ x: 0, y: 2000 }
)
}, 1000)
})
}
//....
当我刷新页面或者后退或者前进(不管之前在什么位置),过一秒之后都会自动跳到(滚动)到 { x: 0, y: 2000 }位置,看下面效果
4.动态路由匹配
比如有这么一个场景,要匹配用户信息的组件,有张三的信息,有李四的信息,有王二麻子的信息,难道我们要写3个用户信息组件吗?当然不需要,我们可以复用用一个组件(只是数据不一样,功能还是一样的),我们先来模拟下这3个用户的信息(后台请求回来的数据),每组信息都有个唯一的id,我们可以通过id不用来渲染不用的用户信息!
let data = [
{
id: "123",
name: "张三",
sex: "男",
age: 18,
sprot: "running",
skill: "javascript",
},
{
id: "345",
name: "李四",
sex: "男",
age: 28,
sprot: "footabll",
skill: "java",
},
{
id: "456",
name: "王二麻子",
sex: "女",
age: 20,
sprot: "swimming",
skill: "node.js",
},
];
首先我们来添加一个用户user组件, path: '/user/:id?', ?是一个正则的匹配,代表前面的字符最多只可以出现一次(0次、或1次),所以能匹配上path: '/user 或者path: '/user/:id
export default new Router({
linkActiveClass: "active",
mode: 'history',
routes: [
//...
{
path: '/user/:id?',
name: 'user',
component: User
}
]
})
然后再看下user组件,把之前模拟的data数据绑定到userList上,然后循环userList,当id不一样匹配不用的内容
<template>
<div>
<p>user</p>
<div v-for="list in userList" :key="list.id" class="user">
<router-link :to="'/user/'+list.id">{{ list.name }}
</router-link>
</div>
</div>
</template>
<script>
let data = [
{
id: "123",
name: "张三",
sex: "男",
age: 18,
sprot: "running",
skill: "javascript",
},
{
id: "345",
name: "李四",
sex: "男",
age: 28,
sprot: "footabll",
skill: "java",
},
{
id: "456",
name: "王二麻子",
sex: "女",
age: 20,
sprot: "swimming",
skill: "node.js",
},
];
export default {
name: "User",
data() {
return {
userList: data,
};
}
};
</script>
<style scoped>
.user {
display: inline-block;
margin: 20px;
}
.userInfo {
margin: 60px;
}
</style>
先来看下效果,当点击不同的用户,路径已经发生了改变
那么怎么让不同的用户显示不用的信息的,我们先来看下当前路由对象有什么东西!我们再created钩子函数里面打印下当前路由对象
export default {
name: "User",
data() {
return {
userList: data,
};
},
created() {
console.log(this.$route);//当前路由对象
},
};
路由对象表示当前激活的路由的状态结果,每次成功的导航后都会产生一个新的对象,下面分别解释下路由对象的属性信息。
fullPath字符串:包含查询参数和hash的完整路径
hash字符串:当前路由的hash值
matched数组:包含当前路由的所有嵌套路径片段的路由记录(假如我们目前的路由是为''/about/company'',matched匹配到的会是一个数组,包含 '/about',''/about/company',这两个path的路由信息)
meta对象:路由元信息 (用来判断当前路由是否需要进一步处理)
name字符串:当前路由的名称
params对象:动态路由参数
path字符串:对应当前的路由的路径
query对象:查询参数
下面我们会主要用到params对象和query对象,上图打印出来的params对象为'{id: '123'}', 那么是不是可以通过当前路由对象中的params对象获取到path中的':id',所以一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params上,获取到id后可以找出userList对象的id信息
<template>
<div>
<p>user</p>
<div v-for="list in userList" :key="list.id" class="user">
<router-link exact :to="{name: 'user',params: { id: list.id }}"
>{{ list.name }}
</router-link>
</div>
<div v-if="user" class="userInfo">
<p>姓名:{{ user.name }}</p>
<p>性别:{{ user.sex }}</p>
<p>年龄:{{ user.age }}</p>
</div>
</div>
</template>
<script>
let data = [
{
id: "123",
name: "张三",
sex: "男",
age: 18,
sprot: "running",
skill: "javascript",
},
{
id: "345",
name: "李四",
sex: "男",
age: 28,
sprot: "footabll",
skill: "java",
},
{
id: "456",
name: "小丽",
sex: "女",
age: 20,
sprot: "swimming",
skill: "node.js",
},
];
export default {
name: "User",
data() {
return {
userList: data,
user: {},
};
},
created() {
this.getUser(this.$route.params.id);
},
methods: {
getUser(id) {
let item = this.userList.find((use) => {
return use.id == id;
});
this.user = item;
},
},
};
</script>
<style scoped>
.user {
display: inline-block;
margin: 20px;
}
.userInfo {
margin: 60px;
}
</style>
执行之后发现当点击别的用户的时候,还是张三的数据,数据没有更新
因为三个路由都渲染同个组件,所以组件的生命周期钩子created函数不会再被调用,那么我们用watch来监听下
export default {
name: "User",
data() {
return {
userList: data,
user: {},
};
},
//监听当前路由的变化
watch: {
$route() {
this.getUser(this.$route.params.id);
},
},
created() {
this.getUser(this.$route.params.id);
},
methods: {
getUser(id) {
let item = this.userList.find((us) => {
return us.id == id;
});
console.log(this.user);
this.user = item;
},
},
};
或者使用的 beforeRouteUpdate 导航守卫(后面导航守卫会详细说)它的作用在当前路由改变,但是该组件被复用时,这个钩子函数会被调用
export default {
name: "User",
data() {
return {
userList: data,
user: {},
};
},
beforeRouteUpdate(to, from, next) {
//to:将要进入的目标 路由对象
//from: 当前导航正要离开的路由
this.getUser(to.params.id);
//执行next()进入当前导航
next();
},
created() {
this.getUser(this.$route.params.id);
},
methods: {
getUser(id) {
let item = this.userList.find((us) => {
return us.id == id;
});
console.log(this.user);
this.user = item;
},
},
};
用watch或者beforeRouteUpdate 导航守卫都可以得到一样的效果
如果每个用户还有其他的信息(sprot,skill),我们想通过这两个属性去查询用户的,怎么做?我们只需要从this.$route.query来获取查询对象就可以,原理和params一样,这里就不细说了,看下面代码修改
<template>
<div>
<p>user</p>
<div v-for="list in userList" :key="list.id" class="user">
<router-link exact :to="{name: 'user',params: { id: list.id }, query: { search: 'sprot' }}">{{ list.name }}
</router-link>
</div>
<div v-if="user" class="userInfo">
<p>姓名:{{ user.name }}</p>
<p>性别:{{ user.sex }}</p>
<p>年龄:{{ user.age }}</p>
</div>
<div v-if="user">
<router-link exact :to="{ path: '', query: { search: 'sprot' } }"
>sport</router-link
>
<router-link exact :to="{ path: '', query: { search: 'skill' } }"
>skill</router-link
>
</div>
<div class="userInfo">
<p v-if="$route.query.search == 'sprot'">运动: {{ user.sprot }}</p>
<p v-if="$route.query.search == 'skill'">技能:{{ user.skill }}</p>
</div>
</div>
</template>
<script>
let data = [
{
id: "123",
name: "张三",
sex: "男",
age: 18,
sprot: "running",
skill: "javascript",
},
{
id: "345",
name: "李四",
sex: "男",
age: 28,
sprot: "footabll",
skill: "java",
},
{
id: "456",
name: "小丽",
sex: "女",
age: 20,
sprot: "swimming",
skill: "node.js",
},
];
export default {
name: "User",
data() {
return {
userList: data,
user: {},
};
},
beforeRouteUpdate(to, from, next) {
this.getUser(to.params.id);
next();
},
created() {
this.getUser(this.$route.params.id);
},
methods: {
getUser(id) {
let item = this.userList.find((us) => {
return us.id == id;
});
console.log(this.user);
this.user = item;
},
},
};
</script>
<style scoped>
.user {
display: inline-block;
margin: 20px;
}
.userInfo {
margin: 60px;
}
</style>
看下面效果,仔细看上面的路径