vue中有“三霸”:拦截器、路由守卫、导航守卫。
他们都有一个共同的作用(…也可能是唯一的作用) —— 在路由变更前做一次判断,或取或舍,或添加token、session之类的【头信息】。
拦截器
我们先看其工作原理:
其基本用法:
Vue.http.interceptors.push((request,next)=>{
//请求发送前的处理逻辑
next((response)=>{
//请求发送后的处理逻辑
return response;
})
})
拦截器是一个全局性的处理函数(工具),它的局限也是“全局”:所有请求都要统一处理,所以用的地方不多 —— 泛用性提示:提示框!
当与服务器进行通信时,我们显示一个“ 加载中… ”的拦截画面隔绝用户进行其他的操作,待处理完成后再去删除加载画面。
这里我们不妨使用UI组件,但是这个不重要,重点是 我们要将拦截器代码写在main.js文件里 ,因为它是一个全局性方法:
Vue.http.interceptors.push((request,next)=>{
if(request.url.includes('/api/')){
let modal=window.UIkit.modal.blockUI(`
<div class="uk-modal-spinner"></div>
<p class="uk-text-center">加载中...</p>
`,{
center:true
})
next((response)=>{
modal.hide();
return response;
})
}
})
这样在每次向服务器发送请求时就会先在页面显示出一个“加载中…”的loading框。
一般我们使用的是【axios】,不会像上面代码中那样做拦截 —— axios拦截器分为request拦截器和response拦截器两种,而且他们一般一起使用!
下面是笔者做的一个项目中为登录添加token的示例:
新增文件setaxios.js:
export default function setAxios(){
axios.interceptors.request.use(config=>{
if(store.state.token){
config.headera.token=store.state.token
}
return config
},error=>{
return Promise.reject(error)
})
axios.interceptors.response.use(response=>{
if(response.status==200){
const data=response.data
if(data.code==-1){
store.commit('settoken','')
localStorage.removeItem('token')
router.replace({path:'/login'})
}
return data
}
return response
},error=>{
return Promise.reject(error)
})
}
然后就可以在main.js文件里添加:
import setaxios from './setaxios'
setaxios();
配置“路由守卫”
让我们来到router.js文件中 —— 这里是“路由”的“聚集地”。
其实每个router对象中都有一个meta属性(这可能是因为编译成HTML的缘故吧):它有一个字段requireAuth:
{
path:'xxx',
name:'xxx',
meta:{
requireAuth:true
},
component:()=>import('./xx/xxx')
},
//...
有了这个字段且值为true时,我们就默认为这个路由页面需要有登录权限的。
所以,
配置完上面,我们要回到main.js文件:在这里(new Vue之外)添加
导航守卫
router.beforeEach((to,from,next)=>{
//无论是刷新还是跳转路由,第一个进入的就是这个路由前置钩子函数
store.commit('settoken',localStorage.getItem('token'))
if(to.meta.requireAuth){
if(store.state.token){
next()
}else{
next({
path:'/login',
query:{redirect:to.fullPath}
})
}
}else{
next()
}
})
这样,我们就可以在login.vue页面判断跳转 —— 实现“再次进去返回到原页面而不是跳转到首页”的功能:
if(this.$route.query.redirect){
this.$router.replace({path:this.$route.query.redirect})
}else{
this.$router.replace({path:'/xxx/xxx'})
}
导航守卫还可以用在组件内部:比如前面提到的loading
<div v-if="state==loading">
is loading...
</div>
<div>
<h1>{{userInfo.name}}</h1>
</div>
//js代码
data(){
return{
state:'loading',
userInfo:{}
}
},
mounted(){
this.init();
},
beforeRouteUpdate(to,from,next){
this.state='loading'
this.init()
next()
},
methods:{
init(){
fetch(`/api/usr/${this.$route.params.id}`)
.then((res)=>res.json)
.then((data)=>{
this.state='';
this.userInfo=data;
});
}
}