手把手教你以最簡單的方式封裝 Vue3 模態(Modal) 類元件

語言: CN / TW / HK

theme: nico

反饋型元件,是前端經常涉及的元件,它們允許向用戶提供提醒、提供更多選項或新增額外資訊,而不會弄亂主要內容。

在 Vue 中 Modal 類元件的實現通常分為兩種模式:

  • 元件宣告模式(declarative

```html

```

優點:定製化程度高、限制低,可以最大程度的發揮主觀設計想法

缺點:複用性差、使用頻繁時需要定義多份 { show, onOk, onClose },程式碼冗餘。

  • 函式呼叫模式(imperative

js Modal.confirm({ title: '...', contnet: '...' }) .then(() => {/* ok */}) .catch(() => { /* close */})

優點:資料獨立與頁面獨立、複用性、通用性強、業務程式碼流程易懂、無法繼承上下文

缺點:涉及到定製化將很難處理,往往需要編寫 template 字串 / jsx 程式碼

彈出層元件涉及思路

在前端發展至今為止,這類元件的封裝已經有了廣泛的累積。

其中函式呼叫(imperative)無非就是利用了 vuerender 能力,將一個元件直接渲染至 html 當中,

但實際實現還是相當麻煩,需要處理 rendervanish 的時機,還得想辦法將處理函式傳遞給元件自身,且無法和元件宣告模式相結合使用。

並且考慮在實際專案開發當中,我們可能並不會考慮重頭實現一個 dialog 元件,而是使用第三方元件庫自帶的去更改,久而久之就會產生大量的宣告式元件(declarative)導致多個頁面的邏輯冗餘。

綜上考慮成本還是過高,但並沒有“萬策盡きた”,我們先明確以下幾點要求:

  • 元件可支援元件宣告、函式呼叫兩種模式(declarative and imperative
  • 定製化能力強、開發成本低,以最簡單的方式製作模態類元件
  • 使用現有元件庫(如 element-plus)整合和定製化功能
  • 支援元件繼承全域性應用上下文
  • 支援 Typescript,型別健全完整

筆者經過長時間的積累,將這套邏輯抽離了出來,形成了一套相對完整的 Vue3 彈出層解決方案 unoverlay-vue,它的體積很小,並且可以滿足我們上述的所有要求。

關於實現基層原理方面,本文不作過多展示,可以檢視筆者以往文章 Vue3 模態(Modal) 模態框封裝方案unoverlay-vue 原始碼

⚙️ Install

```sh pnpm add unoverlay-vue

Or Yarn

yarn add unoverlay-vue ```

在 main.js 中全域性安裝可以使所有彈出層繼承上下文

```ts // main.js import { createApp } from 'vue' import unoverlay from 'unoverlay-vue' import App from './App.vue'

const app = createApp(App) app.use(unoverlay) app.mount('#app') ```

實現基礎的 Modal 功能

定義 Model 元件,這裡以最簡案例實現,不包含動畫邏輯(可以使用 <Transition> 實現)

你可以大膽地發揮你的想象力

```html

```

建立函式呼叫回撥(imperative), 在 Javascript / Typescript 中呼叫

```ts import { transformOverlay } from 'unoverlay-vue' import Model from './Model.vue'

// 轉換函式呼叫模式(imperative) const ModelCallbck = transformOverlay(Model) // 呼叫並獲取 confirm 回撥的值 const value = await ModelCallbck({ title: 'callbackOverlay' }) // value === "callbackOverlay:confirmed" ```

或者任意地方直接調起元件

```ts import { useOverlayCall } from 'unoverlay-vue' import Model from './Model.vue'

const value = await useOverlayCall(Model, { props: { title: 'useOverlay' } }) // value === "useOverlay:confirmed" ```

或者在 template 中以元件宣告模式(declarative)使用

```html