vue-router3.x详细使用讲解

vue-router3.x详细使用讲解

vue-routervue官方的路由管理器,它和 Vue 的核心深度集成,让构建单页面应用变得很简单。

文章介绍的vue-router只针对v3.x版本。

安装

在引入Vuevue-router后使用Vue.use方法安装路由功能。

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

如果是通过script引入的文件,则不需要使用Vue.use方法,它会自动安装。

<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>

只需要在vue后面引入vue-router即可,因为在vue-router源码里已经执行了安装:

// vue-router/src/index.js
...
if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter)
}

简单介绍

定义路由

const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]
const router = new VueRouter({
  routes
})

传入路由配置并初始化一个VueRouter实例,将实例作为配置参数传递到Vue实例中,即可让整个应用拥有路由功能。

new Vue({
  router
})

vue-router提供了router-linkrouter-view两个组件来实现路由的跳转及视图的切换。

router-link

<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签。

<router-link to="foo">to foo</router-link>
<router-link to="bar">to bar</router-link>

router-view

<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。

在创建Vue的时候传入template

new Vue({
  el: '#app',
  router,
  template: "<div><router-link to='foo'>to foo</router-link><router-link to='bar'>to bar</router-link><router-view/></div>"
})

路由配置

动态路由

在配置路由时使用“动态路径参数”,来实现多个路径全部映射在同一个组件上。

{path: '/foo/:id', component: Foo}

一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params中,这样即便传递的参数不同,都是通过渲染同一个组件,在组件中通过this.$route.params来获取不同的数据进行展示。

比如上面的例子在地址栏输入xxx/foo/1,可以通过this.$route.params.id获取到值为"1"

如果输入的地址没有携带id,比如xxx/foo,没有命中设置的path,就不会渲染组件。

多段路径参数

也可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params

{path: '/foo/:id/post/:postId', component: Foo}

此时如果输入的地址为xxx/foo/1/post/2,那么this.$route.params的值为{id: "1", postId: "2"}

存在问题,当存在动态参数时候点击router-link跳转到其他页面,路径会叠加而不是重置

组件复用

当只改变路由参数时,比如从/foo/1导航到/foo/2,原来的组件会复用,不会进行进行重新渲染,也就意味着组件的生命周期钩子不会在被调用。

在这种情况下,可以直接通过watch来对this.$route进行监听,实现数据的更新。

watch: {
  $route(to, from) {
	// ...
  }
}

还有一种方式是通过路由守卫来实现,在引入了vue-router后,给Vue添加了几个方法。
beforeRouteEnterbeforeRouteLeavebeforeRouteUpdate

const Foo = {
  template: `<div @click="updateId">foo{{$route.params.id}}</div>`,
  methods: {
    updateId() {
      this.$router.push('/foo/'+Math.random());
    }
  },
  beforeRouteUpdate(to, from, next) {
    // 必须调用next方法,不然不会更新路由
    next();
  }
}
路由传参

前面提到可以通过$route.params来获取传递给组件的值,但是这样会使得与对应组件高度耦合,让组件只有某些特定的url上使用。

比如:

const Foo = { template: `<div @click="updateId">foo{{$route.params.id}}</div>`}

可以使用props来解耦

const Foo = { 
  props: ['id'],
  template: `<div @click="updateId">foo{{id}}</div>`
}
const routes = [
  {
    path: '/foo/:id',
    component: Foo,
    props: true
  }
]

routes上配置propstrueroute.params 将会被设置为组件属性。

当然可以设置一个对象。

const routes = [
  {
    path: '/foo/:id',
    component: Foo,
    props: { fromUrl: true }
  }
]

对于动态的值,可以传递一个方法

const getDynamicValue = () => {
  return Math.random();
}
const routes = [
  {
    path: '/foo/:id',
    component: Foo,
    props: { dynamicValue: getDynamicValue }
  }
]
路由匹配
*通配符

常规参数只会匹配被 / 分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 *

{
  // 会匹配所有路径
  path: '*'
}
{
  // 会匹配以 `/foo-` 开头的任意路径
  path: '/foo-*'
}

如果设置了通配符,一定要确保路由的顺序,把设置通配符的路由放在最后,不然所有的路由的都会被通配符捕获从而导致渲染异常。路由 { path: '*' } 通常用于客户端 404 错误。

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

// 给出一个路由 { path: '/foo-*' }
this.$router.push('/foo-1')
this.$route.params.pathMatch // '1'
// 给出一个路由 { path: '*' }
this.$router.push('/foobar')
this.$route.params.pathMatch // '/foobar'

源码里的matchRoute方法里添加了pathMatch属性

function matchRoute (
  regex: RouteRegExp,
  path: string,
  params: Object
): boolean {
  const m = path.match(regex)
  // ...
  for (let i = 1, len = m.length; i < len; ++i) {
    const key = regex.keys[i - 1]
    if (key) {
      params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i]
    }
  }
}
高级匹配模式

由于vue-router 使用 path-to-regexp 作为路径匹配引擎,因此支持很多高级的匹配模式。例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。更多可以查看path-to-regexp

命名路由

在定义的路由的时候添加 name属性

const routes = [
  { path: '/foo/:id', component: Foo, name:'fooName' },
  { path: '/bar', component: Bar, name: 'barName' }
]

to属性传递一个对象,不能跟传递路径一样只传一个string。

<router-link :to="{name: 'fooName', params: {id: 1}}">to foo</router-link>

上述的跳转的跟使用router的方法一样。

router.push({name: 'fooName', params: {id: 1}})

嵌套路由

对于一些多层嵌套的组件,可以使用嵌套路由来表示。

routes配置中提供了一个children字段来实现嵌套。

const Foo = { template: `<div><h1>Foo view</h1><router-view></router-view></div>` }
const Bar = { template: `<div>Bar view</div>` }
const Profile = { template: `<div>Profile view</div>` }
const routes = [
  {
    path: '/foo/:id',
    component: Foo,
    children: [
      {
        // 当路径满足 /foo/:id/bar时就会在Foo组件的router-view中渲染Bar组件
        path: 'bar',
        component: Bar
        // 同样可以配置childrens,可以实现多层桥套
      },
      {
        // 当路径满足 /foo/:id/bar时就会在Foo组件的router-view中渲染Bar组件
        path: 'profile',
        component: Profile
      }
    ]
  }
]

注意,在上述配置中,如果访问/foo/:id路径,因为没有匹配到子路由,所以组件内的router-view不会渲染任何东西。如果想默认渲染,可以提供一个空路由。

const routes = [
  {
    path: '/foo/:id',
    component: Foo,
    children: [
      // ...
      {
        path: '',
        component: { template: `<div>default view</div>` }
      }
    ]
  }
]

只能用''而不能用*去匹配。

路由重定向

直接在routes中添加一个rediect字段,这样所有访问/foo的页面都会重定向到/bar

{ path: '/bar', name: 'bar', component: Bar},
{ path: '/foo', redirect: '/bar' }

也可以是一个命名路由对象

{ path: '/foo', redirect: { name: 'bar' } }

也可以是一个函数

{ path: '/foo', redirect: (to) => {
  // 只接收了一个目标路由作为参数
  // 需要return 出可以重定向的路由路径/对象,否则不渲染组件
} }

命名视图

非嵌套,同级展示视图,可以使用命名视图。

<router-view class="header"></router-view>
<router-view class="content" name="content"></router-view>
<router-view class="bottom" name="bottom"></router-view>

在路由配置中添加多个组件,

const Header = { template: '<h1>Header</h1>'}
const Content = { template: '<h1>Content</h1>'}
const Bottom = { template: '<h1>Bottom</h1>'}
const routes = [
  { path: '/',
    components: {
      default: Header,
      content: Content,
      bottom: Bottom
    }
  },
]

路由守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。

有多种方式可以植入路由导航:

全局

全局前置守卫

使用router.beforeEach方法注册全局前置守卫,所有路由在跳转前都会执行这个方法。

const router = new VueRouter({...})
router.beforeEach((to, from, next) => {
  next();
})

beforeEach接收三个参数:

  • to: 要跳转的目标路由对象
  • from:要离开的当前路由对象
  • next:一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-linkto proprouter.push 中的选项。
    • next(error): 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
全局解析守卫

router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

全局后置钩子

在路由跳转完后执行,这个与守卫不同的是不接收next参数,因为它并不会影响导航本身。

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

路由独享

routes配置上直接定义beforeEnter守卫(路由独享守卫只有beforeEnter一个)。

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内守卫

直接在组件上定义就行,有以下三种:

  • beforeRouteEnter: 在渲染该组件的对应路由被 confirm 前调用,此时组件实例还没有被创建,因此不能使用this
  • beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用。比如对于带有动态参数的路径来说,/foo/:id,只改变id,组件实例被复用就会执行这个钩子。
  • beforeRouteLeave:导航离开该组件的对应路由时调用。

虽然beforeRouteEnter在组件实例未被创建之前执行,不能访问this。但是可以通过给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

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

导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值