iT邦幫忙

2023 iThome 鐵人賽

DAY 17
1
Vue.js

Vue3歡樂套件箱耶系列 第 17

開箱17:是不是跟我一樣困惑異步加載???defineAsyncComponent範例應用

  • 分享至 

  • xImage
  •  

上篇簡單介紹了圖片懶加載,那其實頁面(組件)也可以懶加載,因此本篇要介紹改進初始頁面加載的好方法

也就是將我們的應用程式以較小的區塊加載,而不必在頁面加載時加載每個元件。


▲ 資料來源:https://learnvue.co/articles/lazy-load-components

首先我們先來導讀官網介紹

介紹

defineAsyncComponent (Vue 3.x才有的)

在大型專案中,我們可能需要拆分應用為更小的區塊,並僅在需要時再從伺服器載入相關元件。Vue 提供了defineAsyncComponent方法來實現此功能,它在運行時是懶加載的。參數可以是一個非同步載入函數,或是對載入行為進行更具體自訂的選項物件。

如果你是寫react的可以搜尋[lazy&Suspense]關鍵字,像是這影片介紹

function defineAsyncComponent(
  source: AsyncComponentLoader | AsyncComponentOptions
): Component

type AsyncComponentLoader = () => Promise<Component>

interface AsyncComponentOptions {
  loader: AsyncComponentLoader
  loadingComponent?: Component
  errorComponent?: Component
  delay?: number
  timeout?: number
  suspensible?: boolean
  onError?: (
    error: Error,
    retry: () => void,
    fail: () => void,
    attempts: number
  ) => any
}

詳細介紹可前往>> 官網介紹

使用方法

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
})

▲ defineAsyncComponent方法接收一個傳回Promise 的載入函數

而多數情況下我們會將ES模組動態導入ES module dynamic import和defineAsyncComponent搭配使用,因為ES module dynamic import也會回傳一個Promise,因此我們也可以用它來匯入Vue 單一檔案元件

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

▲ 最後得到的AsyncComp是一個外層包裝過的元件,僅在頁面需要它渲染時才會呼叫載入內部實際元件的函數。

而剛剛這是最簡單的使用方式,在非同步操作中難免會需要載入和錯誤狀態,因此defineAsyncComponent()也支援在進階選項中處理這些狀態

載入與錯誤狀態

const AsyncComp = defineAsyncComponent({
  // the loader function
  loader: () => import('./Foo.vue'),

  // A component to use while the async component is loading
  loadingComponent: LoadingComponent,

  // Delay before showing the loading component. Default: 200ms.
  delay: 200,
  
  // A component to use if the load fails 
  errorComponent: ErrorComponent,
  // The error component will be displayed if a timeout is 
  
  //provided and exceeded. Default: Infinity.
  timeout: 3000
})

loader:使用動態引入,引入欲使用的 Component
loadingComponent:加載異步组件時使用的组件
delay:展示加載组件前的延遲時間,預設值為 200 毫秒
errorComponent:加載失敗顯示 error Component
timeout:設定超過多久時間後未加載完成則會顯示錯誤组件,預設值Infinity無窮大

那接著我們跟著此篇,來做一個範例

典型範例

功能大綱

  • 點擊按鈕打開下面視窗

示意圖如下
https://ithelp.ithome.com.tw/upload/images/20231002/20142016Jt5Tkrv8xI.png

(結構)
project-root/
├─ src/
│ ├─ views/
│ │ ├─ AsyncComponent.vue
│ │
│ ├─ components/
│ │ ├─ Dynamic/
│ │ │ ├─ DynamicA.vue
│ │ │ ├─ DynamicModel.vue
│ │ │ ├─ Error.vue
│ │ │ ├─ Loading.vue

└─ ...

以往我們寫法,可能會是
//dynamicA.vue

<template>
  <div>
    <button @click="show = !show">點我打開視窗</button>
    <dynamicModel v-if="show" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import dynamicModel from './dynamicModel.vue'; // 靜態載入元件

const show = ref(false);
</script>

這樣的寫法結果為
https://ithelp.ithome.com.tw/upload/images/20231002/20142016XcWU0SQepR.png
在還沒打開視窗之前,網頁仍是載入此元件了

備註:網頁載入和渲染是兩個不同的概念,網路載入是指從伺服器取得網頁內容和資源的過程;渲染是指瀏覽器將下載的網頁內容和資源轉換為使用者視覺化介面的過程。

而我們需求是只在需要時加載它,我們改寫成defineAsyncComponent()

<template>
  <div>
    <button @click="show = !show">點我打開視窗</button>
    <dynamicModel v-if="show" />
  </div>
</template>

<script setup>
import { ref,defineAsyncComponent} from 'vue';
import ErrorComponent from './Error.vue';
import LoadingComponent from './Loading.vue';

const dynamicModel = defineAsyncComponent({
  loader: () =>
    new Promise(resolve => {
      setTimeout(() => {
        resolve(import('../Dynamic/DynamicModel.vue'));
      }, 3000); // 模擬3秒的載入時間
    }),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 1000,
  timeout: 10000
});

const show = ref(false);
</script>

成果為下

(一開始沒載入dynamicModel,打開後才載入)
前往 >> Demo 網址:https://hahasister-ironman-project.netlify.app/#/asyncComponent

使用這個非同步的包裝組件無縫地替換原始組件,同時實現延遲載入了!

參考資料
https://learnvue.co/articles/lazy-load-components#lazy-loading-a-popup-component-with-defineasynccomponent

#How to use dynamic Components in Vue


上一篇
開箱16:圖片懶加載Lazy Loading範例應用
下一篇
開箱18:圖片/元件懶加載~vue3 + IntersectionObserver API範例應用
系列文
Vue3歡樂套件箱耶30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言