vue router是的vue.js的官方路由管理器。它和vue.js的核心深度集成,可以让构建单页面应用变得易如反掌。路由可以达到的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于vue.js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的css class的链接
- html5历史模式或hash模式,在IE9中自动降级
- 自定义的滚动条行为
安装
(1)vue-router的安装可以使用script的标签通过src路径引入,得配合vue.js使用。
(2)可以使npm包安装vue-router使用,在模块化工程中使用它,必须通过vue.use()明确安装路由功能。
(3)还可以使用github上的最新开发版。
基础实例
vue-router的跳转路由就两种方式跳转,一种是在html页面使用router-link标签跳转,还有一种就是在js中使用编程式跳转
<template>
<div class="container">
<router-link to="/hhhh">
<div>这个是router-link跳转路由方式</div>
</router-link>
<div @click="toRouter">这个是js编程式跳转路由方式</div>
<tabs></tabs>
</div>
</template>
<script>
import home from '../home/home.vue'
import tabs from '../../components/tabs.vue'
export default {
data: function() {
return {}
},
methods: {
releaseA:function(){
this.$router.push({ name: 'releaseinfo', params: { msg: 'releaseinfo' }});
},
toRouter:function(){
this.$router.push({ path:'/hhhh'});
},
},
mounted: function() {
},
components: {
tabs
}
}
</script>
<style lang='scss'>
</style>
使用了上面两种方式跳转路由,你会发现浏览器的url的连接会随之变化。我现在使用的是自动化构建工具,可以在app.vue里面加上router-view标签,路由匹配的组件将会渲染到这里。
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
在router.js配置里面添加上对应路由需要渲染的组件即可完成一个简单的单页面实例。
export default new Router({
routes: [
{
path: '/hhh',
name: 'hhh',
component: hhh
}
]
});
动态路由匹配
动态路由匹配,有时候比如查看用户资料,组件是user,但是用户的id不同,组件需要加载不同的数据。那么可以使用动态路径参数来达到这个效果。
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
现在的,如果js跳转像 /user/foo
和 /user/bar
这些路径都将映射到相同的路由。一个路径参数使用冒号:标记。当匹配到一个路由时,参数值会被设置到this.$route。params,可以在组件中使用。
嵌套路由
实际开发的过程中我们的界面通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
/user/foo/profile /user/foo/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
倘若使用嵌套路由可以很简单的表达关系。
routes: [
{ path: '/user', component: User,
children: [
{
// 当 /user/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
这段加了注释的router配置可以为你很清楚的理解嵌套路由配置。如果的路由路径为/user/article那么页面不会渲染任何组件。
编程式的导航
上面说了除了利用router-link标签创建a标签来定义导航链接,我们可以通过router的实例方法来通过代码实现。
router.push(location,onComplete?,onAbort?)
在vue实例内部我们可以通过this.$router.push来实现路由跳转。
该方法科颜氏一个字符串路径,或者一个描述地址的对象。
// 字符串
routerString:function(){
this.$router.push('hhh');
},
// 对象
routerObj:function(){
this.$router.push({path:'hhh'});
},
// 命名的路由
routerName:function(){
this.$router.push({name:'hhh'});
},
// 带查询参数,变成 /hhh?id=123465
routerQuery:function(){
this.$router.push({path:'hhh',query:{id:'123456'}});
},
需要注意的是在动态路由匹配中我们也可以类似于携带参数访问路由,如果提供了path,params会被忽略,上述例子中的query不属于这个情况。取而代之的是下面的做法。使用下面的做法携带数据。path不能与params一同使用。在router-link中次规则也是如此。
const userId = 123
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1
-> /users/2
),你需要使用 beforeRouteUpdate生命周期函数
来响应这个变化 (比如抓取用户信息)。
router.replace(location,onComplete?,onAbort?)
replace方法不会向history添加新记录,而是跟它的方法名一样--替换掉当前的history记录。
router.go(n)
n传入一个整数,和js中window.history.go(n)类似。
命名视图
有时候想同级展示多个视图,类似主页的布局,头部侧边栏主内容等等,命名视图的作用就来了,可以在界面拥有多个单数命名的视图,而不是只有单独的出口,router-view未设置名字,都默认为default。
假设根目录下的router-view格式是这样。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
因为一个视图使用一个组件渲染,对于同一个路由,多个视图需要为其命名
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
第一个router-view的默认名字为default所以渲染one,name为a的view渲染Bar组件,同理name为b的组件渲染Baz。
重定向和别名
重定向也是通过routes配置来完成,下面的例子是从/a重定向到/b。
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的意思是当用户访问/a时,url会被替换成/b,然后路由匹配为/b;
别名
别名的意思是当用户访问/b时,url还是/b但是路由匹配为/a,相当于用户在访问/a;
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
导航守卫
导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
参数和查询的改变不会出发导航守卫,你可以通过观察 $route
对象来应对这些变化,或使用 beforeRouteUpdate
的组件内守卫。
全局守卫
可以使用router.beforeEach注册一个全局前置守卫。当一个导航触发时,会按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
每个守卫接收3个参数,to:即将要进入目标的路有对象,from:当前导航要离开的路由。next:function 一定要调用这个方法来resolve这个钩子,执行方法可以传参进行下一步操作;
-
next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 -
next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from
路由对应的地址。 -
next('/')
或者next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop 或router.push
中的选项。 -
next(error)
: (2.4.0+) 如果传入next
的参数是一个Error
实例,则导航会被终止且该错误会被传递给router.onError()
注册过的回调。
beforeResolve在导航被确认之前,同时在所有组件内守卫和异步路由由组件被解析之后,解析守卫就被调用。
aflterEach全局后置钩子。
路由独享的守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
用法与全局前置守卫方法参数一样
路由元信息
定义路由的时候开业配置meta字段:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
过渡动效
过渡动效的使用和组件切换使用动态过渡的方法基本一致。只要利用watch属性监听$route的变化可以灵活的使用不同的过渡效果。
watch: {
'$route' (to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
数据获取
数据获取的思路一般有两种:在导航完成之后获取,先完成导航,在组件的生命周期函数内获取数据完成渲染。第二个是在导航完成前,在路由进入的守卫中获取数据,数据获取成功后执行导航。
导航完成后获取数据
<template>
<div class="post">
<div class="loading" v-if="loading">
Loading...
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 组件创建完后获取数据,
// 此时 data 已经被 observed 了
this.fetchData()
},
watch: {
// 如果路由有变化,会再次执行该方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
在导航完成前获取数据
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
我们在导航完成前获取数据,然后渲染组件。
滚动行为
使用前端路由切换新路由时,想要页面滚动到顶部,或者保持在原先的滚动位置,就想重新加载页面那样,vue-router可以做得更好。
这个功能只在支持history.pushState的浏览器中可用。
在创建router实例的时候,可以提供一个scrollBehavior方法
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
to和from和之前的接受对象一样,savedPosition当且仅当popstate导航(通过浏览器的 前进/后退 按钮触发)是才可用。