1.什么是vue-router

vue-router是vue.js官方给出的路由解决方案,只能结合vue项目进行使用,能够轻松的管理SPA项目中组件的切换

2.vue-router的用法

  • 先安装vue-router (npm i vue-router)
  • 在新建router/index.js 文件,在文件内导入Vue和vue-router(新建的路径由自己决定)
    // 导入Vue和VueRouter的包
    // router/index.js文件
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    
    // 调用Vue.use()函数,把VueRouter安装为Vue的插件
    Vue.use(VueRouter)
    
    const Home = () => import("../home.vue");
    
    // 创建路由的实例对象
    const router = new VueRouter({
        // routes是一个数组,定义"hash地址"与"组件"之间的关系
        routes:[
            // 重定向属性,当刚打开页面,并没有点击跳转时,则设置首页面渲染
            {path: '/',redirect: '/home'},
            {path: '/home',component: Home}
        ]
    })
    
    
    // 导出的目的是为了在main.js进行导入, 将路由实例对象挂载在 new Vue()里面
    export default router

在组件中vue-router的用法

// app.vue组件

<template>
    <div>
        // router-link来替代普通的a连接 to为组件路径
        <router-link to='/home'></router-link>
        // 只要在项目中安装和配置了vue-router,就可以使用router-view这个组件
        // 作用 : 占位符,表示渲染的内容的位置
        <router-view></router-view>
    </div>
</template>

嵌套路由的使用

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

Vue.use(VueRouter)
 //懒加载,打包的时候会分离,使用的时候才会加载 可减小js体积
const Home = () => import('Home.vue')
const tab = () => import('Tab.vue')
const router = new VueRouter({
    routes: [
        {
            path: '/home',
            component: Home,
            // 子路由的重定向
            redirec: '/home/tab',
            // 子路由
            children: [
                // 默认子路由 如果children中的path为空,则这条路由规则,就叫做默认子路由
                {
                    path: 'tab', // 子路由路径不用加/
                    component: Tab
                }
            ]
        }
    ]
})

export default router
// app.vue组件

<template>
    <div>
        // 路径为子路径,点击后跳转到子路径
        <router-link to='/home/tab'></router-link>
        // 若使用的是默认子路由,则to后面 /tab可以省略
        <router-link to='/home'></router-link>
        <router-view></router-view>
    </div>
</template>

3.动态路由的使用

// 需求: 在Movie组件中,希望根据id的值,展示对应电影的详情信息

// 这里path路径中有参数,可以开启props传参,是为了方便Movie组件拿到参数值
// 在对象中加入props:true
// 如果路径中没有参数,则开启props传参也没有意义
{path: '/movie/:id',component: Movie,props: true}

// id的值会存在放Vue实例中的 $route.params.id中

// Movie.vue
//则这里就可以直接拿到id的值
props: ['id']

命名路由

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

Vue.use(VueRouter)

const Home = { template: '<div>This is Home</div>' }
const Foo = { template: '<div>This is Foo</div>' }
const Bar = { template: '<div>This is Bar {{ $route.params.id }}</div>' }

const router = new VueRouter({
  mode: 'history',
  base: __dirname,
  routes: [
    { path: '/', name: 'home', component: Home },
    { path: '/foo', name: 'foo', component: Foo },
    { path: '/bar/:id', name: 'bar', component: Bar }
  ]
})

new Vue({
  router,
  template: `
    <div id="app">
      <h1>Named Routes</h1>
      <p>Current route name: {{ $route.name }}</p>
      <ul>
        <li><router-link :to="{ name: 'home' }">home</router-link></li>
        <li><router-link :to="{ name: 'foo' }">foo</router-link></li>
        // 若这里是path,则不可以写params,写了不会生效
        <li><router-link :to="{ name: 'bar', params: { id: 123 }}">bar</router-link></li>
      </ul>
      <router-view class="view"></router-view>
    </div>
  `
}).$mount('#app')

多个路由指向同一个组件时,组件内的生命周期钩子不会被调用了,这是因为组件被复用了,如以下代码都是指向Home组件

const routes = [
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: Home
  }
]


// 使用watch则可以进行监听,对路由参数做出响应
watch: {
    '$route'(to,from){
      console.log(from)
    }
}

// 也可以在组件内使用如下
beforeRouteUpdate(to, from, next) {
    // react to route changes...
    // don't forget to call next()
}

扩展:

a.在hash地址中,/后面的参数项,叫做'路径参数',路由中路径参数放在params中

b.在hash地址中,?后面的参数项,叫做”查询参数”,路由中查询参数放在query中

c.在this.$route中,path只是路径部分,不包含查询参数,而fullPath则是完整路径

3.编程式导航API

// 跳转到指定hash地址,并增加一条历史记录(跳转完可以后退)

this.$router.push('/movie/1');

// 跳转到指定的hash地址,并替换当前的历史记录(跳转完无法后退)

this.$router.replace('/movie/1');

// 在浏览历史前进或后退(不常用)

this.$router.go(-1) // 后退1步

// 后退一步
this.$router.back();

// 前进一步
this.$router.forward();



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

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

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

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

注意:如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况

const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

4.导航守卫

记住参数或查询的改变并不会触发进入/离开的导航守卫(这个是指组件内的beforeRouteEnter)。你可以通过观察 $route 对象(watch)来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

注意: 如果是全局守卫和组件内的beforeRouteUpdate和beforeRouteLeave在动态路由跳转时也会调用触发守卫

全局前置路由守卫

// router/index.js

// to是将要访问的路由的对象
// from 是将要离开的路由的对象
// next是一个函数,调用next() 表示放行,允许这次路由导航
router.beforeEach((to,from,next) => {
    next();
})

// next函数的3中调用方式
// 当前用户拥有后台主页的访问权限,直接放行 next();
// 当前用户没有后台主页的访问权限,强制跳转登录页面 next('/login')
// 当前用户没有后台主页的访问权限,不允许跳转到后台主页  next(false);

使用场景:使用前置路由守卫模拟登录(代码展示)

router.beforeEach((to, from, next) => {
  // 判断要跳转的页面是不是登录页,如果是的话,就直接同意跳转
  
  if (to.path === '/login') {
    return next();
  }
  // 如果不是,则判断浏览器里是否存储了token值,判断登录是否过期,如果不存在token或者过期
   //则强制跳转到登录页重新进行登录
  else if (!localStorage.getItem('token')) {
    return next('/login');
  }
  // 最后则是有token值,则让其正常跳转
  next();
})

以上则是一个模拟登录功能的简单实现,但以上功能还没有完善,只是大概结合全局前置路由守卫进行实现

全局后置钩子

// 不会接受 next 函数也不会改变导航本身:
router.afterEach((to, from) => {
  // ...
})

路由独享守卫(这些守卫与全局前置守卫的方法参数是一样的)

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

组件内守卫

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` 访问组件实例
  })
}

beforeRouteUpdate的注意点:beforeRouteUpdate是路由更新时触发,从主页进入登录界面不会触发这个钩子函数,一个父路由下的子路由跳转会触发这个钩子函数

beforeRouteLeave这个离开守卫通常用来禁止用户在还未保存修改前突然离开

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

路由元信息

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,

      meta: { requiresAuth: true },
      children: [
        {
          path: 'bar',
          component: Bar,
          
        }
      ]
    }
  ]
})

当/foo可以匹配父路由记录,/foo/bar可以匹配子路由记录,$route.matched 数组会把每一层路由记录都放在里面,如当访问/foo/bar时,/foo的路由记录也在to.matched数组内,有时候我们需要检查meta中的属性,但当我们直接访问子路由时,to.meta中并没有这个属性,但我们访问matched中的记录,便可以访问到父路由中的meta属性

// 比如在检查是否需要登录时,因为只有父组件有meta可以判断是否需要回到登录页
// 这时候我们去个每一个二级路由去加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()
  }
})

Logo

前往低代码交流专区

更多推荐