一、vue router介绍
Vue Router是Vue.js官方的路由器,页面间的跳转(比如A->B->C)是一组路由,而Vue Router是管理这些路由的集成器。Vue Router主要有以下功能:
1、模块化的配置
2、路由的参数传递,传递
3、通过各种钩子实现导航的控制
4、视图间的过渡动画
下面我们将用实例介绍这些功能。参考百度的"最新大片"搜索结果,实现如下效果:
二、实现基本框架
所谓饭要一口一口吃,我们先把架子搭起来,实现下面的效果
1、创建工程
用手脚架创建一个项目工程(参考VUE探索第二篇-手脚架)。创建完毕后,默认的目录如下:
2、创建页面组件
我们来分析下这个页面,顶部有三个tab标签,点击每个tab标签展示不同子页面,但是页面的布局是一样的,也就是说这三个子页面其实是可以复用的。
在components目录创建内容子页面组件Content.vue(可参考HelloWorld.vue)。
<template>
<div class="hello">
<!--$route.params.id用来接收路由传递的参数-->
这是内容页面:{{$route.params.id}}
</div>
</template>
<script>
export default {
//该页面组件命名为Content
name: 'Content',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
暂不考虑页面展示细节,页面非常简单,大家注意两个地方:
{{$route.params.id}},用来接收路由传递的参数,区别不同的tab跳转。类似jsp中request.getParameter()
name: 'Content',命名该页面组件为Content。
3、创建Tab标签
修改App.vue,如下:
<template>
<div id="app">
<!--tab-->
<router-link to="/Content/hot" >最热电影</router-link>
<router-link to="/Content/new"> 最新电影</router-link>
<router-link to="/Content/like" >用户好评</router-link>
<!--子页面的插槽-->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<router-link>,是路由的导航组件,to属性表示指定的链接地址,该标签会被渲染成<a>标签,这里配置了不同的目标地址。
<router-view>,路由匹配的组件页面将渲染在这里(即Content.vue),可以认为是个插槽。
4、配置路由
子页面组件和tab标签页面已经实现了,但是目前还无法运行,因为系统还不知道tab标签的目标地址与子页面之间的映射关系。
下面我们来配置router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Content from '@/components/Content'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Content',
component: Content,
redirect:'/Content/hot'
},
{
path: '/Content/:id',
name: 'Content',
component: Content
}
]
})
path,表示是路由的匹配路径,其中:id表示路由的参数(:开头为参数)。本例中tab标签的三个目标链接指向同一个路由,id参数值分别是hot,new,like。
模式 | 匹配路径 | $route.params |
---|---|---|
/Content/:id | /Content/hot | {id:hot} |
/Content/new | {id:new} | |
/Content/like | {id:like} |
在目标页面中可以通过$route.params对象获取传递的参数。
name,该路由的名称。
component:配置path映射的页面组件,即Content,在该页面可以通过$route.params获取路由传递的参数值。
redirect:重定向的地址,即访问根路径时,重定向到“/Content/hot”路径。
路由导航时,根据path路径的匹配,映射到component指定的页面,并渲染到<router-view>指定的插槽位置。
到此,基本的架构就实现了。
三、子页面组件实现
子页面组件最重要的是要根据路由的参数,获取相关数据并展示,那在什么时机去获取数据呢?路由提供了各类状态的钩子,称之为导航守卫,对这些状态钩子进行监听,实现数据的处理。
1、导航卫士
导航守卫分为路由守卫以及组件内守卫,其中:
1、路由守卫,又分为全局守卫,和单个路由独享守卫。
全局守卫,顾名思义,加载全局路由对象router上,任何路由的变化都会响应,其事件包括beforeEach,afterEach;
单个路由独享守卫,是加载某个路由对象上,仅影响该路由的变化,其事件包括beforeEnter。如下:
const router = new VueRouter({
routes: [
{ path: '/home/post/:id', component: Home, name:"home",
//路由独享卫士
beforeEnter:(to, from, next)=>{
console.log("home beforeEnter");
next();
}
}
]
})
//全局路由卫士
router.beforeEach((to, from, next)=>{
console.log("全局的 before Each");
next();
})
router.afterEach((to,from)=>{
console.log("全局的 after Each");
})
导航卫士一般包含三个入参:
to:即将进入的目标路由对象。
from:当前导航正要离开的路由
next:一定要调用该方法来 resolve 这个钩子,这个方法可以控制路由的继续执行,或者中断执行。
2、组件内卫士,组件内响应路由的变化,包括beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave。
const New = {
template: `<div>
new {{ $route.params.id }}
</div>`,
beforeRouteEnter:(to, from, next)=>{
console.log("New template beforeRouteEnter");
next();
},
beforeRouteUpdate: (to, from, next)=> {
console.log("New template beforeRouteUpdate");
next();
},
beforeRouteLeave: (to, from, next)=> {
console.log("New template beforeRouteLeave");
next();
}
}
参数同上。
如A页面跳转到B页面,导航卫士执行的顺序如下:
beforeRouteUpdate是指路由发生变化,但是组件被复用时调用,本例的Content组件就符合该情况。
2、数据获取
对于Content组件,先构造数据对象:
var hotPost = {
vod_1:[
{src:require('../assets/1.jpg'),title:"暴裂无声",duban:"8.9"},
{src:require('../assets/2.jpg'),title:"动物世界",duban:"7.3"},
{src:require('../assets/3.jpg'),title:"邪不压正",duban:"7.1"},
{src:require('../assets/4.jpg'),title:"阳光灿烂的日子",duban:"8.0"}
],
vod_2:[
{src:require('../assets/5.jpg'),title:"我不是药神",duban:"6.9"},
{src:require('../assets/6.jpg'),title:"战狼2",duban:"9.6"},
{src:require('../assets/7.jpg'),title:"烈日灼心",duban:"6.2"},
{src:require('../assets/8.jpg'),title:"绣春刀ii:修罗战场",duban:"7.6"}
]
}
var newPost = {
vod_1:[
{src:require('../assets/9.jpg'),title:"淘气大侦探",duban:"7.0"},
{src:require('../assets/10.jpg'),title:"北方一片苍茫",duban:"6.8"},
{src:require('../assets/11.jpg'),title:"摩天营救",duban:"6.6"},
{src:require('../assets/12.jpg'),title:"汪星卧底",duban:"5.8"}
],
vod_2:[
{src:require('../assets/13.jpg'),title:"神奇马戏团",duban:"5.4"},
{src:require('../assets/14.jpg'),title:"邪不压正",duban:"7.1"},
{src:require('../assets/15.jpg'),title:"牧野诡事之神仙眼",duban:"3.4"},
{src:require('../assets/16.jpg'),title:"阿修罗",duban:"3.0"}
]
}
var likePost = {
vod_1:[
{src:require('../assets/17.jpg'),title:"这个杀手不太冷",duban:"9.4"},
{src:require('../assets/18.jpg'),title:"阿甘正传",duban:"9.4"},
{src:require('../assets/19.jpg'),title:"千与千寻",duban:"9.3"},
{src:require('../assets/20.jpg'),title:"烈日灼心",duban:"7.9"}
],
vod_2:[
{src:require('../assets/21.jpg'),title:"阳光灿烂的日子",duban:"8.8"},
{src:require('../assets/22.jpg'),title:"完美陌生人",duban:"8.6"},
{src:require('../assets/23.jpg'),title:"疯狂动物城",duban:"9.2"},
{src:require('../assets/24.jpg'),title:"无问西东",duban:"7.6"}
]
}
我们分别构造三个数据对象,每个电影对象包括海报地址,海报名称,豆瓣评分三个属性。
下面在beforeRouteEnter,beforeRouteUpdate中实现数据的获取。
export default {
name: 'Content',
data () {
return {
//默认为host数据
post:hotPost
}
},
//组件首次创建前调用,由于此时组件还未被创建,不能使用this
//通过传一个回调给 next来访问组件实例
beforeRouteEnter (to, from, next) {
console.log("beforeRouteEnter:"+to.params.id);
next(vm => vm.setData(to.params.id))
},
//在当前路由改变,但是该组件被复用时调用
//比Content/hot跳转到Content/like时调用,此时组件被创建,可以访问this对象
beforeRouteUpdate (to, from, next) {
console.log("beforeRouteUpdate:"+to.params.id);
this.post = null;
this.setData(to.params.id);
next();
},
methods:{
//根据不同的路由参数,加载不同的数据
setData(id){
if(id=="like"){
this.post = likePost;
}else if(id=="new"){
this.post = newPost;
}else{
console.log("====");
this.post = hotPost;
}
}
}
}
Content.vue的完整代码如下:
<template>
<div class="row" >
<div v-for=" vod in post.vod_1" >
<p >
<img width="125" height="170" :src='vod.src'>
</p>
<p >
<a href="#" >{{vod.title}}</a>
</p>
<p >
豆瓣评分:{{vod.duban}}
</p>
</div>
<div v-for=" vod in post.vod_2" >
<p >
<img width="125" height="170" :src='vod.src'>
</p>
<p >
<a href="#" style="">{{vod.title}}</a>
</p>
<p >
豆瓣评分:{{vod.duban}}
</p>
</div>
</div>
</template>
<script>
var hotPost = {
vod_1:[
{src:require('../assets/1.jpg'),title:"暴裂无声",duban:"8.9"},
{src:require('../assets/2.jpg'),title:"动物世界",duban:"7.3"},
{src:require('../assets/3.jpg'),title:"邪不压正",duban:"7.1"},
{src:require('../assets/4.jpg'),title:"阳光灿烂的日子",duban:"8.0"}
],
vod_2:[
{src:require('../assets/5.jpg'),title:"我不是药神",duban:"6.9"},
{src:require('../assets/6.jpg'),title:"战狼2",duban:"9.6"},
{src:require('../assets/7.jpg'),title:"烈日灼心",duban:"6.2"},
{src:require('../assets/8.jpg'),title:"绣春刀ii:修罗战场",duban:"7.6"}
]
}
var newPost = {
vod_1:[
{src:require('../assets/9.jpg'),title:"淘气大侦探",duban:"7.0"},
{src:require('../assets/10.jpg'),title:"北方一片苍茫",duban:"6.8"},
{src:require('../assets/11.jpg'),title:"摩天营救",duban:"6.6"},
{src:require('../assets/12.jpg'),title:"汪星卧底",duban:"5.8"}
],
vod_2:[
{src:require('../assets/13.jpg'),title:"神奇马戏团",duban:"5.4"},
{src:require('../assets/14.jpg'),title:"邪不压正",duban:"7.1"},
{src:require('../assets/15.jpg'),title:"牧野诡事之神仙眼",duban:"3.4"},
{src:require('../assets/16.jpg'),title:"阿修罗",duban:"3.0"}
]
}
var likePost = {
vod_1:[
{src:require('../assets/17.jpg'),title:"这个杀手不太冷",duban:"9.4"},
{src:require('../assets/18.jpg'),title:"阿甘正传",duban:"9.4"},
{src:require('../assets/19.jpg'),title:"千与千寻",duban:"9.3"},
{src:require('../assets/20.jpg'),title:"烈日灼心",duban:"7.9"}
],
vod_2:[
{src:require('../assets/21.jpg'),title:"阳光灿烂的日子",duban:"8.8"},
{src:require('../assets/22.jpg'),title:"完美陌生人",duban:"8.6"},
{src:require('../assets/23.jpg'),title:"疯狂动物城",duban:"9.2"},
{src:require('../assets/24.jpg'),title:"无问西东",duban:"7.6"}
]
}
export default {
name: 'Content',
data () {
return {
//默认为host数据
post:hotPost
}
},
//组件首次创建前调用,由于此时组件还未被创建,不能使用this
//通过传一个回调给 next来访问组件实例
beforeRouteEnter (to, from, next) {
console.log("beforeRouteEnter:"+to.params.id);
next(vm => vm.setData(to.params.id))
},
//在当前路由改变,但是该组件被复用时调用
//比Content/hot跳转到Content/like时调用,此时组件被创建,可以访问this对象
beforeRouteUpdate (to, from, next) {
console.log("beforeRouteUpdate:"+to.params.id);
this.post = null;
this.setData(to.params.id);
next();
},
methods:{
//根据不同的路由参数,加载不同的数据
setData(id){
if(id=="like"){
this.post = likePost;
}else if(id=="new"){
this.post = newPost;
}else{
console.log("====");
this.post = hotPost;
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.row{
position: relative;
height: 250px;
width: 600px;
}
.row div{
float: left;
margin-left: 20px;
width: 125px;
height: 240px;
text-align: center;
}
.row div a{
font-size: 13px;
text-decoration: none;
}
.row div p:nth-child(1){
margin-bottom: 0px;
}
.row div p:nth-child(2){
margin: 0px;
color: #666;
}
.row div p:nth-child(3){
color: #666;
margin-top: 5px;
font-size: 13px;
}
</style>
此时,大功告成,大家可以运行下。
结合实例,本文介绍了路由的基本概念和用法,要想了解详细的,可以vue参考官方网站。