Vue路由 —— vue-router

          回顾前两篇内容讲到一级路由、声明式导航、编程式导航、路由的重定向、嵌套路由和动态路由,那么在本篇接着来讲关于Vue路由相关的内容:

命名路由        

        命名路由,顾名思义,有命名的路由,同样还是将上一篇的案例继续进行:

        在 /router/index.js 文件中配置路由信息 —— 进行路由命名

<!-- /router/index.js -->
...
  {
    path: '/play/:id',
    component: Play,
    name: 'playMusic'
  }
]
...

params

        在路由信息添加一个 name 并赋值 'playMusic' 的路由名;那么添加这个路由名称可以做什么呢?上一篇内容通过选择歌曲点击跳转播放页,使用了 js 的方式 (location.href) 和 vue-router 提供的编程式导航方式 ( this.$router.push(`/play/${id}`) ),现在有了路由名称,依然可以通过vue-router 提供的编程式导航方式,下面来调整编写代码:

<!-- /views/music/KuGou.vue -->
...
  methods: {
    handlePlay (id) {
      // alert('获取歌曲id:' + id)
      // location.href = '#/play'
      // 编程式导航
      // this.$router.push(`/play/${id}`)
      this.$router.push({ name: 'playMusic', params: { id: id } })
    }
  }
}
</script>

        下面来进行测试运行一下:

query

        这里用到的是一个动态路由的方式来完成,通过选择歌曲,携带歌曲的id跳转到播放页,通过歌曲id去请求歌曲的播放链接,在没使用动态路由之前,使用的是 this.$router.push(`/play/${id}`),出现如下这种情况:

        在路由中并没有配置 /play/10001 的路由信息,且歌曲不止十几条,如果通过去配置这样的路由信息显然就不合理!但还不使用动态路由可以怎么来完成呢?

<!-- /router/index.js -->
...
  {
    path: '/play',
    component: Play,
    name: 'playMusic'
  }
]
...

        动态路由所携带的参数是 /play/:id 格式,那么如果携带的参数是 /play?id= 格式,可以通过什么获取?—— query

<!-- PlayView.vue -->
<template>
    <div>
        <h3>播放界面</h3>
        当前歌曲Id —— {
   
   { songId }}
    </div>
</template>

<script>
export default {
  mounted () {
    // this.songId = this.$route.params.id
    this.songId = this.$route.query.id
    // console.log(this.$router)
    console.log(this.$route)
  },
  data () {
    return {
      songId: ''
    }
  }
}
</script>

        下面来测试运行一下:

         那么关于这块内容就已然告一段落,将携带的id送到播放页,播放通过ajax去请求后端得到播放链接在播放页当中播放就解决了;那么下面就来讲关于路由模式:

路由模式

        Vue 支持两种路由模式 : 1. hash 模式        2.history 模式

hash模式:        http://localhost:8080/#/music/
history模式:     http://localhost:8080/music

        上面的例子当中使用的是 hash 模式:

        如何修改为 history 模式呢?在 /router/index.js 中添加 mode: 'history' 就可以了;

<!-- /router/index.js -->
...
const router = new VueRouter({
  mode: 'history',
  routes
})

export default router

        添加完成之后,将项目进行重新运行 npm start ;

         history 模式显然比起 hash模式 看起来比较好看,但 history 模式存在问题?这种模式需要后端配置支持 ,因为应用是单页客户端应用,如果后端没有正确的配置,当用户在浏览器直接访问是就会返回 404 ,这就不好看了 !所以需要在服务端增加一个覆盖所有情况的候选资源:如URL匹配不到任何静态资源,则应该返回同一个 index.html 页面,该页面就是app所依赖的页面。

具体可以看一下这篇内容:不同的历史模式 | Vue Router

路由原理:

1) hash 路由 :

        知道什么时候路径会发生改变,可以通过BOM提供的 window.onhashchange 监听路径的改变,而 location.hash 可以进行路径的切换且获得 hash 值;

2)  history路由 :

        利用H5新特性,通过BOM提供的 window.onpopstate 监听路径的改变,改变路径可以进行更新,通过history.pushState 进行路径的切换;

路由拦截

        路由拦截是什么呢?举一个非常常见的场景,当你打开一个网站的时候,你想浏览该网站的时候,当你点击阅读的时候它会突然跳转到一个登录界面,如果你还没有选择登录继续点击阅读其他内容时,又弹出来一个登录界面,当你登录的时候,就不会再出现了;当你跳转想去阅读的时候突然被登录给拦截下来;那么一般的进入某些页面时,通过什么手段来检查是否有权限去继续阅读,比如登录可以检查是否存在token,如果有的话可以继续阅读不拦截,如果没有的话给它拦截下来跳转去登录界面登录获取token;下面来看一种大家常见的写法:

...
<script>
export default {
    mounted () {
        // 检查是否有token
        if (!localStorage.getItem('token') {
            this.$router.push('/login')
        })
    }
}
</script>

         这种显然没有任何问题,但是不是最好的,如果需要检查的页面多了,那么就需要在每一个组件当中去检查是否有token,需要检查的都需要编写,这样一来不仅重复代码很多,写起来很难受,有没有一种好的方法呢?有,就是下面要讲的路由守卫,叫法比较多,但不重要;

        路由守卫分为全局守卫和组件内部守卫,非常好理解,如果大部分组件都需要进行检查登录再进入就可以用全局,如果是局部或者少部分地方会涉及到检查登录,可以用组件内部守卫的方式,下面来使用一下:

beforeEach —— 全局前置路由

        当 http://localhost:8080/music 切换到 http://localhost:8080/music/kugou 时则需要进行路由的拦截检查,在 /router/index.js 中编写全局前置路由:

<!-- /router/index.js -->
...
const router = new VueRouter({
  mode: 'history',
  routes
})

router.beforeEach((to, from, next) => {
  // 拦截
  if (to.fullPath === '/music/kugou') {
    if (!localStorage.getItem('loginToken')) {
      console.log('请先登录')
    } else {
      next()
    }
  } else {
  // 放行
    next()
  }
})

export default router

测试运行一下效果:

         如果是有多个路径,可以编写成数组的形式:

const auth = ['/music', '/order', 'monkey']
  if (auth.includes(to.auth)) {
    console.log('请先登录')
  } else {
    next()
  }

beforeRouteEnter —— 组件内部守卫

        那么在某个组件内部需要这种拦截就可以使用组件内部守卫,在进来这个路由前就进行拦截检查:beforeRouteEnter

<!-- PlayView.vue -->
...
<script>
    export default {
        beforeRouteEnter (to, from, next) {
            if (!localStorage.getItem('loginToken')) {
              console.log('检查token')
            } else {
              next()
            }
        }
    }
</script>

        那么有些初学者要问了,不可用mounted吗?在mounted生命周期已经加载完了,而使用组件守卫beforeRouterEnter时组件实例还没有被创建,在路由尚未切换过来的时候就判断是否有登录的token,没有就不会去切换,如果有loginToken则会选择next()对路由进行放行;

当然关于路由守卫的内容想更加深入了解的请到这边 —— 官方文档地址:导航守卫 | Vue Router

路由懒加载

        在讲路由懒加载前先来讲它所处理的一个常见场景,是否有打开过一些网站,进入网站的时候页面加载了很久多没有显示出来;此时它可能在加载好些文件,比如打包好后的一些js、css文件以及一些静态资源;

         有时网站持续了好久才能打开,心急的可能就开始骂起来了,什么网站打开要这么久?有的就误以为需要 "科学上网" 才能进入网站,所以呢就会导致用户体验不佳,尽管网站中内容很丰富,但用户进入网站的第一步就是能够正常的进入到网站!

        当打包构建应用的时候,JavaScript 包会变得非常大,影响页面加载,如果能把不同路由对应的组件分割成不同代码块,当路由被访问的时候才加载对应组件,会变得更加高效!结合 Vue 的异步组件和Webpack的代码分割功能可以实现,具体可以看一下这里:路由懒加载 | Vue Router

         下面来将前面编写的代码进行路由懒加载的优化:

原先代码:

import Vue from 'vue'
import VueRouter from 'vue-router'
// 引入组件
import KuGou from '../views/music/KuGou.vue'
import KuWo from '../views/music/KuWo.vue'
import Play from '../views/PlayView.vue'
import Login from '../views/LoginView.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/music'
  },
  {
    path: '/music',
    component: Music,
    children: [
      {
        path: 'kugou',
        component: KuGou
      },
      {
        path: 'kuwo',
        component: KuWo
      }
    ]
  },
....

优化 —— 路由懒加载:

import Vue from 'vue'
import VueRouter from 'vue-router'
// 引入组件
// import Music from '../views/MusicView.vue'
// import KuGou from '../views/music/KuGou.vue'
// import KuWo from '../views/music/KuWo.vue'
// import Play from '../views/PlayView.vue'
// import Login from '../views/LoginView.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/music'
  },
  {
    path: '/music',
    // component: Music,
    component: () => import('../views/MusicView.vue'),
    children: [
      {
        path: 'kugou',
        // component: KuGou
        component: () => import('../views/music/KuGou.vue')
      },
      {
        path: 'kuwo',
        // component: KuWo
        component: () => import('../views/music/KuWo.vue')
      }
    ]
  },
....

组件按组分块 

ps:

  {
    path: '/music',
    // component: Music,
    component: () => import('../views/MusicView.vue'),
...


// 按组分块
{
    path: '/music',
    // component: Music,
    // component: () => import('../views/MusicView.vue'),
    component: () => import(/* webpackChunkName : "group-foo" */ '../views/MusicView.vue')

        那么本篇目的内容就到此结束,关于路由相关的内容也将在此篇目告一段落,回顾之前关于路由的内容相关:一级路由配置、声明式导航、路由重定向、嵌套路由、编程式导航、动态路由、命令路由、路由拦截和路由懒加载 ;附上前两篇内容链接:最后感谢大家的支持!

猜你喜欢

转载自blog.csdn.net/weixin_52203618/article/details/128788617