VueRouter01 基础

Vue Router基础学习笔记。

安装

1
npm install vue-router

加载

需要使用Vue.use()明确的安装路由

1
2
3
4
5
// main.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div id="app">
<h3>Hello Vue Router</h3>
<template>
<router-link to="/foo">Go to foo</router-link>
<router-link to="/bar">Go to bar</router-link>
</template>
<router-view></router-view>
</div>
<script>
'use strict';
const Foo = {template: '<div> Hello Foo </div>'},
Bar = {template: '<div> Hello Bar </div>'};

const routes = [
{path: '/foo', component: Foo},
{path: '/bar', component: Bar}
];

const router = new VueRouter({
routes
});

let app = new Vue({
router
}).$mount('#app')
</script>

(1)用<router-link>实现导航标签,在DOM中会被渲染为<a>标签,to属性指定链接

(2)用<router-view>作为路由出口,路由对应的组件内容会被渲染到其中

(3)定义路由routes,由对象组成的数组,对象至少包含的属性是pathcomponent,其中path<router-link>中的to属性对应,to属性如果不加/则是以当前地址为基准的相对路径

(4)创建路由实例new VueRouter({routes})

(5)创建Vue实例时可以直接添加router属性挂载路由

动态路由

routes数组的path属性中,以:开始后面的参数就是动态路径参数:

1
2
3
4
5
6
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})

当访问/user/a时,可以在组件中通过$route.params.id获取到动态路径参数a

可以通过$route.query获取URL中的查询参数,通过$route.hash获取路由的hash值

动态路由间的跳转,比如从/user/a/user/b,原组件实例会被复用,所以Vue实例的mounted等生命周期钩子函数不会执行。如果想要响应参数变化有两种方法:

(1)watch组件的$route对象:

1
2
3
4
5
6
7
const User = {
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}

(2)使用beforeRouteUpdate()方法(2.2+)

1
2
3
4
5
6
7
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// 对路由变化作出响应...
// 需要调用 next 方法进行跳转
}
}

路由匹配

有时候,一个路径可以匹配多个多路,此时匹配的优先级就是按照路由的定义顺序,先定义的路由优先级最后,后面定义的路由就不会再匹配

可以使用通配符*来匹配任意路径,由于上面提到的优先级,所以含有通配符的路由应该放在最后,一般用来匹配404路由

1
2
3
4
5
6
7
8
const router = new VueRouter({
routes: [
// 会匹配所有路径
{ path: '*', component: NotFound },
// 匹配以 `/user-` 开头的任意路径
{ path: '/user-'', component: User },
]
})

使用了通配符后,可以通过$route.paramspathMatch属性获取URL通过通配符被匹配的部分。

除了通配符之外,vue-router支持很多高级的匹配模式,可以看这个例子来学习。

嵌套路由

<router-view>是渲染路由内容的组件,最高级路由匹配到的组件会渲染到顶层的<router-view>中。组件可以在内部包含自己的嵌套的<router-view>,将路由选项中的children属性中的组件渲染在其中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
// 当 /user/:id 匹配成功,
// UserHome 会被渲染在 User 的 <router-view> 中
{
path: '',
component: UserHome
}, {
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
]
}
]
})

/开头的嵌套路径也会被当做根路径进行匹配。

嵌套路由如果要设置默认的子路由,子路有的path可以设置为/或者'',但是父路由不能有的name属性

(想要嵌套路由的<router-link>active-class起作用,可以为自由路的默认路由设置redicet:

1
2
3
4
5
routes.find(v => v.path === '/demo38').children = [
{path: '/', redirect: 'demo38-1'},
{path: 'demo38-1', component: HelloWorld},
{path: 'demo38-2', component: NotFound},
];

编程式导航

除了使用<router-link>来进行路由跳转之外,还可以通过$router的实例方法实现编程式的导航

路由跳转可以使用$router.push方法,它会向History栈添加新的记录,为用户点击后腿按钮提供后退功能

1
router.push(location, onComplete?, onAbort?)

第一个参数可以是字符串路径,也可以是一个描述地址的对象:

1
2
3
4
5
6
7
8
9
10
11
// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

如果提供了pathparam虚选项会被忽略:

1
2
3
4
5
6
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

后两个参数是导航成功或终止后的回调函数,在3.1.0+版本后如果省略这两个参数,将会返回一个Promise

如果跳转时路由相同,只是参数发生变化(比如/user/:id跳转),需要使用beforeRouteUpdate来响应变化

除了router.push,还可以使用router.replace来进行跳转,不同的是router.replace不会为History添加新纪录

还可以使用router.go(n),在History记录中向前或者后退多少步。

命名路由

在创建Router的实例时,在routes数组内部对象增加name属性,为路由设置名称

1
2
3
4
5
6
7
8
9
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})

这样在<router-link>to属性中,就可以使用name属性来进行路由的匹配

1
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

命名视图

在一个组件中,可以为<router-view>添加name属性,同时展示多个视图,没有设置name<router-view>,默认为default

1
2
3
<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>

在路由设置里面,需要使用components在当前路由下渲染多个组件,不同组件对应不同namerouter-view

1
2
3
4
5
6
7
8
9
10
11
12
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})

重定向和别名

重定向时在routes数组的对象中添加rediect属性:

1
2
3
4
5
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})

redirect的值可以是字符串,也可以是一个命名路由{ name: 'foo' },也可以是一个函数,返回值是字符串路径或者路径对象

注意导航守卫没有应用在跳转路由上,仅应用在目标上。

别名

别名与重定向不同,重定向会将URL进行替换,而设置了别名的路由,URL并不会改变:

1
2
3
4
5
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})

当用户访问/b时,URL显示为/b,但是加载的组件是A

路由组件传参

在组件中使用$route会使组件与对应路由高耦合,可以使用props将组件合路由解耦:

1
2
3
4
5
6
7
8
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})

使用props解耦,将props设置为true,那么$route.params就会被设置为组件属性

1
2
3
4
5
6
7
8
9
const User = {
props: ['id']
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
]
})

针对包含多个命名视图的路由,必须分别为每个命名视图添加props选项:

1
2
3
4
5
6
7
8
9
const router = new VueRouter({
routes: [
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})

如果props是一个对象,那么会被原样设置为组件的属性,一般用做props为静态的情况:

1
2
3
4
5
const router = new VueRouter({
routes: [
{ path: '/a/b', component: C, props: { popup: false } }
]
})

props是也可以接受一个函数,参数是当前的路由对象route

1
2
3
4
5
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})

访问/search?q=123会将{query: 123}作为属性传递给SearchUser组件

应该尽可能保持props函数为无状态的,如果需要状态来定义props,请使用包装组件。

History模式

vue-router默认使用hash模式,使用URL的hash值来模拟一个完整的URL,当URL改变时,实际上改变的是#后面的hash值,页面不会被重新加载

vue-router提供了history模式,它利用的是history.pushStateAPI来实现URL跳转而无需重新加载页面

1
2
3
4
const router = new VueRouter({
mode: 'history',
routes: [...]
})

这种情况下URL不会有#,但是需要后台配置支持,因为如果后台没有配置,那么用户直接访问URL时,首先会经过后台的真正的路由,但是后台没有匹配的路由会返回404,所以应该在服务端增加一个覆盖所有情况的候选资源,如果URL匹配不到任何静态资源,就返回同一个index.html,这个页面就是单页应用的容器页面

要注意,这个时候的后台无法响应404页面了,必须在前端给出404页面。