Better

Ethan的博客,欢迎访问交流

Vue-router使用

关于阅读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` 访问组件实例
          })
      }
      
  • 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')


留言