关于阅读Vue-router doc的一点总结!
简介
路由的目的是当我们访问某路径时,去渲染对应的组件,核心功能就是:将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。
采用NPM的方式,顺便演示基础使用
import Vue from 'vue'
import VueRouter from 'vue-router'
// 1. 开启router功能
Vue.use(VueRouter)
// 2. 创建路由
const router = new VueRouter({
// 核心参数 routes
routes:[
{
path:'',
component:mycomponent
}
]
})
// 3. 注册路由
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
route
我们使用path定义具体路径,注意路由可以是参数,路由参数直接使用:
开头即可。
嵌套路由
URL 中各段动态路径也按某种结构对应嵌套的各层组件,路由对应的组件中也可以有<router-view>
组件,这就是嵌套组件的由来,定义嵌套组件需要 children
参数,children
参数是一个数组,对象格式其实就是路由对象。
要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
也就说 children
下的 path
不能 /
开头
缺省路由
如果都不匹配的话,可以提供一个path
为空的参数来作为缺省路由
components
我们常用的使用component属性,但是在命名视图的情况下,我们可以通过components属性指定多个组件。格式如下
components:{
routerViewName:Component,
// default 关键字表示默认
default:Component
}
$route解耦
我们通常会使用$route获取参数什么的,但这样会使组件和对应路由形成高度耦合。使用 props 将组件和路由解耦。
如果 props 被设置为 true,route.params 将会被设置为组件属性。
如果 props 是一个对象,会直接将此设置为组件属性。当 props 是静态的时候有用。
你还可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
如果是命名视图,即使用components指定多个组件,则props需要在套一层,格式如下
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
路由独享守卫
路由配置上直接定义 beforeEnter 守卫
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
路由元信息
我们可以给路由定义一些元信息,通过meta字段即可,比如该页面是否需要权限等。这可以帮助我们做权限控制等。
首先需要确定一个概念:routes 配置中的每个路由对象为路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录
一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航守卫中的路由对象)的 $route.matched 数组。因此我们可以通过 $route.matched 拿到所有路由记录的元信息
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保一定要调用 next()
}
})
更多属性
重定向:redirect 属性
alias:定义别名,别名的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
路由组件
vue-router 主要提供两个核心路由组件供我们使用,一个是<router-link>
和<router-view>
,前者提供组件的方式进行跳转,后者提供渲染指定组件的容器。
声明式导航
使用<router-link>
方式进行跳转,我们称之为声明式导航,本质调用的$router.push
当 <router-link
> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active
。我们可以用来控制选中的样式。
命名视图
有时候想同时(同级)展示多个视图,而不是嵌套展示,这个时候命名视图就派上用场了。
如果<router-view>
不设置name属性的话,那就是默认容器,在应用中,我们也可以提供多个<router-view>
,并通过name属性标识,这样就是依赖一个路由可以指定多个组件。
扩展Vue组件
通过注入路由器,我们可以在任何组件内通过 this.$router
访问路由器,也可以通过 this.$route
访问当前路由。
this.$router
可以使用 this.$router
提供的方法进行路由切换,比如go、push、replace,这种方式成之为编程式导航。我们可以根据path和name进行跳转,同时可以传参,具体方式如下
router.push('home') // path way
router.push({ path: 'home' })
router.push({ name: 'user', params: { userId: 123 }})
router.push({ path: 'register', query: { plan: 'private' }})
// 如果提供了 path,params 会被忽略,因此如果需要传路径参数,只能使用写在path中
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
上述的规则,同样适用于 router-link 组件的 to 属性。
在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。
this.$route
可以使用 this.$route
的param属性获取路径参数。可以在一个路由中设置多段『路径参数』,对应的值都会设置到 $route.params 中。
$route 对象还提供了其它有用的信息,例如,$route.query(如果 URL 中有查询参数)、$route.hash 等等。
组件内守卫
可以在路由组件内直接定义以下路由导航守卫,接受参数均为to, from, next:
- beforeRouteEnter:在渲染该组件的对应路由被 confirm 前调用
- 不能访问组件实例this,因为组件还没创建,可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 }) }
- 不能访问组件实例this,因为组件还没创建,可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
- beforeRouteUpdate (2.2 新增):在当前路由改变,但是该组件被复用时调用
- beforeRouteLeave:导航离开该组件的对应路由时调用,离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
VueRouter
全局导航守卫
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
全局前置守卫
const router = new VueRouter({ ... })
/*
to:将要进入的目标路由对象
from:导航正要离开的路由
next:function,一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
*/
router.beforeEach((to, from, next) => {
// ...
})
- next():表示放行
- next(false):中断当前导航,
- next('/') 或者 next({ path: '/' }):当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象
- next(error):导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
全局后置钩子
// 不会接受 next 函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
滚动行为
创建VueRouter传递的options的核心参数是routes,这里讲一个不太重要的scrollBehavior
。
scrollBehavior
方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。这个可以帮助我们进行滚动控制,比如滚动到顶部,或者之前的位置
进阶
路由参数变化
使用路由参数,只是参数发生变化时,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用效率更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
如果需要对参数变化做出相应的话,解决办法如下:
- watch(监测变化) $route 对象
- 使用 2.2 中引入的 beforeRouteUpdate
匹配优先级
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
过渡动效
本质就是 Vue 提供的动画组件<transition>
,可以直接加在<router-view>
上,给所有组件应用统一的样式,也可以直接加在组件模版上,并且可以给<transition>
设置不同的 name 从而应用特有的样式。
还可以基于当前路由与目标路由的变化关系,动态设置过渡效果:
<!-- 使用动态的 transition name -->
<transition :name="transitionName">
<router-view></router-view>
</transition>
<!-- 接着在父组件内 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'
}
}
数据获取
进入某个路由后,需要从服务器获取数据。我们可以通过两种方式来实现:
- 导航完成之后获取:组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。常用 created 事件
- 在导航完成前获取数据:beforeRouteEnter 或 beforeRouteUpdate 事件
懒加载
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
有两种方式,一种是 require 语法,一种是动态 import,DEMO如下
component: resolve => require(['@/pages/login/login.vue'], resolve),
component: () => import('@/pages/task')