問題場景
在大型單頁面應用中,處於對效能的考慮和首屏載入速度的要求,我們一般都會使用webpack的程式碼分割和vue-router的路由懶載入功能將我們的程式碼分成一個個模組,並且只在需要的時候才從伺服器載入一個模組。
但是這種解決方案也有其問題,當網路環境較差時,我們去首次訪問某個路由模組,由於載入該模組的資源需要一定的時間,那麼該段時間內,我們的應用就會處於無響應的狀態,使用者體驗極差。
解決方案
這種情況,我們一方面可以縮小路由模組程式碼的體積,靜態資源使用cdn儲存等方式縮短載入時間,另一方面則可以路由元件上使用非同步元件,顯示loading和error等狀態,使使用者能夠得到清晰明瞭的操作反饋。
Vue官方文件-動態元件&非同步元件
具體實現
- 宣告方法,基於Vue動態元件工廠函式來返回一個Promise物件
/**
* 處理路由頁面切換時,非同步元件載入過渡的處理函式
* @param {Object} AsyncView 需要載入的元件,如 import(`@/components/home/Home.vue`)
* @return {Object} 返回一個promise物件
*/
function lazyLoadView (AsyncView) {
const AsyncHandler = () => ({
// 需要載入的元件 (應該是一個 `Promise` 物件)
component: AsyncView,
// 非同步元件載入時使用的元件
loading: require(`@/components/public/RouteLoading.vue`).default,
// 載入失敗時使用的元件
error: require(`@/components/public/RouteError.vue`).default,
// 展示載入時元件的延時時間。預設值是 200 (毫秒)
delay: 200,
// 如果提供了超時時間且元件載入也超時了,
// 則使用載入失敗時使用的元件。預設值是:`Infinity`
timeout: 10000
});
return Promise.resolve({
functional: true,
render (h, { data, children }) {
return h(AsyncHandler, data, children);
}
});
}
複製程式碼
- 引入路由
const helloWorld = () => lazyLoadView(import(`@/components/helloWorld`))
複製程式碼
- vue-router中使用
routes: [
{
path: `/helloWorld`,
name: `helloWorld`,
component: helloWorld
}
]
複製程式碼
至此,改造已經完成,當你首次載入某一個元件的資源時(可以將網速調為 slow 3g,效果更明顯),就會顯示你在loading元件的內容,而當超出超時時間仍未載入完成該元件時,那麼將顯示error元件的內容(建議error元件儘量簡單,因為當處於低速網路或者斷網情況下時,error元件內的圖片資源等有可能出現無法載入的問題)
關於使用非同步元件後路由鉤子失效的問題
在使用本文的配置對路由引入方式進行重構後,會出現路由鉤子無法使用的問題,這個問題也是剛剛發現,在查閱資料後發現,vue-router目前對於這種使用方法是不支援的…
官方解釋為:
> WARNING: Components loaded with this strategy will **not** have access to
in-component guards, such as `beforeRouteEnter`, `beforeRouteUpdate`, and
`beforeRouteLeave`. If you need to use these, you must either use route-level
guards instead or lazy-load the component directly, without handling loading
state.
複製程式碼
關於使用非同步元件後路由鉤子失效的解決方法
- 需要使用路由鉤子的頁面放棄非同步載入,使用普通的懶載入
- 使用created, mounted等鉤子替代路由鉤子
其他辦法進一步查詢中,歡迎各位在評論中提出問題和解決方案。