学习vue-router

1,042 阅读3分钟

vue-router 基础

  1. 作用: 通过管理URL,实现URL和组件的对应和通过URL进行组件之间的切换
  2. 其他概念:

单页应用:加载单个HTML页面,并在用户与应用程序交互时,动态该页面

  1. 使用步骤:

    • 安装模块: npm install vue-router --save

    • 引入模块: import VueRouter from 'vue-router'

    • 作为插件:Vue.use(VueRoter)

    • 创建路由实例对象:

      
      new VueRouter({
          // 匹配相关参数
      })
      
      
    • 注入vue选项参数: new VueRouter({ router })

    • 告诉路由渲染的位置:

    • 引入组件,匹配组件URL:<router-link v-bind:to="path"> 内容 </router-link>

    具体页面代码:

    
    import Vue from 'vue'
    // 1. 安装vue-router后引入vue-router模块
    import Router from 'vue-router'
    import Index from '@/views/index'
    
    // 2. 作为插件
    Vue.use(Router)
    
    // 3. 创建路由实例对象
    export default new Router({
    mode : 'history',// 历史模式
    linkActiveClass: 'is-active', // 活动样式
    scrollBehavior(to, from, savePosition){
        // 点击浏览器的前进后退或切换导航时触发
        console.log(to); // 要进入的目标对象  要去哪
        console.log(from); // 离开的目标  从哪来
        console.log(savePosition); // 记录滚动条位置,点击前进后退时触发
    
        // if(savePosition){
        //   return savePosition; // 滚动条位置不是0时,即可保存位置
        // }else{
        //   return {x:0,y:0} 
        // }
    
        if(to.hash){
        return {
            selector: to.hash
        }
        }
    },
    routes: [
        {
        path: '/',
        name: 'Index',
        component: Index
        }
    ]
    })
    
    
    

    动态路由匹配

    动态路径参数 以冒号开头

    {
        path:'/user/:id',component: User
    }
    

    一个“路径参数”使用冒号:标记。当匹配到一个路由时,参数会被设置到this.$route.params,可以在每个组件内使用。
    也可以在一个路径张红设置多段“路径参数”,对应的值都会设置到$route.params

    • 捕获所有路由或404 Not found路由 {path: '*'}

    当使用了一个通配符时,$route.params内会自动添加一个名为pathMath参数。


编程式的导航

除了使用<router-link></router-link>创建a标签来定义导航链接,还可以通过编写代码来实现。

router.push(location, onComplete?, onAbort?)
注意:在vue实例内部,可以通过$router访问路由实例

想要导航到不同的URL,则使用router-push方法。这个方法会向history栈添加一个新的记录

该方法的参数可以是一个字符串路径,或者一个描述地址的对象

    // 字符串
    router.push('home')

    // 对象
    router.push({ path: 'home' })

    // 命名的路由
    router.push({ name: 'user', params: { userId: '123'} })

    // 带查询参数,变成 /register?plan=private
    router.push({ path: 'register', query: { plan: 'private' } })

router.replace(location, onComplete?, onAbort?) :替换掉当前的history记录

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


命名视图

<router-view name='slider'></router-view>
路由视图<router-view>一般的默认命名为default,当想要匹配不同的视图时,便可以通过命名,找到相应的视图组件。name值的匹配


## 重定向和别名 ``` const router = new VueRouter({ routes: [ {path: '/a', redirect: '/b'} ] }) ``` 或者是动态返回重定向目标:

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

注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。


路由组件传参

在组件中使用$route会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的URL上使用,限制了灵活性。

使用props将组件和路由解耦: 取代与$route的高耦合

    const User = {
    template: '<div>User {{ $route.params.id }}</div>'
    }
    const router = new VueRouter({
    routes: [
        { path: '/user/:id', component: User }
    ]
    })

通过 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 }
    }
  ]
})

三大模式

  • 布尔模式

如果props 被设置为true,route.params将会被设置为组件属性

  • 对象模式

如果props是一个对象,它会被按原样设置为组件属性。当props是静态的时候有用

  • 函数模式

可以创建一个函数返回props。这样就可以将参数转换成另一种类型,将静态值与基于路由的值结合等。

尽可能保持props函数为无状态的,因为它只会在路由发生变化时起作用。如果需要状态来定义props,请使用包装组件,这样vue才可以对状态变化做出反应

导航守卫

组件内的守卫


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传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了

    beforeRouteUpdate (to, from, next) {
    // just use `this`
    this.name = to.params.name
    next()
    }

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 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)
  }
}

完整的导航解析流程

  1. 导航被触发
  2. 在失活的组件里调用离开守卫。
  3. 调用全局的beforeEach守卫
  4. 在重用的组件里调用beforeRouteUpdate
  5. 在路由配置里调用beforeEnter
  6. 解析异步路由组件
  7. 在被激活的组件里调用beforeRouteEnter
  8. 调用全局的beforeResolve守卫
  9. 导航被确认
  10. 调用全局的afterEach钩子
  11. 触发DOM更新
  12. 用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数

路由元信息

一个路由匹配到的所有路由记录会暴露为$route对象(还有在导航守卫中的路由对象)的$route.matched数组。因此,我们需要遍历$route.matched来检查路由记录中的meta字段

下面例子展示在全局导航守卫中检查元字段:

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 确保一定要调用 next()
  }
})

数据获取

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

导航完成后获取数据

马上导航和渲染组件,然后在组件的created钩子中获取数据。获取数据期间会展示loading状态,可以在不同视图间展示不同的loading状态。