VUE探索第三篇-路由(vue router)

一、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参考官方网站。

上一篇:VUE探索第二篇-手脚架(vue-cli)

发布了33 篇原创文章 · 获赞 95 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tcy83/article/details/81293688