十三. 嵌套路由#
看如下的demo
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./vue/dist/vue.js"></script> <script src="./vue-router/dist/vue-router.js"></script> <style> * { margin: 0; padding: 0; } nav { height: 60px; background-color: #eee; } li { list-style: none; display: inline-block; padding-left: 10px; line-height: 60px; } nav a { text-decoration: none; color: black; padding-left: 10px; padding-right: 10px; } nav a:hover { display: inline-block; color: #ffffff; background-color: #444444; } </style> </head> <body> <div id="app"> <nav> <router-view></router-view> </nav> </div> </body> <script> const navComponent = { template: `<div> <div> <ul> <li><a href="#/index">首页</a></li> <li><a href="#">文章</a></li> <li><a href="#">友情链接</a></li> <li><a href="#">点我关注</a></li> </ul> </div> <div> <router-view></router-view> </div> </div>`, name: "nav" } const indexComponent = { template: `<div> this is 首页 --> index</div>` } const router = new VueRouter({ // todo 如果说路由没有加载出来,也不报错,八成是VueRouter的这个属性名被写错了 routes: [ { path: "/", component: navComponent, children: [ { path: "/index", component: indexComponent } ] }, ] }) var app = new Vue({ el: "#app", router: router, data: {} }) </script> </html>
最终实现的效果就是:当我们点击首页时,在nav标签的下面渲染出首页组件的子组件。
效果图:
说一下需要注意的地方:像下面样通过children块为当前路由注册子路由。
const router = new VueRouter({ // todo 如果说路由没有加载出来,也不报错,八成是VueRouter下面这个属性名被写错了 routes: [ { path: "/", component: navComponent, children: [ { path: "/index", component: indexComponent } ] }, ] })
十四. params和query#
首先:
第一:VueRouter根据浏览器地址栏中的hash值去匹配并渲染组件
第二:url中的含有:网络协议+端口+params+query
params+query相当于是hash
比如这个:
http://localhost:63342/untitled#/index/12?id=10&name=tom
params : /index/12
query : ?id=10&name=tom
- params参数的传递其实在上文中第十节路由参数传递模块已经说了多几种情况了,这里不再重复说
- 那么query模式传递的参数一般怎么处理呢?
参考如下代码:?id=1&name=tom的信息被封装进了router的query部分
const indexComponent = { template: `<div> id:{{id}} name:{{name}}</div>`, props:["id","name"] } const router = new VueRouter({ // todo 如果说路由没有加载出来,也不报错,八成是VueRouter的这个属性名被写错了 routes: [ { path: "/", component: navComponent, children: [ { path: "/index", component: indexComponent, props:function (router) { console.log(router) return router.query } } ] }, ] })
- router的打印结果如图:
当然,它和params的用户几乎是相同的
总结一下,其实就是url中的path部分和参数部分,会被分别封装进route对象的不同属性中。这仅仅是为了可以实现两种参数的区分,最终的目的都是为了可以传递参数。以及实现路由的跳转
十五. 导航守卫#
导航守卫也被称作路由钩子函数,他们指的都是router对象中的回调函数,他们会在路由所处的不同状态被回调
如下是简单的demo
为每个路由单独写回调
const router = new VueRouter({ // todo 如果说路由没有加载出来,也不报错,八成是VueRouter的这个属性名被写错了 routes: [ { path: "/", component: navComponent, children: [ { path: "/index", component: indexComponent, props:function (router) { console.log(router) return router.query }, beforeEnter:function (to,from,next) { console.log("to: ",to) console.log("form: ",from) // next是一个函数 console.log("next: ",next) // next(true) 表示进行路由的跳转 // next(false) 表示不进行路由的跳转 } } ] }, ] })
当发生路由跳转时,beforeEnter会被回调,查看控制台的打印结果:
通过next函数发起路由的跳转,通常会使用这个特性干什么事呢? 比如我们可以在路由的跳转之前判断一下用户是否已经登陆了,是否有权限。
我们可以将其定义在模版中:
const indexComponent = { template: `<div> id:{{id}} name:{{name}}</div>`, props:["id","name"], beforeRouteEnter:function (to,from,next) { console.log("to: ",to) console.log("form: ",from) // next是一个函数 console.log("next: ",next) // next(true) 表示进行路由的跳转 // next(false) 表示不进行路由的跳转 }, beforeRouteLeave:function (to,form,next) { console.log("to: ",to) console.log("form: ",from) // next是一个函数 console.log("next: ",next) // next(true) 表示进行路由的跳转 // next(false) 表示不进行路由的跳转 } }
十六. 路由元信息#
路由的元信息指是路由的 meta属性。注意我们说的是路由而不是路由器。
路由:this.$route
路由器:this.$router
常见的我们可以基于路由的元信息做安全验证相关的操作:点击看相关文章
下面再补充下,官网提供的demo :点击进入参考链接
首先贴出VueRouter部分的代码:
const router = new VueRouter({ // todo 如果说路由没有加载出来,也不报错,八成是VueRouter的这个属性名被写错了 routes: [ { path: "/", component: navComponent, children: [ { path: "/index", component: indexComponent, props: function (router) { console.log(router) return router.query }, meta: {requiresAuth: true} } ] }, ] }) router.beforeEach((to, form, next) => { console.log("to: ", to) console.log("form: ", form) next() })
最终可以实现的效果如图:
贴着张图是啥意思呢?其一是可以比较完成的看到route的各个属性,其二是我们可以看到他有个属性叫做match 它是个数组。里面记录着匹配到的route的信息。 看上图的目的很大一部分原因是去看看这个matched属性是个数组。因为在官方的demo中编码如下:
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ] }) 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() } })
在官方的demo中,路由的全局守卫使用到了这个matched属性以及Array的some方法,some方法的入参接受一个函数,官网demo中的实现采用了ES6的新特性去实现的。
致至为止,相信我们都能很好的理解这个demo了。
十七. VueRouter中的数据获取#
VueRouter中的数据的指的是我们何时像后端发送请求,将组件中需要的数据获取到进而渲染组件。
通常来说有两个时机:
- 导航完成之后获取
导航完成之后说明我们已经找到对应的组件了。而组件中又需要渲染一些动态的数据,这时我们只能选择在组件相应的生命周期回调中(比如created,mounted)向后端发送请求完成拉去数据。
如果想让用户有个良好的体验,我们可以添加上进度条,或者是loding标记。 - 导航完成之前渲染
这种方式指的是在路由的守卫中完成数据的拉取,数据获取到之后,再进行导航。
十八. VueRouter的滚动行为#
VueRouter支持我们自定义切换到一个新的路由时页面的滚动方式。
比如像下面的这个例子,当我们在同步的路由之间切换时,默认的浏览器的下拉框总会定位到上次我们所在的位置。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./vue/dist/vue.js"></script> <script src="./vue-router/dist/vue-router.js"></script> </head> <body> <div id="app"> <router-view></router-view> </div> </body> <script> const home = { template: ` <div> <h1>文章:</h1> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <p>学习 vuerouter 的滚动行为</p> <router-link to="/detail">点击进入detail</router-link> </div> ` } const detail = { template: ` <div> <h1>this is detail</h1> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <p>this is detail</p> <router-link to="/">点击进入首页</router-link> </div>` } const router = new VueRouter({ routes: [ { path: "/", component: home }, { path: "/detail", component: detail } ] }) var app = new Vue({ el: "#app", router, data: {} }) </script> </html>
Vue-Router支持我们定制这个滚动的集体位置。
定制这个滚动的具体位置需要借助一个VueRouter的函数:scrollBehavior
const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { // return 期望滚动到哪个的位置 return { x: number, y: number } // (offset 只在 2.6.0+ 支持) return { selector: string, offset? : { x: number, y: number }} // 返回按下前进/后退按钮时,浏览器记录下的原生状态 return savedPosition } })
如果页面中有锚点,也能像下面这样模拟hash锚点定位
scrollBehavior (to, from, savedPosition) { if (to.hash) { return { selector: to.hash } } }
当然也支持异步滚动
scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ x: 0, y: 0 }) }, 500) }) }