今天来谈谈vue项目{vue,vue-router,component}三大神将之一的vue-router。做为咱们先后端分离很重要的实践之一,router帮咱们完成了SPA应用间的页面跳转。前端
而且,配合axios这样的第三方库,咱们能够实现配合后台接口的拦截器功能。vue
对于一个小型项目而言,router这个文件夹里面就包含了一个router.js就足够了,java
可是,当咱们的页面比较多的时候,咱们就须要分出两个文件出来:一个定义咱们的路由和组件,另外一个实例化组件,并将路由挂载到vue的实例上。webpack
基本的用法就很少赘述,你们能够看vue-router的官网,认真过一遍的话,基本使用确定没什么问题。ios
这里有个很是重要的一点就是当咱们去构造VueRouter的实例的时候,传入的参数的问题。web
import routes from '@/router/router' const router = new VueRouter({ routes // (ES6语法)至关于 routes: routes }) new Vue({ router }).$mount('#app')
若是你这里引入的不是routes,你就要按照下面的方式来写。vue-router
import vRoutes from '@/router/router' const router = new VueRouter({ routes :vRoutes }) new Vue({ router }).$mount('#app')
对于咱们的vue项目,咱们基本都是运用webpack打包的,若是没有懒加载,打包后的文件将会异常的大,形成首页白屏,延时严重,不利于用户体验,而运用懒加载则能够将页面进行划分,webpack将不一样组件打包成不少个小的js文件。须要的时候再异步加载,优化用户的体验,换而言之,有的页面可能100个用户只有一两个会进去,何须把流量花在它身上。axios
import App from '@/App.vue' const index = r => require.ensure([], () => r(require('@/pages/index/index')), 'index') export default [{ path: '/', component: App, children: [ { path: '/index', name:'index', component: index }] }]
若是某个组件包含了嵌套路由,咱们也能够将两个路由打包到一个js chunk中。后端
// 这两条路由被打包在相同的块中,访问任一路由都会延迟加载该路由组件 const orderUser= r => require.ensure([], () => r(require('@/pages/order/user')), 'order') const payRecord= r => require.ensure([], () => r(require('@/pages/order/payRecord')), 'order')
对于浏览器,咱们的router分为两种模式。浏览器
1.hash模式(默认)
按照一个uri的基本结构来讲,hash模式就是在一个基本的URI的片断进行的处理。若是抛开SPA的话,比较常见的应用场景就是咱们在作pc商城的时候,会有好比说:商品详情,评论,商品参数这样的tab切换,就可使用a标签配合id使用,加上一点运动的特效,效果甚佳。
这也是router默认使用的路由方式。不过,这种方式有一个弊端,就是在接入第三方的支付的时候,咱们传入一个url给到第三方支付做为回调地址,可是在支付完成之后,有的第三方支付会把咱们的#做为一个截取符号,仅保留第一个#符号前面的url内容,后面再添加相应的回调参数。致使支付完成之后没法跳转到相应的支付页面
传入的url: http://xx.xx.com/#/pay/123 回调后的地址: http://xx.xx.com/pay/123?data=xxxxx%xxxx
2.history模式
还有一种就是history的模式。它是使用h5的history.pushState来完成URL的跳转的。使用这种方式来处理跳转的好处就是,url和咱们日常看到的没有什么区别。和hash模式做比较的话就是没有了#。不过使用history模式,咱们在后台也要去作相应的处理,由于若是直接去访问一个地址,例如http://www.xxxx.com/user/id的时候,若是后端没有配置的时候,后端就会返回404页面。
4.router-link在循环中this.参数名=undefined
<router-link>组件是咱们在view层中须要用到的跳转组件。它替代了<a>标签须要作的事情,而且帮助咱们作了更多的事情。
不管是 h5 history 模式仍是 hash 模式,它的表现行为一致,因此,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须任何变更。
在 HTML5 history 模式下,router-link
会守卫点击事件,让浏览器再也不从新加载页面。
当你在 HTML5 history 模式下使用 base
选项以后,全部的 to
属性都不须要写(基路径)了。
不过当咱们在v-for的循环中使用了router-link的时候,通常来讲,咱们须要取的都是循环里的值,经过定义的item.xxx就能够取到。若是说须要取一个咱们在data中定义的值的时候,咱们是经过this.foo来取呢?仍是经过foo来取呢?仍是均可以?
这里的话,咱们是不能经过this.foo来取的,由于这里的this,再也不是指向vue的实例了,而是指向了[object Window]。因此用this.foo来取的话,实际上是undefined.
<router-link tag="li" :to="{path:`/user/${item.userID}`}" v-for="(item, index) in userList" :key="index"> //含有固定的值 <p>{{this.foo}}</p>
<p>{{foo}}</p> </router-link>
data(){ return { foo:'bar', } }
初次接触拦截器这个概念是在java中,经过拦截器,咱们能够对用户的登陆状态进行更加粒度的操做。而对于一个SPA的应用来讲,没有了后台路由的介入,咱们就须要在前端实现一套本身的登陆状态的管理机制。
最直观的一点就是,经过用户的token来判断用户是否登陆?
router.beforeEach((to, from, next) => { const NOW = new Date().getTime(); if (to.matched.some(r => r.meta.requireAuth)) { if(NOW > store.state.deadLine){ store.commit('CLEAR_USERTOKEN') } if (store.state.message.login === true) { next(); } else { next({ path: '/login', query: {redirect: to.fullPath} }) } } else { next(); } })
上面的代码中,咱们经过vue-router中的全局守卫,在导航触发的时候大体作了以下几件事:
(1)判断导航的页面是否须要登陆
(2)超过登陆持久期限,清除持久化的登陆用户token
(3)没有超过登陆期限,判断是否登陆状态
(4)没登陆,重定向到登陆页面
可是,仅仅这样是不够的。由于用户直接不正常注销而直接后台运行网页是很正常的事情,这就致使虽然token是存在的,可是对于后台而言,这个token是无效的,过时的了。因此,咱们须要axios配合后台给出的状态码来完善咱们的拦截器。
import router from '@/router/routes' axios.interceptors.response.use( success => { switch (success .code) { case -100: router.replace({ path: 'login', query: {redirect: router.currentRoute.fullPath} }) console.warn('注意,登陆已过时!') break; } return success; }, error => { switch (error.code) { case 404: console.warn('请求地址有误或者参数错误!') break; } return Promise.reject(error.response.data) });
经过后端给到的登陆过时状态码,这里以-100为例,咱们能够用axios的响应拦截器实现,当咱们的token过时的时候,咱们将页面重定向到登陆页面去。
在项目中,我有的同事就是一直this.$router.push(...),从开始push到结尾。
碰到有的页面,好比说,在选择地址的时候须要知道用户当前所在的城市,若是没有的话,就是重定向到城市列表页面去手动选取。选择完成之后再回到选择地址的页面,若是一直使用push的话,点击选择地址的后退时,就会回退到城市列表页。而后形成页面间的死循环。
这里若是使用replace来操做就没有什么问题了,问题就是咱们不该该让城市列表页出如今咱们的浏览历史里面。