問題引入
試想這樣一個業務場景:
在用戶輸入數據,點擊提交按鈕后,這時發起了ajax請求,如果請求成功,
則跳轉到詳情頁面並展示詳情數據,失敗則不跳轉到詳情頁面,只是在當前頁面給出錯誤消息。
難點所在
需要注意的是,這里並沒有單獨的接口用於判斷用戶是否通過校驗
,而是若用戶通過校驗,接口就直接返回了用戶需要的詳情信息,未通過校驗則不會返回詳情信息並報錯。
常見方案問題分析
(一)用戶點擊按鈕后直接跳轉到詳情頁面,在詳情頁面的created鈎子函數中發起ajax請求獲取數據
問題在於:
若用戶未通過校驗,也會先跳轉到詳情頁面,然后再報錯,
而我們的目標是先判斷是否成功,成功了再進行路由跳轉。
(二)將詳情頁面寫在彈窗中,不進行路由跳轉。在當前頁面發起請求,成功則在彈窗中展示詳情數據,失敗了則不展示彈窗
問題在於:
彈窗中的內容特別多的情況下,排版不容易,很容易難看,畢竟彈窗一般適用於展示少量數據 ,
並且產品的設計要求可能就是需要單獨頁面展示,不允許使用彈窗。
另外,如果路由和頁面都已經寫好了,再去寫彈窗也會額外增加不少工作量
(三)發兩次請求:當前頁面發起第一次請求,失敗了不進行跳轉;成功了在詳情頁面再發起一次請求,獲取詳情數據
這種方式肯定可行,但是問題在於:
同一個api發送了兩次請求,多發一次請求白白耗費了網絡資源
(四)當前頁面發起請求,失敗了進行錯誤提示;成功了,則跳轉到詳情頁面,並通過Vuex或者空組件實現非父子組件通信的方式將數據攜帶至詳情頁面展示
這種方式的問題在於:
Vuex的設計目標是存儲系統各組件共用的狀態,例如用戶登錄狀態、菜單伸縮收起狀態,
這里僅有兩個組件,采用Vuex有種大炮打蚊子的感覺,使用空組件事件傳值實現非父子組件通信
會略顯繁瑣。並且這兩種方式加大了系統復雜度,也容易有新坑。
(五)瀏覽器緩存
不用說,更加繁瑣了,也偏離更遠了。
推薦解決方案:使用Vue Router的beforeRouteEnter路由導航守衛
(一)beforeRouteEnter簡介:
它本質上,類似於Vue.js的生命周期鈎子函數。它在新路由被確認前被調用,可以在里面進行數據處理,發起ajax獲取數據,甚至是取消導航。當調用該守衛時,頁面仍停留在原頁面。該守衛執行完畢后(包括異步獲取數據),才會跳轉到新頁面。
(二)使用案例
在詳情組件中使用beforeRouterEnter路由守衛:
import $axios from '@/libs/axios';
import api from '@/api';
export default {
name:'testComponent',
data(){
return {};
},
beforeRouteEnter(to, from, next) {
$axios
.get(api.GetQueueSettingDetail, {
params: {
waybillKey: to.params.pickCode,
warehouseType: 11
}
})
.then(resp => {
if (resp.success) {
next(vm => {
vm.carInfo = resp.result;
});
} else {
next(false);
}
})
.catch(() => {
next(false);
});
},
}
在以上案例中,在新路由被確認前,先發起ajax請求,若成功,就將數據賦值給該組件的carInfo,並跳轉到新路由。若失敗,調用next(false)回調函數取消導航,頁面仍停留在原頁面。
該方案相對來說,比較圓滿的實現了開篇所提的業務需求,並且沒有額外的開銷。
(三)參數說明
三個參數
- to 即將要進入的目標 路由對象
- from 當前導航正要離開的路由
- next 回調函數,一定要調用該方法來 resolve 這個鈎子,可以在里面取消導航(next(false))或進行數據處理
(四) 注意事項
- beforeRouteEnter 守衛 不能 訪問 this,因為守衛在導航確認前被調用,因此即將登場的新組件還沒被創建。因而上面示例中api和axios都采用了手動導入,沒有使用this.axios或者this.api的方式。
- 訪問組件實例的方式是,通過傳一個回調給 next來訪問組件實例,也不能使用this。在上面示例中,獲取數據成功后,使用的是vm.carInfo而不是this.carInfo。
- next(false)的作用是取消導航,這時,一切操作都停留在原頁面,看起來就像什么也沒發生一樣。