如果在一个模块化工程中使用,必须通过Vue.use()明确地安装路由功能。
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue Router 是Vue.js官方的路由管理器,它与Vue.js的核心深度集成。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于Vue.js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的CSS class的链接
- HTML5历史模式或hash模式
- 自定义的滚动条行为
把Vue Router添加后,我们要做的是将组件(components)映射到路由(routes),然后告诉Vue Rotuer 在那里渲染它们。
使用router-link组件来导航,通过to属性指定链接,router-link会默认被渲染成一个a标签。
router-view指定路由匹配到的组件渲染的地方。
定义路由routes 然后创建router实例注入定义的路由进行配置。 最后将router挂载到Vue实例。通过注入路由器,我们可以在任何组件内通过this.$router访问路由器,也可以通过this.$route 访问当前路由。当对应的路由匹配成功,将自动设置class属性值.router-link-active.
一个"路径参数"使用冒号:标记。参数被设置到this.$route.params中。可以在一个路由中设置多个"路径参数",对应的值都会被设置到$route.params中。
当使用同一个组件对应多个路由时,组件实例会被复用。因为两个路由渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
复用组件时,对路由参数的变化做出响应的两个方法:watch/beforeRouteUpdate导航守卫。
当使用通配符时,$route.params内会自动添加一个名为pathMatch参数。它包含了URL通过通配符被匹配的部分。
当同一个路径匹配多个路由时,匹配的优先级按照路由的定义顺序:谁先定义谁先匹配谁的优先级就最高。
以/开头的嵌套路径会被当作根路径。
const router = new VueRouter({
routes:[
{
path:'/test',
compoment:test,
children:[
{
path:'test2',component:test2
},
{}
]
},
{}
]
})
children配置就像routes配置一样,所以可以嵌套多层路由。
除了使用创建a标签来定义导航链接,我们还可以借助router的实例方法通过编写代码来实现。
在Vue实例内部,你可以通过$router访问路由实例,因此可以调用this.$router.push。
router.push(location,onComplete?,onAbort?)
使用router.push可以导航到不同的url,这个方法会向history栈添加一个新的记录,所以当用户点击后退按钮时,会回到之前的URL。
当你点击<router-link>,这个方法会在内部调用。所以说点击<router-link to:...>等同于调用router.psuh(...)。
router.push(location) location可以是字符串、对象、命名路由,可以带查询参数
'name'/{path:'name'}/{name:'nam',parms:{}}/{path:'',query:{}}
如果提供path 那么params会被忽略。 同样的规则适用于router-link的to 属性。
声明式 | 编程式 |
---|---|
router.push(...) | |
router.replace(…) |
rotuer.replace(location,onComplete?,onAbort?)和router.push很像,不同点是它不会向history添加新纪录,会替换掉当前的history记录。
router.go(n):参数是一个整数,意思是在history记录中向前或者向后退多少步。
可以在创建Router实例的时候,在routes配置中给某个路由设置名称来定义命令路由 name:
链接到一个命名路由的两种方式:
<router-link :to="{name:...,params:{...}}"></router-link>
router.push({name:...,params:{...}})
可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口,如果router-view没有设置名字,默认为default。
一个视图使用一个组件渲染,因此对于同一个路由,多个视图就需要使用多个组件:
const router = new VueRouter({
routes:[
{
path:'...',
components:{ //注意components要带上s!!!
default:组件A,
name:组件B
}
},
//重定向也可以通过routes配置来完成
{path:'/a',redirect:'/b'},
//重定向的目标也可以是一个命名的路由
{path:'/a',redirect:{name:'...'}},
//动态返回重定向目标
{ path: '/a', redirect: to => {
// 方法接收目标路由作为参数
// return 重定向的字符串路径/路径对象
}},
//别名:用户访问别名时,url会保持为别名,但是路由会匹配到起别名的真正路由。别名可以让你自由的将UI结构映射到任意的URL,而不是受限于配置的嵌套路由结构。
{path:'/a',component:A, alias:'/b'},
//使用props将组件和路由进行解耦。在组件中添加props数组,内容为路由参数的名字 props:['id',...] ,设置路由配置中props:true,如下
{path:'...',component:...,props:true},
//对于包含命名视图的路由,必须分别为每个命名视图添加props选项。
{
path:'...',
components:{default:...,name1:...,name2:...},
props:{default:true,name1:false,name2:true}
}
]
})
如果props设置为true,那么route.params将会被设置为组件属性。
如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
你可以创建一个函数返回 props。返回的内容将作为组件的属性。
vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。守卫可以分为全局的、单个路由独享的、组件级的。
参数或查询的改变并不会触发进入/离开的导航守卫。
全局前置守卫
使用router.beforeEach注册一个全局前置守卫。当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve完之前一直处于等待中。
router.beforeEach((to,from,next) =>{
...
})
to:Route 即将要进入的目标路由对象。
from:Route 当前导航正好离开的路由。
next:Function 一定要调用该方法来resolve这个钩子。执行的结果依赖next方法的调用参数:
1、next():进入管道中的下一个钩子。如果全部钩子都执行完了,则导航的状态是confirmed.
2、next(false):终止当前导航。
3、next('/')或next({path:'/'}) 跳转到一个不同的地址。当前的导航被终止,然后进行一个新的导航。
4、next(error):导航会被终止且该错误会被传递给router.onError()注册过的回调。
确保要调用next方法,否则钩子就不会被resolved。
全局解析守卫 不太明白
使用router.beforeResolve注册一个全局守卫。在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
全局后置钩子
router.afterEach((to,from) =>{}) 不接受next函数也不会改变导航本身。
路由独享守卫
在路由配置上定义beforeEnter守卫
const router = new VueRouter({
routes:[
{
path:'...',component:...,
beforeEnter:(to,from,next) =>{...}//与全局前置守卫参数一致。
}
]
})
组件内守卫
可以在路由组件内直接定义一下路由导航守卫:
beforeRouteEnter/beforeRouteUpdate/beforeRouteLeave
const Foo = {
template:'...',
//该方法不能获取组件实例this,因为守卫在导航确认之前被调用,组件实例还没有被创建。可以通过传递一个回调给next来访问组件实例。在导航被确认的时候执行回调,并把组件实例作为回调方法的参数。next(vm =>{通过vm访问组件实例})
//beforeRouteEnter是支持给next传递回调的唯一守卫。因为剩下两个this实例是可以使用的,再支持回调没有必要。
beforeRouteEnter(to,from,next){},
beforeRouteUpdate(to,from,next){},
beforeRouteLeave(to,from,next){}
}
完整的导航解析流程:
- 导航被触发。
- 在失活的组件里调用离开守卫。beforeRouteLeave
- 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数。
路由元信息
定义路由的时候可以配置meta字段。routes:[{meta:{}}]
过渡动效
是基本的动态组件,我们可以使用组件给它添加一些过渡效果。
<transition>
<router-view></router-view>
</transition>
如果想让每个路由组件都有各自的过渡效果,可以在各路由组件内使用并设置不同的name。还可以基于当前路由与目标路由的变化关系,动态设置过渡效果。
数据获取
- 导航完成之后获取:先完成导航,然后再接下来的组件生命周期钩子中获取数据。在数据获取期间添加"加载中"之类的提示。
- 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。数据获取成功后记得调用next方法继续导航的执行。获取失败进行相应失败操作处理。
滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部或者保持原先的滚动位置,就像重新加载页面那样。vue-router能做到,而且能更好,它让你可以自定义路由切换时页面如何滚动。
const router = new VueRouter({
routes:[...],
scrollBehavior(to,from,savedPostion){
return 要滚动到的位置 {x:number,y:number}
//如果返回一个falsy的值,或者一个空对象,那么不会发生滚动。
}
//savePostion当且仅当popstate导航(通过浏览器前进/后退按钮触发)时才可用。
})
路由懒加载
我们把不同路由对象的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样会更加高效。
结合Vue的异步组件和webpack的代码分割功能,可以轻松的实现路由组件的懒加载。
首先,将异步组件定义为一个返回一个Promise的工厂函数(该函数返回的Promise应该resolve组件本身)
const Foo = () => Promise.resolve({/*组件定义对象*/})
在webpack中,使用动态import 语法来定义代码分块点
import('Foo.vue')
const Foo = () => import('./Foo.vue')
在路由配置中声明都不需要改变,像之前一样使用即可。
API参考
组件支持用户在具有路由功能的应用中导航。通过to属性指定目标地址,默认渲染成带有正确连接的标签,可以通过配置tag属性生成别的标签。当目标路由成功激活后,链接元素自动设置一个表示激活的CSS类名。
<router-link>props:
to: 表示目标路由的链接。是一个字符串或者描述目标位置的对象。
replace:默认false。 设置replace会调用router.replace()而不是router.push(),导航后不会留下history记录。
append:默认false。设置append属性后,则会在当前(相对)路径前添加基路径。比如,从/a导航到一个相对路径b,没有配置append 则路径为/b,如果配了则为/a/b。
tag:默认是<a> 使用tag可以指定使用何种标签。它同样会监听点击,触发导航。
active-class: 默认:router-link-active.设置链接激活时使用的CSS类名。默认值可以通过路由的构造选项linkActiveClass来全局配置。
exact:默认false。
event:默认click。声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
exact-active-class:默认router-link-exact-active。配置当链接被精确匹配的时候应该激活的 class。注意默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。
组件是一个functional组件,渲染路径匹配到的视图组件。
因为它也是个组件,所以可以配合和使用。如果两个结合一起用,要确保在内层使用 。
name属性:默认是default。如果设置了名称,则会渲染对应的路由配置中components下相应的组件。
Router构建选项
routes :Array
interface RouteConfig = {
path: string,
component?: Component,
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名视图组件
redirect?: string | Location | Function,
props?: boolean | Object | Function,
alias?: string | Array<string>,
children?: Array<RouteConfig>, // 嵌套路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,
// 2.6.0+
caseSensitive?: boolean, // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions?: Object // 编译正则的选项
}
mode
路由模式:
hash
: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。history
: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。- abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
base
应用的基路径。
linkActiveClass/linkExactActiveClass
scrollBehavior 滚动行为
parseQuery/stringifyQuery/fallback
Router实例属性:router.app/router.mode/router.currentRoute
Router实例方法
router.beforeEach/beforeResolve/afterEach/push/replace/go/back/forward/getMatchedComponents/resolve/addRoutes/
router.onReady(callback, [errorCallback]):该方法把一个回调排队,在路由完成初始导航时调用,这意味着它可以解析所有的异步进入钩子和路由初始化相关联的异步组件。
router.onError(callback):注册一个回调,该回调会在路由导航过程中出错时被调用。注意被调用的错误必须是下列情形中的一种:
- 错误在一个路由守卫函数中被同步抛出;
- 错误在一个路由守卫函数中通过调用
next(err)
的方式异步捕获并处理; - 渲染一个路由的过程中,需要尝试解析一个异步组件时发生错误。
路由对象
一个路由对象(route object)表示当前激活的路由的状态信息,包含当前url解析得到的信息还有URL匹配到的路由记录(route records)。路由对象是不可变的,每次成功的导航后都会产生一个新的对象。
路由对象出现的地方:
- 组件内,this.$route
- 在$route观察者回调内
- router.match(location)的返回值
- 导航守卫的参数 to/from
- scrollBehavior的参数 to/from
路由对象的属性
$route.path:字符串,当前路由的路径,总是解析为绝对路径。
$route.params:一个key/value对象。路由参数组成的对象。
$route.query:一个key/value对象,表示URL查询参数。
$route.hash:当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。
$route.fullPath:完成解析后的URL,包含查询参数和hash的完整路径
$route.matched:Array<RouteRecord> 包含当前路由的所有嵌套路径片段的路由记录。
$route.name 当前路由的名字 如果有的话
$route.redirectedFrom:如果存在重定向,则为重定向来源的路由的名字。
组件注入
注入的属性:通过在Vue根实例的router配置传入router实例,this.$router/this.$route会被注入到每个子组件。
this.$route是当前激活的路由信息对象。这个属性是只读的,可以通过watch检测它的变化。