用Vue3.0 寫過組件嗎?如果想實現一個 Modal你會怎麼設計?

来源:https://www.cnblogs.com/smileZAZ/p/18061459
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、組件設計 組件就是把圖形、非圖形的各種邏輯均抽象為一個統一的概念(組件)來實現開發的模式 現在有一個場景,點擊新增與編輯都彈框出來進行填寫,功能上大同小異,可能只是標題內容或者是顯示的主體內容稍微不同 這時候就沒必要寫兩個組件,只需要 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

一、組件設計

組件就是把圖形、非圖形的各種邏輯均抽象為一個統一的概念(組件)來實現開發的模式

現在有一個場景,點擊新增與編輯都彈框出來進行填寫,功能上大同小異,可能只是標題內容或者是顯示的主體內容稍微不同

這時候就沒必要寫兩個組件,只需要根據傳入的參數不同,組件顯示不同內容即可

這樣,下次開發相同界面程式時就可以寫更少的代碼,意義著更高的開發效率,更少的 Bug和更少的程式體積

二、需求分析

實現一個Modal組件,首先確定需要完成的內容:

  • 遮罩層

  • 標題內容

  • 主體內容

  • 確定和取消按鈕

主體內容需要靈活,所以可以是字元串,也可以是一段 html 代碼

特點是它們在當前vue實例之外獨立存在,通常掛載於body之上

除了通過引入import的形式,我們還可通過API的形式進行組件的調用

還可以包括配置全局樣式、國際化、與typeScript結合

三、實現流程

首先看看大致流程:

  • 目錄結構

  • 組件內容

  • 實現 API 形式

  • 事件處理

  • 其他完善

目錄結構

Modal組件相關的目錄結構

├── plugins
│   └── modal
│       ├── Content.tsx // 維護 Modal 的內容,用於 h 函數和 jsx 語法
│       ├── Modal.vue // 基礎組件
│       ├── config.ts // 全局預設配置
│       ├── index.ts // 入口
│       ├── locale // 國際化相關
│       │   ├── index.ts
│       │   └── lang
│       │       ├── en-US.ts
│       │       ├── zh-CN.ts
│       │       └── zh-TW.ts
│       └── modal.type.ts // ts類型聲明相關

因為 Modal 會被 app.use(Modal) 調用作為一個插件,所以都放在plugins目錄下

組件內容

首先實現modal.vue的主體顯示內容大致如下

<Teleport to="body" :disabled="!isTeleport">
    <div v-if="modelValue" class="modal">
        <div
             class="mask"
             :style="style"
             @click="maskClose && !loading && handleCancel()"
             ></div>
        <div class="modal__main">
            <div class="modal__title line line--b">
                <span>{{ title || t("r.title") }}</span>
                <span
                      v-if="close"
                      :title="t('r.close')"
                      class="close"
                      @click="!loading && handleCancel()"
                      >✕</span
                    >
            </div>
            <div class="modal__content">
                <Content v-if="typeof content === 'function'" :render="content" />
                <slot v-else>
                    {{ content }}
                </slot>
            </div>
            <div class="modal__btns line line--t">
                <button :disabled="loading" @click="handleConfirm">
                    <span class="loading" v-if="loading"> ❍ </span>{{ t("r.confirm") }}
                </button>
                <button @click="!loading && handleCancel()">
                    {{ t("r.cancel") }}
                </button>
            </div>
        </div>
    </div>
</Teleport>

最外層上通過Vue3 Teleport 內置組件進行包裹,其相當於傳送門,將裡面的內容傳送至body之上

並且從DOM結構上來看,把modal該有的內容(遮罩層、標題、內容、底部按鈕)都實現了

關於主體內容

<div class="modal__content">
    <Content v-if="typeof content==='function'"
             :render="content" />
    <slot v-else>
        {{content}}
    </slot>
</div>

可以看到根據傳入content的類型不同,對應顯示不同得到內容

最常見的則是通過調用字元串和預設插槽的形式

// 預設插槽
<Modal v-model="show"
       title="演示 slot">
    <div>hello world~</div>
</Modal>

// 字元串
<Modal v-model="show"
       title="演示 content"
       content="hello world~" />

通過 API 形式調用Modal組件的時候,content可以使用下麵兩種

  • h 函數
$modal.show({
  title: '演示 h 函數',
  content(h) {
    return h(
      'div',
      {
        style: 'color:red;',
        onClick: ($event: Event) => console.log('clicked', $event.target)
      },
      'hello world ~'
    );
  }
});
  • JSX
$modal.show({
  title: '演示 jsx 語法',
  content() {
    return (
      <div
        onClick={($event: Event) => console.log('clicked', $event.target)}
      >
        hello world ~
      </div>
    );
  }
});

實現 API 形式

那麼組件如何實現API形式調用Modal組件呢?

Vue2中,我們可以藉助Vue實例以及Vue.extend的方式獲得組件實例,然後掛載到body

import Modal from './Modal.vue';
const ComponentClass = Vue.extend(Modal);
const instance = new ComponentClass({ el: document.createElement("div") });
document.body.appendChild(instance.$el);

雖然Vue3移除了Vue.extend方法,但可以通過createVNode實現

import Modal from './Modal.vue';
const container = document.createElement('div');
const vnode = createVNode(Modal);
render(vnode, container);
const instance = vnode.component;
document.body.appendChild(container);

Vue2中,可以通過this的形式調用全局 API

export default {
    install(vue) {
       vue.prototype.$create = create
    }
}

而在 Vue3 的 setup 中已經沒有 this概念了,需要調用app.config.globalProperties掛載到全局

export default {
    install(app) {
        app.config.globalProperties.$create = create
    }
}

事件處理

下麵再看看看Modal組件內部是如何處理「確定」「取消」事件的,既然是Vue3,當然採用Compositon API 形式

// Modal.vue
setup(props, ctx) {
  let instance = getCurrentInstance(); // 獲得當前組件實例
  onBeforeMount(() => {
    instance._hub = {
      'on-cancel': () => {},
      'on-confirm': () => {}
    };
  });

  const handleConfirm = () => {
    ctx.emit('on-confirm');
    instance._hub['on-confirm']();
  };
  const handleCancel = () => {
    ctx.emit('on-cancel');
    ctx.emit('update:modelValue', false);
    instance._hub['on-cancel']();
  };

  return {
    handleConfirm,
    handleCancel
  };
}

在上面代碼中,可以看得到除了使用傳統emit的形式使父組件監聽,還可通過_hub屬性中添加 on-cancelon-confirm方法實現在API中進行監聽

app.config.globalProperties.$modal = {
   show({}) {
     /* 監聽 確定、取消 事件 */
   }
}

下麵再來目睹下_hub是如何實現

// index.ts
app.config.globalProperties.$modal = {
    show({
        /* 其他選項 */
        onConfirm,
        onCancel
    }) {
        /* ... */

        const { props, _hub } = instance;

        const _closeModal = () => {
            props.modelValue = false;
            container.parentNode!.removeChild(container);
        };
        // 往 _hub 新增事件的具體實現
        Object.assign(_hub, {
            async 'on-confirm'() {
            if (onConfirm) {
                const fn = onConfirm();
                // 當方法返回為 Promise
                if (fn && fn.then) {
                    try {
                        props.loading = true;
                        await fn;
                        props.loading = false;
                        _closeModal();
                    } catch (err) {
                        // 發生錯誤時,不關閉彈框
                        console.error(err);
                        props.loading = false;
                    }
                } else {
                    _closeModal();
                }
            } else {
                _closeModal();
            }
        },
            'on-cancel'() {
                onCancel && onCancel();
                _closeModal();
            }
    });
}
};

其他完善

關於組件實現國際化、與typsScript結合,大家可以根據自身情況在此基礎上進行更改

參考文獻

  • https://segmentfault.com/a/1190000038928664

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在 2023 年的年底,我用 Vite-SSG + Vue3 + Vuetify3 把之前使用 SPA 編寫的官網進行了重構,支持多語言,響應式並且對 SEO 和社交媒體分享十分友好 ...
  • Vue.js 是一個漸進式 JavaScript 框架,適用於構建用戶界面,特別適合開發企業級 Web 應用程式。它具有漸進式設計、虛擬 DOM、響應式數據綁定和組件化等優勢。ViewDesign(原名 iView)是一個基於 Vue.js 的企業級 UI 組件庫,提供全面的組件庫、企業級設計規範、... ...
  • 本文探討了前端發展的新趨勢,以及 Vue.js 生態中 ViewDesign 這一優秀 UI 組件庫的崛起。首先闡述了現代 Web 應用對前端開發提出的新需求,以及 Vue.js 作為漸進式框架在滿足這些需求方面的優勢。接著重點介紹了 ViewDesign 作為基於 Vue.js 的 UI 組件庫的... ...
  • 開源項目地址:https://gitee.com/easyxaf/jsplumb-navigator 前言 jsPlumb可用於連接DOM元素,它不依賴框架,所以與主流框架都可以無縫的集成。但比較遺憾的是社區版中沒有平移、縮放等功能,如果用它來開發工作流等項目,用戶體驗會大打折扣。我的項目是用Bla ...
  • 大家好,我是 Java陳序員。 由於為了生活奔波,常年在外,導致很多關係稍疏遠的親戚之間來往並不多。 因此節假日回家時,往往會搞不清楚哪位親戚應該喊什麼稱呼,很容易“社死”。 今天給大家介紹一個親戚關係計算器,讓你快速的計算出正確的親戚稱謂! 關註微信公眾號:【Java陳序員】,獲取開源項目分享、A ...
  • 寫在前面 情人節已經接近尾聲了,雖然跟我沒什麼關係,但是我還是很渴望,能遇到一個良人相伴一生。 現在時間: 內心異常平靜,相對吵鬧我更喜歡安靜的晚上,沒人打擾,enjoy自己獨處的時間! 保存內容顯示 1、任務拆解 讀取已保存內容 將讀取內容在富文本里顯示 2、讀取已保存內容 這個很好理解,就是增加 ...
  • Vue進階 一、vue實例 1.一個基本的vue的實例 <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <h1> {{title}} </h1> <button id="btn" @click=" ...
  • 前言 Composables 稱之為可組合項,熟悉 react 的同學喜歡稱之為 hooks ,由於可組合項的存在,Vue3 中的組件之間共用狀態比以往任何時候都更容易。這種新範例引入了一種更有組織性和可擴展性的方式來管理整個應用程式的狀態和邏輯。 什麼是Composables 本質上,可組合項是一 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...