溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么使用Vue3設(shè)計(jì)實(shí)現(xiàn)一個(gè)Model組件

發(fā)布時(shí)間:2022-08-08 15:46:47 來(lái)源:億速云 閱讀:134 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容主要講解“怎么使用Vue3設(shè)計(jì)實(shí)現(xiàn)一個(gè)Model組件”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么使用Vue3設(shè)計(jì)實(shí)現(xiàn)一個(gè)Model組件”吧!

    一、組件設(shè)計(jì)

    組件就是把圖形、非圖形的各種邏輯均抽象為一個(gè)統(tǒng)一的概念(組件)來(lái)實(shí)現(xiàn)開(kāi)發(fā)的模式

    現(xiàn)在有一個(gè)場(chǎng)景,點(diǎn)擊新增與編輯都彈框出來(lái)進(jìn)行填寫(xiě),功能上大同小異,可能只是標(biāo)題內(nèi)容或者是顯示的主體內(nèi)容稍微不同

    這時(shí)候就沒(méi)必要寫(xiě)兩個(gè)組件,只需要根據(jù)傳入的參數(shù)不同,組件顯示不同內(nèi)容即可

    這樣,下次開(kāi)發(fā)相同界面程序時(shí)就可以寫(xiě)更少的代碼,意義著更高的開(kāi)發(fā)效率,更少的 Bug和更少的程序體積

    二、需求分析

    實(shí)現(xiàn)一個(gè)Modal組件,首先確定需要完成的內(nèi)容:

    • 遮罩層

    • 標(biāo)題內(nèi)容

    • 主體內(nèi)容

    • 確定和取消按鈕

    主體內(nèi)容需要靈活,所以可以是字符串,也可以是一段 html 代碼

    特點(diǎn)是它們?cè)诋?dāng)前vue實(shí)例之外獨(dú)立存在,通常掛載于body之上

    除了通過(guò)引入import的形式,我們還可通過(guò)API的形式進(jìn)行組件的調(diào)用

    還可以包括配置全局樣式、國(guó)際化、與typeScript結(jié)合

    三、實(shí)現(xiàn)流程

    首先看看大致流程:

    • 目錄結(jié)構(gòu)

    • 組件內(nèi)容

    • 實(shí)現(xiàn) API 形式

    • 事件處理

    • 其他完善

    目錄結(jié)構(gòu)

    Modal組件相關(guān)的目錄結(jié)構(gòu)

    ├── plugins
    │   └── modal
    │       ├── Content.tsx // 維護(hù) Modal 的內(nèi)容,用于 h 函數(shù)和 jsx 語(yǔ)法
    │       ├── Modal.vue // 基礎(chǔ)組件
    │       ├── config.ts // 全局默認(rèn)配置
    │       ├── index.ts // 入口
    │       ├── locale // 國(guó)際化相關(guān)
    │       │   ├── index.ts
    │       │   └── lang
    │       │       ├── en-US.ts
    │       │       ├── zh-CN.ts
    │       │       └── zh-TW.ts
    │       └── modal.type.ts // ts類(lèi)型聲明相關(guān)

    因?yàn)?Modal 會(huì)被 app.use(Modal) 調(diào)用作為一個(gè)插件,所以都放在plugins目錄下

    組件內(nèi)容

    首先實(shí)現(xiàn)modal.vue的主體顯示內(nèi)容大致如下

    <Teleport to="body" :disabled="!isTeleport">
        <div v-if="modelValue" class="modal">
            <div
                 class="mask"
                 :
                 @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>

    最外層上通過(guò)Vue3 Teleport 內(nèi)置組件進(jìn)行包裹,其相當(dāng)于傳送門(mén),將里面的內(nèi)容傳送至body之上

    并且從DOM結(jié)構(gòu)上來(lái)看,把modal該有的內(nèi)容(遮罩層、標(biāo)題、內(nèi)容、底部按鈕)都實(shí)現(xiàn)了

    關(guān)于主體內(nèi)容

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

    可以看到根據(jù)傳入content的類(lèi)型不同,對(duì)應(yīng)顯示不同得到內(nèi)容

    最常見(jiàn)的則是通過(guò)調(diào)用字符串和默認(rèn)插槽的形式

    // 默認(rèn)插槽
    <Modal v-model="show"
           title="演示 slot">
        <div>hello world~</div>
    </Modal>
    
    // 字符串
    <Modal v-model="show"
           title="演示 content"
           content="hello world~" />

    通過(guò) API 形式調(diào)用Modal組件的時(shí)候,content可以使用下面兩種

    • h 函數(shù)

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

    $modal.show({
      title: '演示 jsx 語(yǔ)法',
      content() {
        return (
          <div
            onClick={($event: Event) => console.log('clicked', $event.target)}
          >
            hello world ~
          </div>
        );
      }
    });

    實(shí)現(xiàn) API 形式

    那么組件如何實(shí)現(xiàn)API形式調(diào)用Modal組件呢?

    在Vue2中,我們可以借助Vue實(shí)例以及Vue.extend的方式獲得組件實(shí)例,然后掛載到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方法,但可以通過(guò)createVNode實(shí)現(xiàn)

    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中,可以通過(guò)this的形式調(diào)用全局 API

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

    而在 Vue3 的 setup 中已經(jīng)沒(méi)有 this概念了,需要調(diào)用app.config.globalProperties掛載到全局

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

    事件處理

    下面再看看看Modal組件內(nèi)部是如何處理「確定」「取消」事件的,既然是Vue3,當(dāng)然采用Compositon API 形式

    // Modal.vue
    setup(props, ctx) {
      let instance = getCurrentInstance(); // 獲得當(dāng)前組件實(shí)例
      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
      };
    }

    在上面代碼中,可以看得到除了使用傳統(tǒng)emit的形式使父組件監(jiān)聽(tīng),還可通過(guò)_hub屬性中添加 on-cancel,on-confirm方法實(shí)現(xiàn)在API中進(jìn)行監(jiān)聽(tīng)

    app.config.globalProperties.$modal = {
       show({}) {
         /* 監(jiān)聽(tīng) 確定、取消 事件 */
       }
    }

    下面再來(lái)目睹下_hub是如何實(shí)現(xiàn)

    // index.ts
    app.config.globalProperties.$modal = {
        show({
            /* 其他選項(xiàng) */
            onConfirm,
            onCancel
        }) {
            /* ... */
    
            const { props, _hub } = instance;
    
            const _closeModal = () => {
                props.modelValue = false;
                container.parentNode!.removeChild(container);
            };
            // 往 _hub 新增事件的具體實(shí)現(xiàn)
            Object.assign(_hub, {
                async 'on-confirm'() {
                if (onConfirm) {
                    const fn = onConfirm();
                    // 當(dāng)方法返回為 Promise
                    if (fn && fn.then) {
                        try {
                            props.loading = true;
                            await fn;
                            props.loading = false;
                            _closeModal();
                        } catch (err) {
                            // 發(fā)生錯(cuò)誤時(shí),不關(guān)閉彈框
                            console.error(err);
                            props.loading = false;
                        }
                    } else {
                        _closeModal();
                    }
                } else {
                    _closeModal();
                }
            },
                'on-cancel'() {
                    onCancel && onCancel();
                    _closeModal();
                }
        });
    }
    };

    到此,相信大家對(duì)“怎么使用Vue3設(shè)計(jì)實(shí)現(xiàn)一個(gè)Model組件”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI