封裝一個更易用的Dialog組件過程詳解
場景
在項目中,我們經(jīng)常會遇到使用彈窗的場景,但有時組件庫自帶的彈窗不能滿足我們的需求,需要我們自己封裝,這時我們?nèi)绾稳プ远x一個更加方便調(diào)用的彈窗?
搭建環(huán)境
首先我們需要搭建一個Vue3+ts的環(huán)境。
用vite的官方模板:
yarn create vite demo-app --template vue-ts
進入并安裝依賴
cd demo-app yarn
依賴安裝完成后啟動app
yarn dev
創(chuàng)建組件
先在src/components目錄下創(chuàng)建MyDialog.vue,搭建一個組件的基本框架
<script lang="ts" setup> import { ref, reactive } from "vue"; defineProps({ message: { type: String, default: "", }, title: { type: String, default: "", }, }); const emits = defineEmits<{ (e: "confirm"): void; (e: "close"): void; }>(); const visible = ref(true); function clickConfirm() { console.log("確認"); emits("confirm"); } function clickClose() { console.log("取消"); emits("close"); } </script> <template> <div class="wrap" v-if="visible"> <div class="container"> <div class="title">{{ title }}</div> <div class="content"> <div>{{ message }}</div> </div> <div class="controll"> <button @click="clickConfirm">確認</button> <button @click="clickClose">取消</button> </div> </div> </div> </template> <style scoped> .wrap { position: absolute; top: 0; left: 0; background: rgba(15, 15, 15, 0.5); width: 100%; height: 100%; } .container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); min-width: 300px; min-height: 200px; padding: 10px; background: white; display: flex; flex-direction: column; } .content { flex: 1; padding: 10px; text-align: left; } .title { min-height: 30px; } .controll { display: flex; width: 100%; justify-content: space-around; } </style>
創(chuàng)建調(diào)用組件的hook函數(shù)
在src目錄下創(chuàng)建hooks目錄,然后再hooks目錄下創(chuàng)建useMyDialog.ts.
函數(shù)調(diào)用組件我們需要:
- 將組件轉(zhuǎn)換成VNode
- 將VNode轉(zhuǎn)換成DOM然后渲染到頁面
import { createVNode, render, ComponentPublicInstance } from "vue"; export default function useMyDialog(option?: any) { const props = { ...option, }; const vm = createVNode(MyDialog, props); const container = document.createElement("div"); render(vm, container); document.querySelector("#app")?.appendChild(container.firstElementChild!); }
ps:
container.firstElementChild!中的!表示container.firstElementChild不為null或者undefined
接下來我們在App.vue中測試一下
<script setup lang="ts"> import useMyDialog from "./hooks/useMyDialog"; function showDialog() { useMyDialog({ message: "test1", onClose: () => { console.log("self"); }, }); } </script> <template> <button @click="showDialog">顯示Dialog</button> </template>
Dialog的緩存、隱藏
隱藏
我們需要將close返回出去,這樣我們就可以手動調(diào)用close函數(shù)關(guān)閉Dialog.
在useMyDialog.ts中添加
import { ComponentPublicInstance,VNode } from "vue"; export default function useMyDialog(option?: any) { const userCloseFn = option?.onClose; props.onClose = () => { close(); userCloseFn ?? userCloseFn(); }; function close(vm: VNode) { ( vm.component!.proxy as ComponentPublicInstance<{ visible: boolean }> ).visible = false; } return { close: close.bind(null, vm), } }
緩存
現(xiàn)在每次點擊顯示Dialog按鈕時都會創(chuàng)建一個新的組件實例,這不是我們的預(yù)期,所以我們需要將組件進行緩存.
在useMyDialog.ts中添加
import { ComponentPublicInstance } from 'vue' const instances: any[] = []; export default function useMyDialog(option?: any) { const tempVm: any = instances.find( (item) => `${item.vm.props?.message ?? ""}` === `${(option as any).message ?? ""}` ); if (tempVm) { ( tempVm.vm.component!.proxy as ComponentPublicInstance<{ visible: boolean; }> ).visible = true; return { close: close.bind(null, tempVm.vm), }; } }
完整代碼
src/hooks/useMyDialog.ts
import { createVNode, render, ComponentPublicInstance, VNode } from "vue"; import MyDialog from "../components/MyDialog.vue"; const instances: any[] = []; export default function useMyDialog(option?: any) { const props = { ...option, }; const userCloseFn = option?.onClose; props.onClose = () => { close(vm); userCloseFn ?? userCloseFn(); }; function close(vm: VNode) { ( vm.component!.proxy as ComponentPublicInstance<{ visible: boolean }> ).visible = false; } const tempVm: any = instances.find( (item) => `${item.vm.props?.message ?? ""}` === `${(option as any).message ?? ""}` ); if (tempVm) { ( tempVm.vm.component!.proxy as ComponentPublicInstance<{ visible: boolean; }> ).visible = true; return { close: close.bind(null, tempVm.vm), }; } const vm = createVNode(MyDialog, props); const container = document.createElement("div"); render(vm, container); document.querySelector("#app")?.appendChild(container.firstElementChild!); instances.push({ vm }); return { close: close.bind(null, vm), }; }
總結(jié)
這種調(diào)用方式不局限于Dialog組件,其他有需要的業(yè)務(wù)組件也可以通過這種封裝方式去簡化調(diào)用.
以上代碼其實是element-plus的message組件的簡化版,有興趣的可以去看看element-plus的源碼,鏈接貼在下方.
以上就是封裝一個更易用的Dialog組件過程詳解的詳細內(nèi)容,更多關(guān)于Dialog組件封裝的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue組件教程之Toast(Vue.extend 方式)詳解
這篇文章主要給大家介紹了關(guān)于Vue組件教程之Toast(Vue.extend 方式)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-01-01vuex中store存儲store.commit和store.dispatch的用法
這篇文章主要介紹了vuex中store存儲store.commit和store.dispatch的用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07Vue中使用create-keyframe-animation與動畫鉤子完成復(fù)雜動畫
這篇文章主要介紹了Vue中使用create-keyframe-animation與動畫鉤子完成復(fù)雜動畫,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04