封裝一個更易用的Dialog組件過程詳解
場景
在項目中,我們經常會遇到使用彈窗的場景,但有時組件庫自帶的彈窗不能滿足我們的需求,需要我們自己封裝,這時我們如何去自定義一個更加方便調用的彈窗?
搭建環境
首先我們需要搭建一個Vue3+ts的環境。
用vite的官方模板:
yarn create vite demo-app --template vue-ts
進入並安裝依賴
cd demo-app yarn
依賴安裝完成後啟動app
yarn dev
創建組件
先在src/components目錄下創建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>
創建調用組件的hook函數
在src目錄下創建hooks目錄,然後再hooks目錄下創建useMyDialog.ts.
函數調用組件我們需要:
- 將組件轉換成VNode
- 將VNode轉換成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返回出去,這樣我們就可以手動調用close函數關閉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), } }
緩存
現在每次點擊顯示Dialog按鈕時都會創建一個新的組件實例,這不是我們的預期,所以我們需要將組件進行緩存.
在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), }; }
總結
這種調用方式不局限於Dialog組件,其他有需要的業務組件也可以通過這種封裝方式去簡化調用.
以上代碼其實是element-plus的message組件的簡化版,有興趣的可以去看看element-plus的源碼,鏈接貼在下方.
element-plus源碼
以上就是封裝一個更易用的Dialog組件過程詳解的詳細內容,更多關於Dialog組件封裝的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Vue3內置組件Teleport使用方法詳解
- Vue實現Dialog封裝
- Android自定義Dialog的方法實例
- vue+el-element中根據文件名動態創建dialog的方法實踐
- vue實現自定義組件掛載原型上