vue-router 实战了解这些就够了
一. 安装
1.1. 加入依赖
npm install vue-router
1.2. 项目中引入
以最新的vue-cli3架构为例,在main.js中加入
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
更普遍的,一般是有一个专门的路由js文件,如router.js
在main.js中只是引入router.js。而在router.js中加入上述引用部分。以后相关路由配置都是router.js中配置的。
以上涉及各js文件的目录组织如下:
src
------ main.js
------ router
------------ router.js
1.3. 示例
先贴实战部分示例来了解一下直观的用法
main.js
import 'babel-polyfill'
import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
import router from './router/router'
import store from './vuex/store'
import api from './api/api'
import Vant from 'vant'
import 'vant/lib/index.css'
import infiniteScroll from 'vue-infinite-scroll'
Vue.use(infiniteScroll)
Vue.use(Vuex)
Vue.use(Vant)
Vue.prototype.$api = api
new Vue({
router, // 通过 router 选项,提供了一种机制将 “路由” 从根组件注入到每一个子组件中,子组件能通过 this.$router 访问
store, // 通过 store 选项,提供了一种机制将 “状态” 从根组件注入到每一个子组件中,子组件能通过 this.$store 访问
render: h => h(App)
}).$mount('#app')
router.js
import Vue from 'vue'
import Router from 'vue-router'
const home = () => import('@/views/index/index.vue')
const airport = () => import('@/components/airport/airport.vue')
const orderDetail = () => import('@/views/order/order-detail.vue')
Vue.use(Router)
export default new Router({
mode: 'hash',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: home
},
{
path: '/airport',
name: 'airport',
component: airport
},
{
path: '/orderDetail/:orderNo',
name: 'orderDetail',
component: orderDetail
}
]
})
二. 用法
2.1. 第一个demo
- HTML
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>
- JS
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 通过 router 配置参数注入路由从而让整个应用都有路由功能
const app = new Vue({
router // (缩写) 相当于 router: router
}).$mount('#app')
通过注入路由器,我们可以在任何组件内通过
this.$router
访问路由器,也可以通过this.$route
访问当前路由:
// Home.vue
export default {
computed: {
username () {
return this.$route.params.username
}
},
methods: {
goBack () {
window.history.length > 1
? this.$router.go(-1)
: this.$router.push('/')
}
}
}
2.2. 动态路由匹配
即请求url中包含动态参数时的匹配,类似这种形式: /user/:id,类似于springMVC中的@pathVariable
下面举个基本的栗子了解下用法:
- 请求路径:/user/:id
- 应用场景:比如可以把不同ID的用户都映射到User组件上
- 然后再User组件中可以通过 this.$route.params.id 取到动态url上的params参数对象中的id属性
可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params
中。例如:
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/evan | { username: 'evan' } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: '123' } |
除了
$route.params
外,$route
对象还提供了其它有用的信息,例如,$route.query
(如果 URL 中有查询参数)、$route.hash
等等。
2.3. 嵌套路由
例如通常在App.vue中有一个最顶层渲染出口 <router-view />
<template>
<div id="app" class="app">
<router-view />
</div>
</template>
当访问 /user/1 时会把User组件渲染到最顶层的出口<router-view />中
同时User组件中包含嵌套的渲染出口<router-view />
例如,在 User
组件的模板添加一个 <router-view />:
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
要在嵌套的出口中渲染组件,需要在 VueRouter
的参数中使用 children
配置:
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
children
配置就是像 routes
配置一样的路由配置数组!
此时,基于上面的配置,当你访问 /user/foo
时,User
的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个 空的 子路由:
const router = new VueRouter({
routes: [
{
path: '/user/:id', component: User,
children: [
// 当 /user/:id 匹配成功,
// UserHome 会被渲染在 User 的 <router-view> 中
{ path: '', component: UserHome },
// ...其他子路由
]
}
]
})
2.4. 编程式的导航
2.4.1. router.push(location, onComplete?, onAbort?)
声明式 | 编程式 |
---|---|
<router-link :to="..."> |
router.push(...) |
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path
,params
会被忽略!
因为path选项就表示完整的URL,而params又是URL中的一部分,所以同时存在会有歧义!
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
2.4.2. router.replace(location, onComplete?, onAbort?)
跟 router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
声明式 | 编程式 |
---|---|
<router-link :to="..." replace> |
router.replace(...) |
2.4.3. router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)
。
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
2.5. 命名路由
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user', // 给路由起个名字而已
component: User
}
]
})
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
或者
router.push({ name: 'user', params: { userId: 123 }})
调用皆可以!
2.6. 重定向
当用户访问 /a
时,URL 将会被替换成 /b
,然后匹配路由为 /b
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
2.7. 别名
/a
的别名是 /b
,意味着,当用户访问 /b
时,URL 会保持为 /b
,但是路由匹配则为 /a
,就像用户访问 /a
一样。
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
2.8. 路由组件传参
顾名思义,就是在路由的过程中,把路由参数转换成组件属性,按照组件间传值的方式进行传参!
why do this?
在组件中使用
$route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
2.8.1. 用法对比
与 $route
的耦合
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
通过 props
解耦
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
]
})
布尔模式
如果 props
被设置为 true
,route.params
将会被设置为组件属性。
对象模式
如果 props
是一个对象,它会被按原样设置为组件属性。当 props
是静态的时候有用。
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
]
})
函数模式
你可以创建一个函数返回 props
。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
URL /search?q=vue
会将 {query: 'vue'}
作为属性传递给 SearchUser
组件。