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.header.token=store.state.token // 配置header
}
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;
});
}
}