回顾前两篇内容讲到一级路由、声明式导航、编程式导航、路由的重定向、嵌套路由和动态路由,那么在本篇接着来讲关于Vue路由相关的内容:
命名路由
命名路由,顾名思义,有命名的路由,同样还是将上一篇的案例继续进行:
在 /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 提供的编程式导航方式,下面来调整编写代码:
...
methods: {
handlePlay (id) {
// alert('获取歌曲id:' + id)
// location.href = '#/play'
// 编程式导航
// this.$router.push(`/play/${id}`)
this.$router.push({ name: 'playMusic', params: { id: id } })
}
}
}
下面来进行测试运行一下:
query
这里用到的是一个动态路由的方式来完成,通过选择歌曲,携带歌曲的id跳转到播放页,通过歌曲id去请求歌曲的播放链接,在没使用动态路由之前,使用的是 this.$router.push(`/play/${id}`),出现如下这种情况:
在路由中并没有配置 /play/10001 的路由信息,且歌曲不止十几条,如果通过去配置这样的路由信息显然就不合理!但还不使用动态路由可以怎么来完成呢?
...
{
path: '/play',
component: Play,
name: 'playMusic'
}
]
...
动态路由所携带的参数是 /play/:id 格式,那么如果携带的参数是 /play?id= 格式,可以通过什么获取?—— query
播放界面
当前歌曲Id —— {{ songId }}
下面来测试运行一下:
那么关于这块内容就已然告一段落,将携带的id送到播放页,播放通过ajax去请求后端得到播放链接在播放页当中播放就解决了;那么下面就来讲关于路由模式:
路由模式
Vue 支持两种路由模式 : 1. hash 模式 2.history 模式
hash模式: http://localhost:8080/#/music/
history模式: http://localhost:8080/music
上面的例子当中使用的是 hash 模式:
如何修改为 history 模式呢?在 /router/index.js 中添加 mode: 'history' 就可以了;
...
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;下面来看一种大家常见的写法:
...
这种显然没有任何问题,但是不是最好的,如果需要检查的页面多了,那么就需要在每一个组件当中去检查是否有token,需要检查的都需要编写,这样一来不仅重复代码很多,写起来很难受,有没有一种好的方法呢?有,就是下面要讲的路由守卫,叫法比较多,但不重要;
路由守卫分为全局守卫和组件内部守卫,非常好理解,如果大部分组件都需要进行检查登录再进入就可以用全局,如果是局部或者少部分地方会涉及到检查登录,可以用组件内部守卫的方式,下面来使用一下:
当 http://localhost:8080/music 切换到 http://localhost:8080/music/kugou 时则需要进行路由的拦截检查,在 /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
...
那么有些初学者要问了,不可用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')
那么本篇目的内容就到此结束,关于路由相关的内容也将在此篇目告一段落,回顾之前关于路由的内容相关:一级路由配置、声明式导航、路由重定向、嵌套路由、编程式导航、动态路由、命令路由、路由拦截和路由懒加载 ;附上前两篇内容链接:最后感谢大家的支持!