/
CATEGORY
JavaScript
/
動態渲染 Vue 元件以及動態導入模組,簡化重複邏輯

動態渲染 Vue 元件以及動態導入模組,簡化重複邏輯

MUKI AI Summary

在開發模組化功能時,使用大量的 v-if 來動態渲染 Vue 元件會導致程式碼重複且難以維護。為了解決這個問題,可以透過動態渲染元件和動態導入模組來簡化邏輯。首先,使用 Barrel Files 將模組集中管理,接著在 App.vue 中根據 moduleId 渲染對應的 Vue 元件,避免用大量的 v-if 判斷。進一步優化可以使用 Vue 的 動態元件搭配 Mapping 表格來渲染模組,這樣可以大幅減少重複的程式碼。

為了進一步簡化,推薦使用 Vite 的 import.meta.glob 功能,這樣可以自動批量導入模組,無需手動更新 Mapping 表格或 index.ts 檔案。透過這種方式,不僅程式碼更簡潔,未來新增或修改元件時只需將新檔案放入目錄即可,無需更改現有邏輯,讓開發和維護更加高效。這種動態渲染方式特別適合需要處理大量條件邏輯的場景。...

最近在開發一個模組化的功能,遇到要根據不同的條件動態渲染元件的狀況,雖然使用大量的 v-if 也能解決這問題,但程式碼重複性很高,也不利於維護,重點是也不美觀 XXD。所以我們可以用動態渲染元件搭配動態導入模組,讓程式碼好維護並好擴展。

實際情況

以下是我實際開發時碰到的狀況,節錄片段程式碼與大家分享。

▼ 這是我的模組目錄結構,現在的需求是要在 App.vue 匯入這些模組

src/
├─ components/
│  ├─ A1-1-2.vue
│  ├─ A1-2-2.vue
│  ├─ A1-3-3.vue
│  ├─ B1-1-1.vue
│  ├─ C1-1-2.vue
│  └─ index.ts
├─ App.vue

▼ 我先用 Barrel Files 將模組集中在 index.ts 統一管理 (關於 Barrel Files 的介紹,可以參考我寫的使用 Barrel Files 的好處與大多數人不推薦的原因

export { default as A112 } from './A1-1-2.vue'
export { default as A122 } from './A1-2-2.vue'
export { default as A133 } from './A1-3-3.vue'
export { default as B111 } from './B1-1-1.vue'
export { default as C112 } from './C1-1-2.vue'

▼ 接著在 App.vue 匯入元件,並根據 moduleId 來渲染對應的 Vue 元件,一開始先使用 v-if 暴力解

<template>
  <div>
    <Module.A112 v-if="moduleId === 'a1-1-2'" />
    <Module.A122 v-if="moduleId === 'a1-2-2'" />
    <Module.A133 v-if="moduleId === 'a1-3-3'" />
    <!-- 其他模組 -->
  </div>
</template>
<script lang="ts" setup>
  import * as Modules from '@/components/home/module'
</script>

問題點應該蠻明確的,一來程式碼重複性高,二來難以擴展,因為新增模組時都要手動編輯,容易出錯成本也高;再來就是模組越多時,真的看得很阿雜!

解法一:動態渲染元件

▼ 像上面這種重複性高的元件,我們可以做一個 Mapping 表格,搭配 Vue 的動態元件 <component> 來渲染對應的模組

<template>
  <div>
    <component :is="moduleMapping[moduleId]" />
  </div>
</template>

<script lang="ts" setup>
import * as Module from '@/components/home/module'
const moduleMapping = {
  'a1-1-2': Module.A112,
  'a1-2-2': Module.A122,
  'a1-3-3': Module.A133,
  'b1-1-1': Module.B111,
  'c1-1-2': Module.C112
};
</script>

透過 Mapping 映射表與 <component> 動態元件,就不需要用一堆的 v-if 判斷條件,可以大幅簡化程式碼。

但這樣做只是把 v-if 的邏輯判斷放到 Mapping 映射表,我還是要同時維護 Mapping 映射表以及用來管理模組的 index.ts 檔案,感覺只是換湯不換藥。

所以接下來推薦第二種解法:動態元件搭配動態模組,真正做到全自動化

解法二:動態渲染元件與動態管理模組

▼ 回到剛剛的 index.ts,我是用 Barrel Files 將模組集中管理。但未來只要新增模組,這邊就要再更新一次,Mapping 映射表也要再寫一次,非常麻煩

export { default as A112 } from './A1-1-2.vue'
export { default as A122 } from './A1-2-2.vue'
export { default as A133 } from './A1-3-3.vue'
export { default as B111 } from './B1-1-1.vue'
export { default as C112 } from './C1-1-2.vue'

幸好 Vite 有一個 import.meta.glob 能幫助我們以動態方式批量導入模組

什麼是 import.meta.glob

import.meta.glob 是 Vite 提供的功能,可以讓我們以動態方式批量導入模組。它可以載入特定目錄中的檔案,並生成一個檔案路徑與模組的映射,對於需要處理大量模組(例如元件、設定檔)時特別有用。

使用 import.meta.glob 批量導入模組

▼ 將 index.ts 檔案修改為以下程式碼

interface Modules {
  [key: string]: { default: unknown }
}
// 以相對路徑和萬用字元(*)來匹配檔案
// 預設情況下,模組是延遲載入的。如果需要立即載入,可以設定 eager: true:
const modules: Modules = import.meta.glob('./*.vue', { eager: true })
export default Object.keys(modules).reduce((acc: Record<string, unknown>, path: string) => {
  // 視情況處理匯出的 moduleName
  const moduleName = path.replace('./', '').replace('.vue', '').toLowerCase()
  acc[moduleName] = modules[path].default
  return acc
}, {})

▼ 可以印出 acc 來確認物件名稱是否如你所預期

▼ 回到 App.vue,修改 import 內容,並大膽的刪除 Mapping 映射表

<script lang="ts" setup>
  // 原本的 import
  import * as Module from '@/components/home/module'
  // 修改後的 import
  import Modules from '@/components/home/module'
  
  // 刪除以下內容
  const moduleMapping = {
    'a1-1-2': Module.A112,
    'a1-2-2': Module.A122,
    'a1-3-3': Module.A133,
    'b1-1-1': Module.B111,
    'c1-1-2': Module.C112
  };

</script>

▼ 原本的 <component> 是用 Mapping 映射表判斷,現在改成用 Modules 判斷

<template>
  <div>
    <component :is="Modules[moduleId]" />
  </div>
</template>

動態元件 <component> 需要加入 v-if 判斷條件嗎

在這個範例中,我的 Modules[moduleId] 如果找不到對應的元件,Vue 會自動渲染為空,不會造成錯誤。此外,我也確定 moduleId 一定是個有效值,也就是在 Modules 中一定有對應的元件,所以不一定要加上 v-if 檢查

▼ 但如果習慣使用 v-if 謹慎處理,一樣可以加入 v-if 做二次判斷

<template>
  <div>
    <component 
      v-if="Modules[moduleId]" 
      :is="Modules[child.moduleId]" 
    />
  </div>
</template>

好處是避免在 moduleId 無效時出現 Vue 的警告訊息,且程式碼更加有防禦性,能對錯誤情況有明確的處理。


至此,將模組和元件都動態處理後,就不用再寫繁瑣的程式碼做各種判斷了。

完整的程式碼

最後附上完整的程式碼供大家參考

▼ 使用 import.meta.glob 動態管理模組

interface Modules {
  [key: string]: { default: unknown }
}
const modules: Modules = import.meta.glob('./*.vue', { eager: true })
export default Object.keys(modules).reduce((acc: Record<string, unknown>, path: string) => {
  // 視情況處理匯出的 moduleName
  const moduleName = path.replace('./', '').replace('.vue', '').toLowerCase()
  acc[moduleName] = modules[path].default
  return acc
}, {})

▼ 使用 <component> 動態渲染元件

<template>
  <div>
    <component 
      v-if="Modules[moduleId]" 
      :is="Modules[child.moduleId]" 
    />
  </div>
</template>
<script lang="ts" setup>
  import Modules from '@/components/home/module'
</script>

小結

透過這種方式,可以解決手動撰寫重複程式碼的問題,讓元件渲染邏輯變得更加靈活且可維護,且程式碼也變得非常簡潔乾淨。未來若需要新增或修改元件,只需將新檔案放入目錄,無需改動現有邏輯,敲~級方便!。

這種動態渲染的方式,尤其適用於需要處理大量條件邏輯的場景,可以讓開發更高效,維護更輕鬆!

以上有任何問題歡迎留言給我,如果你覺得這篇文章對你有幫助,也別忘了分享給你的朋友唷 😉

歡迎給我點鼓勵,讓我知道你來過 :)

2
MUKI says:

如果文章有幫助到你,歡迎分享給更多人知道。文章內容皆為 MUKI 本人的原創文章,我會經常更新文章以及修正錯誤內容,因此轉載時建議保留原出處,避免出現版本不一致或已過時的資訊。

本文地址:https://muki.tw/dynamic-rendering-vue-components-and-dynamic-module-import/ 已複製

Subscribe
Notify of
guest

0 則留言
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Copyright © since 2008 MUKI space* / omegaSS theme All Rights Reserved.