自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

提供可制定化的路由加載方式,Vue 如何做到?

開(kāi)發(fā) 前端
這個(gè)月老大在技術(shù)優(yōu)化上(前端公共庫(kù))派了幾個(gè)任務(wù)給我,其中的一個(gè)是"路由注冊(cè)改造,采用組件內(nèi)的異步加載",這里我們先來(lái)看傳統(tǒng)方式是怎么做的。

[[417936]]

背景

在開(kāi)始之前,先介紹一下我們目前新項(xiàng)目的采用的技術(shù)棧

  • 前端公共庫(kù):vue3 + typescript + jsx + antdVue
  • 后臺(tái)項(xiàng)目:vue3 + typescript + jsx + antdVue

沒(méi)錯(cuò),我們現(xiàn)在都采用 ts + jsx 語(yǔ)法來(lái)開(kāi)發(fā)新項(xiàng)目,這里可能會(huì)有小伙伴說(shuō)了,不用 template 嗎,裝啥裝。這里面要討論內(nèi)容很多,下次有機(jī)會(huì)在分享,今天不討論這個(gè)問(wèn)題。

回到正文~~

這個(gè)月老大在技術(shù)優(yōu)化上(前端公共庫(kù))派了幾個(gè)任務(wù)給我,其中的一個(gè)是"路由注冊(cè)改造,采用組件內(nèi)的異步加載",大家一看,肯定會(huì)想,就這?,這個(gè)不是配合 router.beforeEach 和 router.afterEach 在加個(gè)顯示進(jìn)度條的庫(kù) NProgress 不就完事了嘛。沒(méi)錯(cuò),就是按傳統(tǒng)的方式會(huì)有一些問(wèn)題,后面會(huì)講,這里我們先來(lái)看傳統(tǒng)方式是怎么做的。

傳統(tǒng)方式

這個(gè)方法大家應(yīng)該都用過(guò),就是在路由切換的時(shí)候,頂部顯示一個(gè)加載的進(jìn)度條,我們這里借助的庫(kù)是 NProgress。

第一步,需要安裝插件:

  1. yarn add  nprogress 

第二步,main.ts中引入插件。

  1. import NProgress from 'nprogress' 
  2. import 'nprogress/nprogress.css' 

第三步,監(jiān)聽(tīng)路由跳轉(zhuǎn),進(jìn)入頁(yè)面執(zhí)行插件動(dòng)畫(huà)。

路由跳轉(zhuǎn)中

  1. router.beforeEach((tofromnext) => { 
  2.   // 開(kāi)啟進(jìn)度條 
  3.   NProgress.start() 
  4.   next() 
  5. }) 

跳轉(zhuǎn)結(jié)束

  1. router.afterEach(() => { 
  2.   // 關(guān)閉進(jìn)度條 
  3.   NProgress.done() 
  4. }) 

很簡(jiǎn)單的一個(gè)配置,運(yùn)行后,當(dāng)我們切換路由時(shí)就會(huì)看到頂部有一個(gè)進(jìn)度條了:

圖片

這種模式存在兩個(gè)問(wèn)題(目前能想到的):

  • 弱網(wǎng)絡(luò)的情況,頁(yè)面會(huì)卡那里,動(dòng)的很慢。
  • 當(dāng)網(wǎng)絡(luò)斷開(kāi)時(shí),進(jìn)度條件會(huì)一直處于加載的狀態(tài),并沒(méi)有及時(shí)反饋加載失敗。
  • 當(dāng)有比較特殊需求,如,當(dāng)加載菜單二時(shí),我想用骨架屏的方案來(lái)加載,當(dāng)加載菜單三,我想要用傳統(tǒng)的菊花樣式加載,這種情況,我們現(xiàn)在的方案是很難做的。

弱網(wǎng)絡(luò)

我們模擬一下弱網(wǎng)絡(luò),打開(kāi)瀏覽器控制臺(tái),切到 NetWork,網(wǎng)絡(luò)換成** Slow 3G**,然后在切換路由,下面是我實(shí)操的效果:

圖片

可以看到,我們切換到菜單二時(shí),進(jìn)度條件會(huì)慢慢走,頁(yè)面沒(méi)有及時(shí)切換到菜單二的界面,如果頁(yè)面內(nèi)容越多,效果越明顯。

網(wǎng)絡(luò)斷開(kāi)

我們?cè)賮?lái)模擬一下網(wǎng)絡(luò)斷開(kāi)的情況,切到 NetWork,網(wǎng)絡(luò)換成** Offline**,然后在切換路由,下面是我實(shí)操的效果:

圖片

會(huì)看到在沒(méi)有網(wǎng)絡(luò)的情況下,進(jìn)度條件還是在那一直轉(zhuǎn),一直加載,沒(méi)有及時(shí)的反饋,體驗(yàn)也是很差的。

我們想要啥效果

我們團(tuán)隊(duì)想要的效果是:

  • 只要點(diǎn)擊菜單,頁(yè)面就要切換,即使在弱網(wǎng)的情況
  • 在加載失敗時(shí)要給予一個(gè)失敗的反饋,而不是讓用戶傻傻的在那里等待
  • 支持每個(gè)路由跳轉(zhuǎn)時(shí)特有的加載特效

尋找解決方案

為了解決上面的問(wèn)題,我們需要一種能異步加載并且能自定義 loading 的方法,查閱了官方文檔,Vue2.3 中新增了一個(gè)異步組件,允許我們自定義加載方式,用法如下:

  1. const AsyncComponent = () => ({ 
  2.   // 需要加載的組件 (應(yīng)該是一個(gè) `Promise` 對(duì)象) 
  3.   component: import('./MyComponent.vue'), 
  4.   // 異步組件加載時(shí)使用的組件 
  5.   loading: LoadingComponent, 
  6.   // 加載失敗時(shí)使用的組件 
  7.   error: ErrorComponent, 
  8.   // 展示加載時(shí)組件的延時(shí)時(shí)間。默認(rèn)值是 200 (毫秒) 
  9.   delay: 200, 
  10.   // 如果提供了超時(shí)時(shí)間且組件加載也超時(shí)了, 
  11.   // 則使用加載失敗時(shí)使用的組件。默認(rèn)值是:`Infinity` 
  12.   timeout: 3000 
  13. }) 

注意如果你希望在 Vue Router 的路由組件中使用上述語(yǔ)法的話,你必須使用 Vue Router 2.4.0+ 版本。

但我們現(xiàn)在是使用 Vue3 開(kāi)發(fā)的,所以還得看下 Vue3 有沒(méi)有類(lèi)似的方法。查閱了官方文檔,也找到了一個(gè)方法 defineAsyncComponent,用法大概如下:

  1. import { defineAsyncComponent } from 'vue' 
  2.  
  3. const AsyncComp = defineAsyncComponent({ 
  4.   // 工廠函數(shù) 
  5.   loader: () => import('./Foo.vue'), 
  6.   // 加載異步組件時(shí)要使用的組件 
  7.   loadingComponent: LoadingComponent, 
  8.   // 加載失敗時(shí)要使用的組件 
  9.   errorComponent: ErrorComponent, 
  10.   // 在顯示 loadingComponent 之前的延遲 | 默認(rèn)值:200(單位 ms) 
  11.   delay: 200, 
  12.   // 如果提供了 timeout ,并且加載組件的時(shí)間超過(guò)了設(shè)定值,將顯示錯(cuò)誤組件 
  13.   // 默認(rèn)值:Infinity(即永不超時(shí),單位 ms) 
  14.   timeout: 3000, 
  15.   // 定義組件是否可掛起 | 默認(rèn)值:true 
  16.   suspensible: false
  17.   /** 
  18.    * 
  19.    * @param {*} error 錯(cuò)誤信息對(duì)象 
  20.    * @param {*} retry 一個(gè)函數(shù),用于指示當(dāng) promise 加載器 reject 時(shí),加載器是否應(yīng)該重試 
  21.    * @param {*} fail  一個(gè)函數(shù),指示加載程序結(jié)束退出 
  22.    * @param {*} attempts 允許的最大重試次數(shù) 
  23.    */ 
  24.   onError(error, retry, fail, attempts) { 
  25.     if (error.message.match(/fetch/) && attempts <= 3) { 
  26.       // 請(qǐng)求發(fā)生錯(cuò)誤時(shí)重試,最多可嘗試 3 次 
  27.       retry() 
  28.     } else { 
  29.       // 注意,retry/fail 就像 promise 的 resolve/reject 一樣: 
  30.       // 必須調(diào)用其中一個(gè)才能繼續(xù)錯(cuò)誤處理。 
  31.       fail() 
  32.     } 
  33.   } 
  34. }) 

但在官方 V3 遷移指南中 官方有指出下面這段話:

  • Vue Router 支持一個(gè)類(lèi)似的機(jī)制來(lái)異步加載路由組件,也就是俗稱(chēng)的懶加載。盡管類(lèi)似,這個(gè)功能和 Vue 支持的異步組件是不同的。當(dāng)用 Vue Router 配置路由組件時(shí),你不應(yīng)該使用 defineAsyncComponent。你可以在 Vue Router 文檔的懶加載路由章節(jié)閱讀更多相關(guān)內(nèi)容。

官網(wǎng)說(shuō)不應(yīng)該使用defineAsyncComponent來(lái)做路由懶加載,但沒(méi)說(shuō)不能使用,而我們現(xiàn)在需要這個(gè)方法,所以還是選擇用了(后面遇到坑在分享出來(lái))。

思路

有了上面的方法,我們現(xiàn)在的思路就是重寫(xiě) Vue3 中的 createRouter方法,在createRouter 我們遞歸遍歷傳進(jìn)來(lái)的 routes, 判斷當(dāng)前的組件是否是異步加載組件,如果是我們用 defineAsyncComponent方法給它包裝起來(lái)。

下面是我現(xiàn)在封裝的代碼:

  1. import { RouteRecordMenu } from '@/components/AdminLayout'
  2. import PageLoading from '@/components/AdminLayout/components/PageLoading'
  3. import PageResult from '@/components/AdminLayout/components/PageResult'
  4. import { 
  5.   AsyncComponentLoader, 
  6.   AsyncComponentOptions, 
  7.   defineAsyncComponent, 
  8.   h, 
  9. from 'vue'
  10. import { createRouter as vueCreateRouter, RouterOptions } from 'vue-router'
  11.  
  12. /** 
  13.  * 
  14.  * @param routerOptions vue createRouter 的參數(shù) 
  15.  * @param asyncComponentOptions 異步組件配置參數(shù) 
  16.  * @returns 
  17.  */ 
  18.  
  19. export default function createRouter( 
  20.   routerOptions: RouterOptions, 
  21.   { 
  22.     loadingComponent = PageLoading, 
  23.     errorComponent = PageResult, 
  24.     delay = 200, 
  25.     timeout = 3000, 
  26.     suspensible = false
  27.     onError, 
  28.   }: Omit<AsyncComponentOptions, 'loader'> = {}, 
  29. ) { 
  30.   const treedRoutes = (childrenRoutes: RouteRecordMenu[]) => { 
  31.     return childrenRoutes.map((childrenRoute: RouteRecordMenu) => { 
  32.       if (childrenRoute.children) { 
  33.         childrenRoute.children = treedRoutes(childrenRoute.children); 
  34.       } else { 
  35.         if (typeof childrenRoute.component === 'function') { 
  36.           childrenRoute.component = defineAsyncComponent({ 
  37.             loader: childrenRoute.component as AsyncComponentLoader, 
  38.             loadingComponent, 
  39.             errorComponent, 
  40.             delay, 
  41.             timeout, 
  42.             suspensible, 
  43.             onError, 
  44.           }); 
  45.         } 
  46.       } 
  47.       return childrenRoute; 
  48.     }); 
  49.   }; 
  50.   treedRoutes(routerOptions.routes); 
  51.   return vueCreateRouter(routerOptions); 

上面重寫(xiě)了 createRouter 方法,并提供了可選的配置參數(shù) routerOptions,routerOptions里面的字段其實(shí)就是defineAsyncComponent里面了的參數(shù),除了 loder。

有了現(xiàn)在的 createRouter,我們來(lái)看相同場(chǎng)景,不同效果。

弱網(wǎng)絡(luò)

圖片

可以看到第二種方案在弱方案的情況下,只要我們切換路由,頁(yè)面也會(huì)馬上進(jìn)行切換,過(guò)渡方式也是采用我們指定的。不像第一種方案一樣,頁(yè)面會(huì)停在點(diǎn)擊之前的頁(yè)面,然后在一下的刷過(guò)去。

當(dāng)切換到菜單時(shí),因?yàn)檫@里我指定的時(shí)間 timeout 為 3 秒,所以在3秒內(nèi)如果沒(méi)有加載出來(lái),就會(huì)顯示我們指定的 errorComponent。

現(xiàn)在,打開(kāi)瀏覽器,切到 NetWork,網(wǎng)絡(luò)換成** Offline**,也就是斷網(wǎng)的情況,我們?cè)趤?lái)看下效果。

網(wǎng)絡(luò)斷開(kāi)

圖片

可以看到,當(dāng)我們網(wǎng)絡(luò)斷開(kāi)的時(shí)候,在切換頁(yè)面時(shí),會(huì)顯示我們指定 errorComponent,不像第一種方式一樣會(huì)一直卡在頁(yè)面上加載。

變換 Loading

下面來(lái)看看,我事例路由:

router.ts

  1. import { RouteRecordRaw, RouterView, createWebHistory } from 'vue-router' 
  2. import { RouteRecordMenu } from '@ztjy/antd-vue/es/components/AdminLayout' 
  3. import { AdminLayout, Login } from '@ztjy/antd-vue-admin' 
  4. import createRouter from './createRoute' 
  5.  
  6. export const routes: RouteRecordMenu[] = [ 
  7.   { 
  8.     path: '/menu'
  9.     name'Menu'
  10.     component: RouterView, 
  11.     redirect: '/menu/list'
  12.     meta: { 
  13.       icon: 'fas fa-ad'
  14.       title: '菜單一'
  15.     }, 
  16.     children: [ 
  17.       { 
  18.         path: '/menu/list'
  19.         component: () => import('@/pages/Menu1'), 
  20.         meta: { 
  21.           title: '列表'
  22.         }, 
  23.       }, 
  24.     ], 
  25.   }, 
  26.   { 
  27.     path: '/menu2'
  28.     name'Menu2'
  29.     component: RouterView, 
  30.     redirect: '/menu2/list'
  31.     meta: { 
  32.       icon: 'fas fa-ad'
  33.       title: '菜單二'
  34.     }, 
  35.     children: [ 
  36.       { 
  37.         path: '/menu2/list'
  38.         component: () => import('@/pages/Menu2'), 
  39.         meta: { 
  40.           title: '列表'
  41.         }, 
  42.       }, 
  43.     ], 
  44.   }, 
  45.   { 
  46.     path: '/menu3'
  47.     name'Menu3'
  48.     component: RouterView, 
  49.     redirect: '/menu3/list'
  50.     meta: { 
  51.       icon: 'fas fa-ad'
  52.       title: '菜單三'
  53.     }, 
  54.     children: [ 
  55.       { 
  56.         path: '/menu3/list'
  57.         component: () => import('@/pages/Menu3'), 
  58.         meta: { 
  59.           title: '列表'
  60.         }, 
  61.       }, 
  62.     ], 
  63.   }, 
  64.  
  65. const router = createRouter({ 
  66.   history: createWebHistory('/'), 
  67.   routes: [ 
  68.     { 
  69.       path: '/login'
  70.       component: Login, 
  71.       props: { 
  72.         title: '商化前端后臺(tái)登錄'
  73.       }, 
  74.     }, 
  75.     { 
  76.       path: '/'
  77.       redirect: '/menu'
  78.       component: AdminLayout, 
  79.       props: { 
  80.         title: '商化前端 后臺(tái) 模板'
  81.         routes, 
  82.       }, 
  83.       meta: { 
  84.         title: '首頁(yè)'
  85.       }, 
  86.       children: routes as RouteRecordRaw[], 
  87.     }, 
  88.   ], 
  89. }) 
  90.  
  91. export default router 

我們現(xiàn)在想用下面已經(jīng)封裝好的冒泡加載方式來(lái)代替菊花的樣式:

圖片

很簡(jiǎn)單,我們只需要把對(duì)應(yīng)加載組件(BubbleLoading)的名稱(chēng),傳給 createRouter 既可,為了演示效果,我們把網(wǎng)絡(luò)切花到 Slow 3G,代碼如下:

router.ts

  1. /***這里省略很多字**/ 
  2. const router = createRouter( 
  3.   { 
  4.     history: createWebHistory('/'), 
  5.     routes: [ 
  6.       /***這里省略很多字**/ 
  7.     ] 
  8.   }, 
  9.   { 
  10.     loadingComponent: BubbleLoading, // 看這里看這里 
  11.   } 
  12.  
  13. export default router 
圖片

花里胡哨

如果我們只要點(diǎn)擊菜單二才用 BubbleLoading ,點(diǎn)擊其它的就用菊花的加載,那又要怎么做呢?

這里,大家如果認(rèn)真看上面二次封裝的 createRouter 方法,可能就知道怎么做了,其中里面有一個(gè)判斷就是

  1. typeof childrenRoute.component === 'function' 

其實(shí)我做的就是判斷如果外面?zhèn)鬟M(jìn)來(lái)的路由采用的異步加載的方式,我才對(duì)用 defineAsyncComponent 重寫(xiě),其它的加載方式我是不管的,所以,我們想要自定義各自的加載方式,只要用 defineAsyncComponent 重寫(xiě)即可。

回到我們的 router.ts 代碼,

  1. // 這里省略一些代碼 
  2. export const routes: RouteRecordMenu[] = [ 
  3.    // 這里省略一些代碼 
  4.   { 
  5.     path: '/menu2'
  6.     name'Menu2'
  7.     component: RouterView, 
  8.     redirect: '/menu2/list'
  9.     meta: { 
  10.       icon: 'fas fa-ad'
  11.       title: '菜單二'
  12.     }, 
  13.     children: [ 
  14.       { 
  15.         path: '/menu2/list'
  16.         component: defineAsyncComponent({  // 看這里 
  17.           loader: () => import('@/pages/Menu2'),// 看這里 
  18.           loadingComponent: BubbleLoading,// 看這里 
  19.         }), 
  20.         meta: { 
  21.           title: '列表'
  22.         }, 
  23.       }, 
  24.     ], 
  25.   }, 
  26.  // 這里省略一些代碼 
  27.  // 這里省略一些代碼 

在上面,我們用defineAsyncComponent定義菜單二的 component加載方式,運(yùn)行效果如下:

圖片

從圖片可以看出點(diǎn)擊菜單一和三時(shí),我們使用菊花的加載方式,點(diǎn)擊菜單二就會(huì)顯示我們自定義的加載方式。

注意

這里有一個(gè)顯性的 bug,就是下面代碼:

  1. component: defineAsyncComponent({  
  2.    loader: () => import('@/pages/Menu2'), 
  3.    loadingComponent: BubbleLoading, 
  4. }), 

不能用函數(shù)的方式來(lái)寫(xiě),如下所示:

  1. component: () => defineAsyncComponent({  
  2.    loader: () => import('@/pages/Menu2'), 
  3.    loadingComponent: BubbleLoading, 
  4. }), 

這里因?yàn)槲以?createRouter 方法中使用 typeof childrenRoute.component === 'function'來(lái)判斷,所以上面代碼又會(huì)被defineAsyncComponent包起來(lái),變成兩層的defineAsyncComponent,所以頁(yè)面加載會(huì)出錯(cuò)。

我也想解決這個(gè)問(wèn)題,但查了很多資料,沒(méi)有找到如何在方法中,判斷方法采用的是defineAsyncComponent 方式,即下面這種形式:

  1. component: () => defineAsyncComponent({  
  2.    loader: () => import('@/pages/Menu2'), 
  3.    loadingComponent: BubbleLoading, 
  4. }), 

本文到這里就分享完了,我是刷碗智,我們下期見(jiàn)~

 

責(zé)任編輯:姜華 來(lái)源: 大遷世界
相關(guān)推薦

2011-11-09 15:49:52

API

2009-12-03 11:12:42

路由器限速

2009-11-20 11:37:11

Oracle完全卸載

2011-06-22 09:45:46

JavaScriptAPI

2023-11-30 10:13:17

TensorRT架構(gòu)

2021-04-21 15:46:23

區(qū)塊鏈互聯(lián)網(wǎng)技術(shù)

2023-04-28 15:24:33

數(shù)字化轉(zhuǎn)型數(shù)字經(jīng)濟(jì)

2016-01-08 10:03:07

硅谷通吃互聯(lián)網(wǎng)

2019-08-08 10:18:15

運(yùn)維架構(gòu)技術(shù)

2021-05-24 10:55:05

Netty單機(jī)并發(fā)

2010-03-30 10:44:05

Nginx啟動(dòng)

2024-12-04 13:52:30

2022-09-09 08:41:43

Netty服務(wù)端驅(qū)動(dòng)

2013-07-15 10:45:42

2011-04-29 10:32:46

項(xiàng)目管理

2017-11-14 08:25:36

數(shù)據(jù)庫(kù)MySQL安全登陸

2016-06-15 11:06:27

云計(jì)算AWS

2021-06-04 05:54:53

CIO數(shù)據(jù)驅(qū)動(dòng)數(shù)字轉(zhuǎn)型

2010-05-20 17:29:02

IIS安全

2018-01-12 15:17:40

數(shù)據(jù)庫(kù)水平分庫(kù)數(shù)據(jù)遷移
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)