前端路由
看到这里可能有朋友有疑惑了,前端也有路由吗?这些难道不应该是在后端部分操作的吗?确实是这样,但是现在前后端分离后,加上现在的前端框架的实用性,为的就是均衡前后端的工作量,所以在前端也有了路由,减轻了服务器对这方面的判断,在前端做好路由分发之后,后端就只需要写API接口了,更着重于数据交互,逻辑上的代码编写了
单页面应用
那么,既然有前端路由,每个路由是不是都要单写一个页面呢?不需要的,现在都提倡单页面应用
什么是单页面应用呢
单页面应用,即 single page application ,简称SPA ,所有的路由其实都只在一个页面上完成,这种就叫单页面应用,我们不需要每个路由对应一个页面去编写
为什么要用单页面应用
1.传统的根据路由切换页面,都是立即切换,如果切换的网络资源很多的话,加载需要很久,用户体验很不好,并且写的页面越多,也越不好管理,可能还会有很多重复的代码出现,到后期更新迭代后,页面越来越多,这样会产生更多的资源
2.SPA可以完美解决以上的问题,并且数据切换时只是局部切换,且并不会立即切换,而是在某个合适的时间用ajax异步请求后端的API接口,再加载出数据,这里的【某个合适的时间】是指:因为用户查看网页的时候并不会永远都在切换页面吧?所以在某个刚好的时间切换就行了
单页面应用的原理
原理就是运用了锚点,即html页面上的id属性,因为id用的符号【#】,根据前面的web前端基础开发,相信你已经可以理解了,比如一个页面右边的固定按钮,返回顶部,比如这里淘宝的页面,这个返回顶部的按钮
用的就是【#】,这个就不多介绍了,因为学到vue这里,前面说过的,你得有钱端的基础知识才更容易学。在js里【#】其实是hash值,比如这个例子:
因为js自带有location对象,在我点击登录时,因为用onhashchange监听了hash改变,所以立马可以得到新的hash值,然后按逻辑把数据渲染出来
代码:
总之在单页面应用里,用【#】的意思是当前页面根本就没有跳转,还是当前的页面,只是用了一些机制把数据换掉了而已,这个机制就是vue-router,vue-router其实就是用了这个onhash的原理,不过比js原生的多了很多特性和功能
注:
- 锚点值即a标签的跳转,hash
- onhashchange获取url上锚点的改变
- location.hash指当前的锚点
- js里也有switch的用法
vue-router
vue-router插件位置:
官方文档传送门:点我
vue-router简介
这里说下,为什么要截图官方的文档,直接去官方文档看不就好了吗对吧,原因是:vue-router更新版本很快,所以文档也会跟着更新,有些知识点可能有些不一样。加上也就不用切换到另一个界面看了再回来看了,我截的图也只是个人觉得需要注意的点
安装vue-router
安装之后当前目录下多了个vue-router文件夹:
引入vue-router包
引入本地包
你可以引入你下载的本地的,真正的使用vue-router是和vue配套一起使用的,所以vue也得引入
引入cdn包
查看控制台如果没报错表示引入成功
引入cdn包代码:
使用vue-router
使用步骤:
1.引入vue-router插件
2.让根元素Vue载入自定义的VueRouter对象(此对象由vue-route插件提供,插件最后会返回一个VueRouter对象和router-linke和router-view组件),方面后面使用
3.创建、配置一个router对象,里面设定好不同的锚点值对应的路由以及对应好第二步里自定义的VueRoter对象
4.将创建好的router对象挂载到vue实例对象上,直接如上写即可,如果报错什么matched,一定是根元素Vue里没有写入router
5.利用vue-router插件提供的两个全局的组件
6.根据不同的路由,渲染整个页面,最后展示出来
注意:
当使用vue-router时,挂载组件只需要再VueRouter对象里挂载就行了,不需要再在vue实例对象里用components属性挂载了
VueRouter定义路由组件的属性是routes,不是router,定义的url属性用path
定义的url路由不用手动写上【#】,vue-router会自动添加上
以上就是vue-router的简单使用
命名路由
vue的路由也可以命名的,是不是感觉越来越像一门后端语言了,哈哈
注:
使用命名路由,在定义路由组件内部,最好用v-bind绑定,添加一个name属性,值则为你定义的路由名字,必须是字典形式:{name:'名字'}
我这里试了下,不用绑定直接使用也可以,以前的版本不行的,现在的貌似可以,具体还有待研究,我使用的版本:vue2.6.9,vue-router3.0.2
代码
默认路由
给默认的路由,因为很多时候打开页面,总要展示一个默认的页面吧,所以如下:给路由组件添加一个 '/',然后指向一个你觉得可以作为默认页面的组件就行
补充一下,在vue-router3.0.1版本中,还有这种写法:
但是由于我目前的是3.0.2,所有效果没出来,并且我感觉没多大用啊,反正都是渲染,直接用官方的就行了,搞些新写法整那么花里胡哨干嘛对吧?
路由重定向
这个跟上面的默认路由很类似,只是用的是redirect属性:
我这里访问页面会自动跳转到登录页面,这种就是路由重定向
代码:
带参数的路由
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染
带参数是什么意思,比如就目前我这个博客园后台编辑页面:
https://i.cnblogs.com/EditPosts.aspx?postid=10570774
其中,【?】后面的postid=10...就是带的参数
除了这种,还有这种:
其中的p/105...就是参数,当然博客园的到底用的什么做的url路由就不得而知了,也与本文无关了。
总之就是这种:xxx/user/userid/1,其中的userid/1就是参数
总结下就是,有以下两种参数,表达的意思都是一样的,是为了找page等于多少的数据
xxx.com/cont?page=2
xxx.com/cont/page/1
那么在实际开发中,肯定会有指定某个参数的访问,这种其实就是查询嘛,查询单个数据,也是非常常见且重要的,来个例子:
(有些图片看着字很小或者又显示不全,你右键查看图片源地址可以看大图的,这个是博客园主题的关系,显示不全)
注意:
- 用【:】匹配的路由组件,path是这样:/page/:pid,有【/】分割,在匹配时用的params,就可以匹配 /page/1,/page/2,/page/3等等的,浏览器url上显示的是 /page/1
- 另一种匹配,路由组件中的path没做任何改动,直接在匹配时用的query,就可以匹配 /page?pid=1,/page?pid=2,/page?pid=3等等的,浏览器url显示的是 /page?pid=1
相关具体步骤:
1.重新定义了两个局部组件,其中一个用 /page:pid ,为什么这么写,这是官方文档里明确说明了的:
2.再在router-link里多给了一个参数,params,在另外一个里添加了query:
params和query都是固定的,不能随意更改,不信你可以改了试试。
第一页 匹配对应前面的 xxx.com/cont/page/1第二页 匹配对应前面的 xxx.com/cont?page=2
那有没有想过,它这就给个参数(params/query),为什么就可以匹配呢?匹配之后可以拿到那个参数吗?
你可以用this.$router和this.$route查看下 ,为什么是这两个参数呢?因为读源码所得,当引入了vue-router之后,Vue实例化对象就多个两个恶属性,一个是$router,一个是$route
如下,给了一个已创建的生命周期函数,在生命周期里打印了this.$router,发现就是VueRouter对象,里面有相关的属性
再打印this.$route看看,发现用【:】 可以匹配url的参数(比如这里用的 :pid),并且给这个参数加了个键,然后匹配到的参数会进入params参数里去,作为一个字典存在:
为什么要传给params呢?你想想,像这样的匹配,匹配到了之后,需要交给后端处理,那你怎么拿到这个参数并传递给后端呢?没有变量名字你怎么传?是不是需要一个形参啊?所以这里就有个params属性,它对应一个字典,字典key就是定义路由组件时path部分,冒号后面的字段,值就是匹配到的值,比如这里就是:{pid:'1'}。
并且它还有个参数query,但是此时如上图,query还是一个空字典。
也就是说这两个属性 query和params都是路由组件自带的属性,它本来就有的,所以知道为什么前面说在router-link绑定那里不能随意换成其他参数了吧
关于这里,可能你理解起来有点吃力,如果你觉得比较吃力,得多看官方文档,传送门 多试几个例子,我个人感觉它那官方文档对于这里都没说的多清楚
由于我是做Python开发的,因为Python的django框架里就有个路由匹配的参数 path('/page:int
那么有了用【:】匹配,再看另一个url为/page?pid=2是否能拿到值呢?是否会把参数作为键值交给自己自带的属性query呢:
确实如此:
那假如说我把两个router-link传进的参数不小心写错了,本来是params的写成query,本来是query写成了params,看是否可以拿到呢:
拿是可以拿到,但是参数都进错了归属,这种操作太非主流了,可以是可以,但不建议这么干
代码:
编程式导航
编程式导航,听着那么高端大气,到底什么是编程式导航呢,官方文档解释:
也就是说,编程式导航就是直接使用router实例的push方法,并且其实前面我们用的router-link组件最终其实也是用的这个push方法,换言之我们也可以直接使用这个push方法,自定义,自己编写导航,这就是编程式导航
官网使用案例:
// 字符串 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-link组件换成了$router.push(),传进一个字典,字典和之前用的router-link传入的一样即可,这个字典就是name和params(或者是query)
push这个意思就很明显了,就是把这些参数推进去
代码:
嵌套路由
因为路由组件之下,完全还有可能有细分的子路由组件之类的,这样就可以把整个页面切分成很多个模块,每个模块做着不同的分工,所以就需要有嵌套路由这个东西,
然后用法和路由组件是一样的,就不用多说了,直接来个例子说明:
先看这个百度首页,我圈出来的部分:
百度首页和个人中心页面当做路由的根组件,把我圈出来的部分作为子路由嵌套嵌套到个人中心路由组件里:
就是这么简单,反正路由组件该有的都得有就行了
代码:
子路由的path
唯一要注意的是,写子路由的path时,要嘛带上完整的路由:
要嘛不要带/,直接给一个路由的相对路径,vue会自动拼写成完整路由,其他没做任何改变,一样可以显示:
动态路由
我画出来的那个【'$route'】这个要注意,后面我们会用到
什么是动态路由呢,我个人觉得官网讲的太随意了, 你看了估计都看不太懂啥意思
那么动态路由到底是什么呢?这么说吧,因为Vue是单页面应用,那么就会有很多公用路由组件部分对吧,那么这些公用组件部分会被多个组件使用,通常的方法就是把公用路由组件挂载成子组件,然后公用的部分显示,不同的部分做单独渲染或者说单独的覆盖数据就行。
这意思感觉有点像是模板一样对吧?反正都是那一套模板,把数据放进去就行了,好的,不多说,直接上例子:
先看个网站,稀土掘金的,这个也是个开发者社区,然后我标记出来的,1就是根路由,2就是子路由,3就是公用部分(这个是要账号登录状态才有的),不管我点2部分的路由的哪个标签,3永远都有的,而且没有变,切换的永远是3下面的数据,并且3下面的数据的css样式也是公用部分:
好的,简单的模仿一个:
(为了在一个页面显示,所以排版看着有点怪)
看下控制台打印的结果:
大概什么逻辑呢?就是相同部分都用同一个路由组件作为子组件,在父级路由的router-link里传入对应的标签参数,在公用路由组件里做下处理,把内容填充上就行,不再需要分别定义多个组件了。
这个在实际开发中,可以再公用路由组件里做判断,如果是哪个子路由,数据就是什么样,做好不同的分类显示就行了,这里作为案例就没做那么详细真实了
好的,至于这个 '$route'(to,form) 其中的'$route'到底是什么暂且不 谈了,详细的看官网了
代码:
keep-alive的用法
在路由组件中也可以用keep-alive,主要用途是对切换页面时组件的缓存,防止销毁创建增大开销
这个在前面的局部组件也有用到的,不多说。用法就是在组件的出口前后加一个vue特殊的组件
其他没变只是在入口函数那里加了keep-alive组件
全局前置守卫验证
这个全局前置守卫在旧版的vue-router里名字叫全局守卫
在开发中,按照常识,都知道,不同用户的权限是不一样的,然后能访问的路由也是不一样的,所以这就是权限,而作为权限验证,得有一套机制吧,这套机制就是全局守卫
比如,某个网站,验证用户的登录状态,如果未登录就提示登陆,否则就进不了需要登录的网页,必须登录之后才可以查看网页,这种场景就可以用全局前置守卫
相关的说明:
上面这个说的什么意思呢,主要就是说,必须要调用next方法,不然的话会卡住
路由元信息
首先呢,路由组件有一个元信息meta参数,这个参数就可以配置一些权限验证:
这里给一个小demo,如下,这是优酷视频网的某个部分,上面是免费视频,下面是需要会员才能看的视频
好的,就做个这种简单的出来:
点击会员时,它会自动跳转到登录页面
当我登录之后,会员页面显示:
以上部分,在router-link传入参数部分,可能有朋友会想,唉,你这为什么要定义两个公用组件,都那一套啊,免费电影和会员部分都用一套公用组件多省事啊,确实啊,这样确实省事,其实最开始我就是这样的,但是因为都用一个公用组件,在不同根组件切换到子组件时,会有错乱,达不到我们的需要的效果,所以我定义了两个公用组件。并且针对两个组件,后期肯定还会有不同的配置的,所以这样会更好维护一点
原理就是在定义路由时,给需要验证的路由配置原信息,加了个meta参数,这个参数是router对象自带的属性,我们可以随意定义键值进去:
利用这个键值用beforeEach全局守卫在路由跳转时判断这个参数,如果为true则需要做验证,验证通过则next()为空放行,验证不通过则用next跳转,这里的next是个回调函数,有点编程式导航router.push的意思,此时这个beforeEach全局守卫其实也可以看出一个周期钩子函数
对于验证登录状态,用了js自带的一个localStorage永久存储对象作为存储 :
需要清除的话,右键那个那个file://,点clear就行,详细的就不多说了,自行查资料研究吧
完整代码
官方文档里其实还有一个路由内部的守卫是这个:beforeEnter,这个就自己研究了
在vue-router的官方文档里,守卫一共有这么多,自己研究了
补充
如果你遇到这样的报错:
检查你的组件,看template参数写对没有
如果遇到路由匹配到了,但是url没有正常显示:
路由组件如下,
编程式导航部分:
coursedetail部分:
页面显示:
这样虽然有显示,但是url不对,原因是参数匹配有误,路由组件用的参数名是courseId,在做编程式导航时用的参数是detailId,我上面已经标注出来了
作如下改动,即可:
页面正常显示:
总结:
vue-router就到这里,我把经常用到的知识点都揉在一起了,篇幅有点长,耐心看吧,其实都挺简单的,只是有些地方稍微注意一下就行