從0到1解鎖Element-Plus組件二次封裝El-Dialog動態(tài)調(diào)用的原理解析
技術(shù)難題初登場
家人們,最近在開發(fā)一個超復(fù)雜的后臺管理系統(tǒng)項目,里面有各種數(shù)據(jù)展示、表單提交、權(quán)限控制等功能,在這個過程中,我頻繁地使用到了element-plus
組件庫中的el-dialog
組件 。它就像一個小彈窗,可以用來顯示各種提示信息、編輯表單之類的。比如說在用戶點擊 “編輯” 按鈕時,就彈出一個el-dialog
,里面放著編輯表單,讓用戶修改數(shù)據(jù)。
但隨著功能的不斷完善,我發(fā)現(xiàn)原生的el-dialog
組件在某些場景下真的不夠靈活。比如說,我想在不同的組件中根據(jù)不同的業(yè)務(wù)邏輯動態(tài)地控制對話框的顯示和隱藏,還要傳遞不同的數(shù)據(jù)給對話框,原生組件用起來就很麻煩,每次都要寫很多重復(fù)的代碼,這可太影響開發(fā)效率了。所以,我就決定對el-dialog
進行二次封裝,實現(xiàn)動態(tài)調(diào)用,讓它更好地滿足我的項目需求。接下來,就把我的經(jīng)驗分享給大家,一起看看怎么解決這個問題。
實現(xiàn)效果
代碼實現(xiàn)
import { ElButton, ElDialog } from "element-plus"; import { createApp, h } from "vue"; // 創(chuàng)建一個 DialogManager 類來管理對話框 class DialogManager { constructor() { this.dialogs = []; } /** * 創(chuàng)建并顯示一個動態(tài)對話框 * @param {Object} options - 對話框配置選項 * @returns {Object} - 返回對話框?qū)嵗? */ create(options = {}) { // 合并默認(rèn)配置 const defaultOptions = { title: "提示", visible: true, fullscreen: false, top: "15vh", modal: true, lockScroll: true, closeOnClickModal: true, closeOnPressEscape: true, beforeClose: null, footerBtns: [ { label: "取消", type: "default", handler: (instance) => { instance.close(); }, }, { label: "確定", type: "primary", handler: (instance) => { instance.close(); }, }, ], }; const dialogOptions = { ...defaultOptions, ...options }; // 創(chuàng)建一個容器元素 const container = document.createElement("div"); document.body.appendChild(container); // 創(chuàng)建 Dialog 組件實例 const app = createApp({ data() { return { dialogVisible: dialogOptions.visible, }; }, provide() { return { manager: this.$options.manager, // 提供manager }; }, inject: ["manager"], // 注入manager render() { const footerNodes = dialogOptions.footerBtns.map((btn, index) => { return h( ElButton, { key: index, type: btn.type || "default", size: "small", onClick: () => { if (btn.handler) { btn.handler(this); } }, }, () => btn.label ); }); // 改進 content 渲染方式 const renderContent = () => { if (typeof dialogOptions.content === "string") { return h("div", { class: "dialog-content" }, dialogOptions.content); } else if (dialogOptions.content) { // 只傳遞必要的屬性和方法 const props = { // 提供關(guān)閉對話框的回調(diào) closeDialog: () => { this.close(); }, // 可以添加其他必要的屬性 }; // 如果有額外的 props,合并它們 if (dialogOptions.contentProps) { Object.assign(props, dialogOptions.contentProps); } return h(dialogOptions.content, props); } else { return h("div", { class: "dialog-content" }, "No content provided"); } }; return h( ElDialog, { title: dialogOptions.title, modelValue: this.dialogVisible, "onUpdate:modelValue": (val) => { this.dialogVisible = val; }, fullscreen: dialogOptions.fullscreen, top: dialogOptions.top, modal: dialogOptions.modal, lockScroll: dialogOptions.lockScroll, closeOnClickModal: dialogOptions.closeOnClickModal, closeOnPressEscape: dialogOptions.closeOnPressEscape, beforeClose: dialogOptions.beforeClose, onClose: () => { this.dialogVisible = false; if (dialogOptions.onClose) { dialogOptions.onClose(this); } // 延遲銷毀,讓關(guān)閉動畫完成 setTimeout(() => { this.destroy(); }, 300); }, }, { // 默認(rèn)插槽用于對話框內(nèi)容 default: renderContent, footer: () => h( "div", { class: "dialog-footer", }, footerNodes ), } ); }, methods: { // 關(guān)閉對話框 close() { this.dialogVisible = false; }, // 銷毀對話框?qū)嵗? destroy() { app.unmount(); if (container.parentNode) { container.parentNode.removeChild(container); } // 從管理列表中移除 if (this.manager) { const index = this.manager.dialogs.indexOf(this); if (index !== -1) { this.manager.dialogs.splice(index, 1); } } }, }, mounted() { // 添加到管理列表 if (this.manager) { this.manager.dialogs.push(this); } else { console.error("Manager is not initialized"); } }, }).provide("manager", this); // 全局提供manager // 掛載應(yīng)用 const instance = app.mount(container); instance.manager = this; // 直接在實例上設(shè)置manager return instance; } /** * 關(guān)閉所有對話框 */ closeAll() { this.dialogs.forEach((dialog) => { dialog.close(); }); } } // 創(chuàng)建單例實例 const dialogManager = new DialogManager(); // 導(dǎo)出創(chuàng)建對話框的函數(shù) export function createDialog(options) { return dialogManager.create(options); } // 導(dǎo)出關(guān)閉所有對話框的函數(shù) export function closeAllDialogs() { dialogManager.closeAll(); }
調(diào)用示例
-------------------------------- 基本文本對話框 ------------------------------------- import { createDialog } from '@/utils/dialog-manager'; export default { methods: { showSimpleDialog() { createDialog({ title: '確認(rèn)操作', content: '你確定要執(zhí)行這個操作嗎?', onClose: () => { console.log('對話框已關(guān)閉'); }, footerBtns: [ { label: '取消', handler: (instance) => { console.log('點擊了取消'); instance.close(); }, }, { label: '確認(rèn)', type: 'primary', handler: (instance) => { console.log('執(zhí)行確認(rèn)操作'); instance.close(); }, }, ], }); }, }, }; -------------------------- 包含組件的對話框 -------------------------------- import { createDialog } from '@/utils/dialog-manager'; import MyComponent from '@/components/MyComponent.vue'; export default { methods: { showComponentDialog() { createDialog({ title: '自定義組件對話框', content: MyComponent, contentProps: { // 這里可以傳入組件的props message: '這是來自父組件的消息', src: 'xxx', }, width: '600px', footerBtns: [ { label: '關(guān)閉', handler: (instance) => { instance.close(); }, }, ], }); }, }, }; -------------------------- 異步操作對話框 -------------------------------- import { createDialog } from '@/utils/dialog-manager'; export default { methods: { async showAsyncDialog() { const dialog = createDialog({ title: '正在加載...', content: '數(shù)據(jù)加載中,請稍候...', showClose: false, // 隱藏關(guān)閉按鈕 footerBtns: [], // 不顯示底部按鈕 }); try { // 模擬異步操作 const result = await this.fetchData(); dialog.close(); // 顯示成功消息 createDialog({ title: '操作成功', content: '數(shù)據(jù)加載完成', footerBtns: [ { label: '確定', type: 'primary', handler: (instance) => instance.close(), }, ], }); } catch (error) { dialog.close(); // 顯示錯誤消息 createDialog({ title: '操作失敗', content: `錯誤: ${error.message}`, footerBtns: [ { label: '關(guān)閉', handler: (instance) => instance.close(), }, ], }); } }, }, };
原理剖析
這里面的原理其實也不難理解,主要是利用了Vue
的響應(yīng)式原理 。我們都知道,在Vue
中,數(shù)據(jù)發(fā)生變化時,視圖會自動更新。我們封裝的組件就是基于這個特性,通過一個響應(yīng)式的變量來控制el-dialog
的顯示與隱藏。比如說,我們定義一個isDialogVisible
變量,當(dāng)它為true
時,el-dialog
就顯示,為false
時就隱藏 。
而動態(tài)傳遞參數(shù)呢,則是通過props
來實現(xiàn)的。我們在封裝的組件中定義好props
,然后在調(diào)用組件的時候,就可以把需要的數(shù)據(jù)通過props
傳遞進去,這樣對話框就能根據(jù)不同的數(shù)據(jù)展示不同的內(nèi)容啦 。比如說,我們要在對話框里顯示不同的提示信息,就可以把提示信息作為props
傳遞給對話框組件 。再結(jié)合provide
和inject
,它們就像是一座橋梁,能夠讓不同層級的組件之間方便地進行通信,讓數(shù)據(jù)的傳遞更加靈活 。
實際應(yīng)用場景
經(jīng)過二次封裝實現(xiàn)動態(tài)調(diào)用的el-dialog
在實際項目中的應(yīng)用場景可太廣泛了 。比如說在用戶信息編輯場景,當(dāng)用戶點擊 “編輯” 按鈕,就可以動態(tài)彈出我們封裝好的對話框,里面填充好用戶當(dāng)前的信息,用戶修改完成后點擊確認(rèn),就能提交新的信息,整個過程非常流暢自然 。
在文件上傳確認(rèn)場景,當(dāng)用戶選擇好文件準(zhǔn)備上傳時,彈出對話框讓用戶確認(rèn)文件信息,比如文件名、文件大小等 。如果沒問題再點擊上傳,這樣可以避免用戶誤操作上傳錯誤的文件 。還有在權(quán)限管理中,當(dāng)管理員要給某個用戶分配新的權(quán)限時,通過動態(tài)調(diào)用對話框,展示所有權(quán)限選項,管理員勾選后提交,就能完成權(quán)限分配,操作簡單又高效 。
總結(jié)
經(jīng)過二次封裝實現(xiàn)動態(tài)調(diào)用的el-dialog
組件,不僅大大提高了代碼的復(fù)用性,讓我們在不同的業(yè)務(wù)場景中能夠輕松應(yīng)對對話框的各種需求,還增強了項目的靈活性和可維護性。以前寫一堆重復(fù)代碼的日子一去不復(fù)返啦!
強烈建議各位小伙伴在自己的項目中也嘗試應(yīng)用這種二次封裝的方法 ,相信你們會發(fā)現(xiàn)它的強大之處。要是在實踐過程中有什么經(jīng)驗,或者遇到了問題,都?xì)g迎在評論區(qū)留言分享。咱們一起交流,共同進步 !
到此這篇關(guān)于從0到1解鎖Element-Plus組件二次封裝El-Dialog動態(tài)調(diào)用的文章就介紹到這了,更多相關(guān)Element-Plus二次封裝El-Dialog內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue使用axios實現(xiàn)動態(tài)追加數(shù)據(jù)
在vuejs中使用axios時,有時候需要追加數(shù)據(jù),比如,移動端下拉觸底加載,分頁加載,滑動滾動條等,下面小編就來為大家介紹一下如何使用使用axios實現(xiàn)動態(tài)追加數(shù)據(jù)吧2023-10-10使用reactive導(dǎo)致數(shù)據(jù)失去響應(yīng)式的原因和解決方案
在 Vue 的響應(yīng)式系統(tǒng)中,reactive 對象是一個深度代理,它會追蹤對象屬性的變更,但如果你將整個對象重新賦值,那么 Vue 無法繼續(xù)追蹤新的對象,本文給大家介紹了使用reactive導(dǎo)致數(shù)據(jù)失去響應(yīng)式的原因和解決方案,需要的朋友可以參考下2024-09-09誤引用vuex-persistedstate導(dǎo)致用戶信息無法清除問題及解決
這篇文章主要介紹了誤引用vuex-persistedstate導(dǎo)致用戶信息無法清除問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04使用element-ui的Pagination分頁的注意事項及說明
這篇文章主要介紹了使用element-ui的Pagination分頁的注意事項及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02