vue Router


vue Router

Vue Router

基础知识

注入路由后,可以通过this.$router访问路由器,this.$route访问当前路由

动态路由

匹配特定路径

routers:[psth:'/user/:id',component: User]

/user/A/user/B都将匹配到相同的路由

动态路径参数使用:标记,可以通过this.$route.params,例如访问上面的idthis.$route.params.id

/user/A转到/user/B的时候,组件将被复用,生命周期将不会被调用,如果想根据路由参数进行响应的话:

  1. 监测$route对象

    watch:{
        $route(to,from){
            //do something
        }
    }
  1. 使用路由守卫
beforeRouteUpdate(to,from,next){
    //do something
}

匹配任意路径(捕获404)

使用通配符*,当使用通配符的时候,$route.params会自动添加一个名为pathMatch的参数,包含通过通配符被匹配的部分

匹配优先级

同一个路径可以匹配多个路由,匹配的优先级是:谁先匹配,谁的优先级更高

嵌套路由

<router-view></router-view>是最顶层的出口,渲染最高级路由匹配的组件,被渲染的组件也可以包含<router-view></router-view>,需要配置children配置:

routers:[psth:'/user/:id',component: User,
                    children:[{
                        path:'', component: 
                    },
                    .....]
]

命名路由

可以通过一个名称来标识一个路由

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 }})

编程式路由

router指代this.$router

router.push(location, onComplete?, onAbort?)

声明式 编程式
router.push(…)

参数可以是字符串路径或描述地址的对象

//字符串
router.push('home')
//对象
router.push({path:'home'})
//命名的路由
router.push({name:'user',params:{userId:'123'}})
//带查询参数
router.push({path:'register',query:{plan:'private'}})

如果提供了pathparams会被忽略掉

// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

同样的规则也适用于router-linkto属性

router.repace(location, onComplete?, onAbort?)

声明式 编程式
Router.replace(…)

不会向history里面添加记录

onComplete 和 onAbort

导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用

router.go(n)

在history记录中前进或后退多少步,类似于window.history.go(n)

对比history

Vue router history
router.push(…) window.history.pushState(…)
router.replace(…) window.history.repalceState(…)
router.go(n) window.history.go(n)

命名视图

同级展示多个视图,可以在界面中拥有多个单独命名的视图,而不是一个出口,如果router-view没有设置名字,那么默认为dafault

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

重定向和别名

重定向通过routes来配置

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

redirect也可以是一个命名的路由,也可以是一个方法

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

别名对应的配置如下:

const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

路由组件传参

在组件中直接使用$route会使其与对应路由形成高度耦合,使用props将组件和路由解耦

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

https://jsfiddle.net/posva/6du90epg/

hash和history模式(针对单页应用SPA)

前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。

hash——即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。 比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

已经有 hash 模式了,而且 hash 能兼容到IE8, history 只能兼容到 IE10,为什么还要搞个 history 呢?
首先,hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。其次,hash 的传参是基于 url 的,如果要传递复杂的数据,会有体积的限制,而 history 模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。

进阶

导航守卫

导航表示路由正在发生变化,查询参数的变化并不会触发进入/离开的导航守卫

  • 全局前置守卫
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发的时,全局前置守卫按照创建顺序调用

next:Function:一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-linkto proprouter.push 中的选项。

next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

  • 全局后置钩子
router.afterEach((to, from) => {
  // ...
})
  • 路由独享守卫
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
  • 组件内的守卫
    • beforeRouterEnter
    • beforeRouterUpdate
    • beforeRouterLeaver
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdatebeforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

文章作者: 木叶勇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 木叶勇 !
  目录