跳到主要內容
前端開發

JavaScript 動態載入:import() 與 Vue 非同步元件

整理靜態 import、動態 import() 與 Vue defineAsyncComponent 的使用方式,並簡單補充 Vite import.meta.glob 的用途。

W

  • 一般 import 會在模組載入時建立依賴;import() 則可以等到真的需要功能時再載入。
  • 打包工具通常也會利用動態載入進行 code splitting,把較大的功能拆成獨立 chunk。
  • 靜態載入與動態載入流程比較

靜態 import

一般最常使用的是靜態 import:

import { formatReport } from './report'

formatReport(data)

適合:

  • 頁面一定會使用的功能
  • 體積不大的模組
  • 元件初始化時就需要的程式碼

如果沒有特別需要延後載入,使用一般 import 就可以。

動態 import()

import() 會回傳 Promise,可以等到需要時再載入:

async function exportReport() {
  const { formatReport } = await import('./report')

  return formatReport(data)
}

比較適合:

  • 大型圖表
  • 編輯器
  • 匯出功能
  • 只有部分使用者會使用的功能

例如使用者按下「匯出」之後,才載入相關套件,就不用把這些程式碼全部放進初始 bundle。

路徑最好讓打包工具可以在 build 時分析:

await import('./report')

不要依賴完全由執行期字串拼出的路徑,否則打包工具可能無法判斷要把哪些檔案放進產物。

Vue 3 非同步元件

Vue 可以搭配 defineAsyncComponent() 延後載入元件:

<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

const HeavyEditor = defineAsyncComponent(
  () => import('./HeavyEditor.vue')
)
</script>

<template>
  <HeavyEditor v-if="isEditing" />
</template>

這種方式適合只在特定情況下才會顯示的元件。

例如:

開啟編輯器
打開大型圖表
開啟報表
進入特定功能頁

如果是首屏一定會顯示的核心元件,就沒有必要全部改成非同步載入。

補充:Vite 的 import.meta.glob

如果需要一次取得某個資料夾裡的一批模組,Vite 另外提供:

const modules = import.meta.glob('./widgets/*.vue')

假設資料夾是:

widgets/
├── chart.vue
├── map.vue
└── table.vue

Vite 會建立類似這樣的對照:

{
  './widgets/chart.vue': () => import('./widgets/chart.vue'),
  './widgets/map.vue': () => import('./widgets/map.vue'),
  './widgets/table.vue': () => import('./widgets/table.vue')
}

需要某個模組時再載入:

const module =
  await modules['./widgets/chart.vue']?.()

預設是 lazy loading。

如果希望全部直接載入,可以使用:

const modules = import.meta.glob(
  './widgets/*.vue',
  { eager: true }
)

可以簡單理解成:

import.meta.glob(...)
→ 找出一批符合條件的檔案
→ 需要時才載入

import.meta.glob(..., { eager: true })
→ 找出一批符合條件的檔案
→ 一開始全部載入

import.meta.glob() 是 Vite 提供的功能,不是 JavaScript 原生 API。

一般 Vue / Nuxt 專案不一定會用到,只有需要批次取得一組檔案時才比較方便。

怎麼選

情況建議
一開始就會使用靜態 import
使用者操作後才需要import()
Vue 大型元件延後載入defineAsyncComponent()
Vite 批次取得一組檔案import.meta.glob()

小結

一般情況先使用:

import something from './something'

只有確定某個功能不需要一開始載入時,再考慮:

const module = await import('./something')

Vue 元件則可以使用:

defineAsyncComponent(
  () => import('./HeavyComponent.vue')
)

至於 import.meta.glob(),把它當成 Vite 提供的批次 import 工具就可以,不需要每個專案都使用。

參考資料

分享這篇