成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[417936]]

背景

在開始之前,先介紹一下我們目前新項目的采用的技術棧

  • 前端公共庫:vue3 + typescript + jsx + antdVue
  • 后臺項目:vue3 + typescript + jsx + antdVue

沒錯,我們現在都采用 ts + jsx 語法來開發(fā)新項目,這里可能會有小伙伴說了,不用 template 嗎,裝啥裝。這里面要討論內容很多,下次有機會在分享,今天不討論這個問題。

回到正文~~

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

傳統方式

這個方法大家應該都用過,就是在路由切換的時候,頂部顯示一個加載的進度條,我們這里借助的庫是 NProgress。

第一步,需要安裝插件:

  1. yarn add  nprogress 

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

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

第三步,監(jiān)聽路由跳轉,進入頁面執(zhí)行插件動畫。

路由跳轉中

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

跳轉結束

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

很簡單的一個配置,運行后,當我們切換路由時就會看到頂部有一個進度條了:

圖片

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

  • 弱網絡的情況,頁面會卡那里,動的很慢。
  • 當網絡斷開時,進度條件會一直處于加載的狀態(tài),并沒有及時反饋加載失敗。
  • 當有比較特殊需求,如,當加載菜單二時,我想用骨架屏的方案來加載,當加載菜單三,我想要用傳統的菊花樣式加載,這種情況,我們現在的方案是很難做的。

弱網絡

我們模擬一下弱網絡,打開瀏覽器控制臺,切到 NetWork,網絡換成** Slow 3G**,然后在切換路由,下面是我實操的效果:

圖片

可以看到,我們切換到菜單二時,進度條件會慢慢走,頁面沒有及時切換到菜單二的界面,如果頁面內容越多,效果越明顯。

網絡斷開

我們再來模擬一下網絡斷開的情況,切到 NetWork,網絡換成** Offline**,然后在切換路由,下面是我實操的效果:

圖片

會看到在沒有網絡的情況下,進度條件還是在那一直轉,一直加載,沒有及時的反饋,體驗也是很差的。

我們想要啥效果

我們團隊想要的效果是:

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

尋找解決方案

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

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

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

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

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

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

  • Vue Router 支持一個類似的機制來異步加載路由組件,也就是俗稱的懶加載。盡管類似,這個功能和 Vue 支持的異步組件是不同的。當用 Vue Router 配置路由組件時,你不應該使用 defineAsyncComponent。你可以在 Vue Router 文檔的懶加載路由章節(jié)閱讀更多相關內容。

官網說不應該使用defineAsyncComponent來做路由懶加載,但沒說不能使用,而我們現在需要這個方法,所以還是選擇用了(后面遇到坑在分享出來)。

思路

有了上面的方法,我們現在的思路就是重寫 Vue3 中的 createRouter方法,在createRouter 我們遞歸遍歷傳進來的 routes, 判斷當前的組件是否是異步加載組件,如果是我們用 defineAsyncComponent方法給它包裝起來。

下面是我現在封裝的代碼:

  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 的參數 
  15.  * @param asyncComponentOptions 異步組件配置參數 
  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); 

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

有了現在的 createRouter,我們來看相同場景,不同效果。

弱網絡

圖片

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

當切換到菜單時,因為這里我指定的時間 timeout 為 3 秒,所以在3秒內如果沒有加載出來,就會顯示我們指定的 errorComponent。

現在,打開瀏覽器,切到 NetWork,網絡換成** Offline**,也就是斷網的情況,我們在來看下效果。

網絡斷開

圖片

可以看到,當我們網絡斷開的時候,在切換頁面時,會顯示我們指定 errorComponent,不像第一種方式一樣會一直卡在頁面上加載。

變換 Loading

下面來看看,我事例路由:

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: '商化前端后臺登錄'
  73.       }, 
  74.     }, 
  75.     { 
  76.       path: '/'
  77.       redirect: '/menu'
  78.       component: AdminLayout, 
  79.       props: { 
  80.         title: '商化前端 后臺 模板'
  81.         routes, 
  82.       }, 
  83.       meta: { 
  84.         title: '首頁'
  85.       }, 
  86.       children: routes as RouteRecordRaw[], 
  87.     }, 
  88.   ], 
  89. }) 
  90.  
  91. export default router 

我們現在想用下面已經封裝好的冒泡加載方式來代替菊花的樣式:

圖片

很簡單,我們只需要把對應加載組件(BubbleLoading)的名稱,傳給 createRouter 既可,為了演示效果,我們把網絡切花到 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 
圖片

花里胡哨

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

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

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

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

回到我們的 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加載方式,運行效果如下:

圖片

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

注意

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

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

不能用函數的方式來寫,如下所示:

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

這里因為我在 createRouter 方法中使用 typeof childrenRoute.component === 'function'來判斷,所以上面代碼又會被defineAsyncComponent包起來,變成兩層的defineAsyncComponent,所以頁面加載會出錯。

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

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

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

 

責任編輯:姜華 來源: 大遷世界
相關推薦

2011-11-09 15:49:52

API

2009-12-03 11:12:42

路由器限速

2009-11-20 11:37:11

Oracle完全卸載

2023-11-30 10:13:17

TensorRT架構

2011-06-22 09:45:46

JavaScriptAPI

2023-04-28 15:24:33

數字化轉型數字經濟

2021-04-21 15:46:23

區(qū)塊鏈互聯網技術

2010-03-30 10:44:05

Nginx啟動

2024-12-04 13:52:30

2021-05-24 10:55:05

Netty單機并發(fā)

2022-09-09 08:41:43

Netty服務端驅動

2019-08-08 10:18:15

運維架構技術

2016-01-08 10:03:07

硅谷通吃互聯網

2013-07-15 10:45:42

2011-04-29 10:32:46

項目管理

2018-01-12 15:17:40

數據庫水平分庫數據遷移

2016-06-15 11:06:27

云計算AWS

2021-06-04 05:54:53

CIO數據驅動數字轉型

2010-05-20 17:29:02

IIS安全

2017-11-14 08:25:36

數據庫MySQL安全登陸
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区国产精品 | 久久国产精品视频免费看 | 国产精品国产 | 日韩精品a在线观看图片 | 亚洲一区二区久久 | 国产 日韩 欧美 在线 | 亚洲精品成人 | 久久久久久91 | 日本久久精品 | 中文字幕一区二区三区四区五区 | 麻豆视频在线免费看 | 一区二区三区四区在线视频 | 国产精品福利网站 | 日韩欧美在线视频观看 | 国产精品毛片一区二区在线看 | 色婷婷久久久久swag精品 | 日日夜夜视频 | 成人免费在线播放 | 91视在线国内在线播放酒店 | 欧一区 | 精品国产乱码久久久久久丨区2区 | 超碰3| 伊人伊人伊人 | 国产欧美日韩精品一区 | 天堂在线www| 国产亚洲人成a在线v网站 | 999久久久国产精品 欧美成人h版在线观看 | 在线国产精品一区 | 亚洲视频在线看 | 欧美一区在线看 | 国产一区二区三区免费 | 国产69精品久久99不卡免费版 | 亚洲 中文 欧美 日韩 在线观看 | 日韩中文一区二区三区 | 天天操网| 免费在线h视频 | 欧洲精品视频一区 | 亚洲福利一区二区 | 亚洲永久精品国产 | 色婷婷国产精品综合在线观看 | 91性高湖久久久久久久久_久久99 |