Vue Router 是 Vue.js 官方的路由管理器.它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
单页应用与多页应用区别:
起步:
使用vue,通过组件组合来组成应用程序,然后将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们.
App.vue
<template>
<div id="app">
<router-view/>
div>
template>
javascript
// 1. 定义 (路由) 组件,可以从其他文件 import 进来
const Foo = {
template: 'foo' }
const Bar = {
template: 'bar' }
// 2. 定义路由 每个路由应该映射一个组件
const routes = [
{
path: '/foo', component: Foo },
{
path: '/bar', component: Bar }
]
// 3 router/index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
// 创建 router 实例,然后传 `routes` 配置,还有其它配置如:mode(路由模式)
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4.main.js
// 创建和挂载根实例 通过 router 配置参数注入路由, 从而让整个应用都有路由功能
// vuex store
import store from '@/store/index'
// 菜单和路由设置
import router from './router'
new Vue({
router,
store
}).$mount('#app)
this.$router
与this.$route
的区别
路由: 根据不同的url地址展现不同的内容或页面. 前端路由更多用在单页应用(SPA)
url组成部分:
两种路由对比:
hash(默认) | history(mode: history) | |
---|---|---|
url | http:// abc.com/#/user/20 有#,不美观 | http:// abc.com/user/20 美观 |
兼容性 | 兼容低版本浏览器以及IE浏览器 | HTML5新推出的API |
特点 | hash变化会触发网页跳转,即浏览器的前进,后退;不会导致浏览器向服务器发出请求,也就不会刷新页面;会触发hashchange事件 | 用url规范的路由,但跳转时不发送请求,不自动刷新页面; 因为不发送请求, url地址变化, 一旦页面一手动刷新就会找不到这个页面,报404,需要服务器端做点手脚,将不存在的路径请求重定向到入口文件(index.html) |
主要内容 | hashchange事件 | pushState和replaceStateAPI; onpopstate 事件 |
实现原理:检测 url 的变化,截获 url 地址,然后解析来匹配路由规则
hash模式: hashchange事件监听hash变化 源码
/**
* 添加 url hash 变化的监听器
*/
setupListeners () {
const router = this.router
/**
* 每当 hash 变化时就解析路径
* 匹配路由
*/
window.addEventListener('hashchange', () => {
const current = this.current
/**
* transitionTo:
* 匹配路由
* 并通过路由配置,把新的页面 render 到 ui-view 的节点
*/
this.transitionTo(getHash(), route => {
replaceHash(route.fullPath)
})
})
}
history模式: window.onpopstate监听; pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求 源码
export class HTML5History extends History {
constructor (router, base) {
super(router, base)
/**
* 原理还是跟 hash 实现一样
* 通过监听 popstate 事件
* 匹配路由,然后更新页面 DOM
*/
window.addEventListener('popstate', e => {
const current = this.current
// Avoiding first `popstate` event dispatched in some browsers but first
// history route not updated since async guard at the same time.
const location = getLocation(this.base)
if (this.current === START && location === initLocation) {
return
}
this.transitionTo(location, route => {
if (supportsScroll) {
handleScroll(router, route, current, true)
}
})
})
}
go (n) {
window.history.go(n)
}
push (location, onComplete, onAbort) {
const {
current: fromRoute } = this
this.transitionTo(location, route => {
// 使用 pushState 更新 url,不会导致浏览器发送请求,从而不会刷新页面
pushState(cleanPath(this.base + route.fullPath))
onComplete && onComplete(route)
}, onAbort)
}
replace (location, onComplete, onAbort) {
const {
current: fromRoute } = this
this.transitionTo(location, route => {
// replaceState 跟 pushState 的区别在于,不会记录到历史栈
replaceState(cleanPath(this.base + route.fullPath))
onComplete && onComplete(route)
}, onAbort)
}
}
export function cleanPath (path: string): string {
return path.replace(/\/\//g, '/')
}
动态路由
把某种模式匹配到的所有路由,全都映射到同个组件. 例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来实现
const router = new vueRouter({
routes: [
//动态路径参数,以冒号开头,能命中`user/10`, `user/20`等格式的路由
{
path: 'user/:id', component: User}
]
})
// 获取参数
this.$route.params.id
注意: 当使用路由参数时,例如从 /user/10 导航到 /user/20,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。复用也意味着组件的生命周期钩子不会再被调用。
如果想对路由参数的变化作出响应的话,可以监测$route 对象或 beforeRouteUpdate 导航守卫:
const User = {
template: '...',
// 监测
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
},
// 或导航守卫
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
next()
}
}
捕获所有路由或 404 Not found 路由
想匹配任意路径,我们可以使用通配符(*)
,路由 { path: '*' }
通常用于客户端 404 错误
{
path: '*',
name: '404',
component: () => import('@/pages/error-page-404')
}
//当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。
//它包含了 URL 通过通配符被匹配的部分
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
注: 含有通配符的路由应该放在最后
嵌套路由: children 配置嵌套多层路由
命名视图: 同级展示多个视图,而不是嵌套展示
命名路由: 以一个名称(name)来标识一个路由
组件传参: 在组件中使用 $route 会使之与其对应路由形成高度耦合,使用 props 将组件和路由解耦
路由懒加载(按需加载): 当路由被访问的时候才加载对应组件
const Foo = () => import('./Foo.vue')
声明式导航 | 编程式导航 |
---|---|
|
this.$router.push(...) |
|
this.$router.replace(...) |
router.go(n) 在 history 记录中向前(n为正整数)或者后退(n为负整数)多少步 |
push()、replace()方法所带参数可以是一个字符串路径,或者一个描述地址的对象
注意: 如果提供了 path,params 会被忽略
const userId = '123'
// 字符串
router.push('home')
// 对象
router.push({
name: 'user', params: {
userId }}) // -> /user/123
router.push({
path: `/user/${
userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({
path: '/user', params: {
userId }}) // -> /user
导航守卫
vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航,共三种导航守卫类型: 全局的、单个路由配置的、组件级的.
全局守卫:
路由钩子 | 描述 |
---|---|
router.beforeEach((to, from, next) => { }) | 全局前置守卫,可用于路由拦截,权限验证,撤销请求等 |
router.beforeResolve() | 全局解析守卫 |
router.afterEach((to, from) => {}) | 全局后置钩子 |
每个守卫方法接收三个参数:
参数 | 描述 |
---|---|
to:Route |
即将要进入的目标 路由对象 |
from:Route |
当前导航正要离开的路由 |
next: Function |
调用 next 方法,确保钩子被 resolved. 四种调用参数: next() 进行下一钩子、next(false): 中断当前导航、next(’/’): 导航中断,跳转一个新导航、next(error): 导航终止且该错误会被传递给 router.onError() 注册过的回调 |
路由守卫: beforeEnter
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件守卫
路由钩子 | 描述 |
---|---|
beforeRouteEnter (to, from, next) {}) | 在渲染该组件的对应路由被 confirm 前调用 .当守卫执行前,组件实例还没被创建, 不!能!获取组件实例 this |
beforeRouteUpdate (to, from, next) {}) | 在当前路由改变,但是该组件被复用时调用,可以访问组件实例 this . 用途: 对动态路由参数的变化作出监听 |
beforeRouteLeave (to, from, next) {}) | 导航离开该组件的对应路由时调用,可以访问组件实例 this |
路由元信息: 定义路由的时候可以配置 meta 字段
过渡动效: 给路由设置过渡效果
数据获取: 进入某个路由后,从服务器获取数据
滚动行为: 自定义路由切换时页面如何滚动
Vue Router API