從0到1解鎖Element-Plus組件二次封裝El-Dialog動態(tài)調(diào)用的原理解析
技術(shù)難題初登場
家人們,最近在開發(fā)一個超復雜的后臺管理系統(tǒng)項目,里面有各種數(shù)據(jù)展示、表單提交、權(quán)限控制等功能,在這個過程中,我頻繁地使用到了element-plus組件庫中的el-dialog組件 。它就像一個小彈窗,可以用來顯示各種提示信息、編輯表單之類的。比如說在用戶點擊 “編輯” 按鈕時,就彈出一個el-dialog,里面放著編輯表單,讓用戶修改數(shù)據(jù)。
但隨著功能的不斷完善,我發(fā)現(xiàn)原生的el-dialog組件在某些場景下真的不夠靈活。比如說,我想在不同的組件中根據(jù)不同的業(yè)務邏輯動態(tài)地控制對話框的顯示和隱藏,還要傳遞不同的數(shù)據(jù)給對話框,原生組件用起來就很麻煩,每次都要寫很多重復的代碼,這可太影響開發(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 = {}) {
// 合并默認配置
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);
},
},
{
// 默認插槽用于對話框內(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
// 掛載應用
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();
// 導出創(chuàng)建對話框的函數(shù)
export function createDialog(options) {
return dialogManager.create(options);
}
// 導出關(guān)閉所有對話框的函數(shù)
export function closeAllDialogs() {
dialogManager.closeAll();
}調(diào)用示例
-------------------------------- 基本文本對話框 -------------------------------------
import { createDialog } from '@/utils/dialog-manager';
export default {
methods: {
showSimpleDialog() {
createDialog({
title: '確認操作',
content: '你確定要執(zhí)行這個操作嗎?',
onClose: () => {
console.log('對話框已關(guān)閉');
},
footerBtns: [
{
label: '取消',
handler: (instance) => {
console.log('點擊了取消');
instance.close();
},
},
{
label: '確認',
type: 'primary',
handler: (instance) => {
console.log('執(zhí)行確認操作');
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的響應式原理 。我們都知道,在Vue中,數(shù)據(jù)發(fā)生變化時,視圖會自動更新。我們封裝的組件就是基于這個特性,通過一個響應式的變量來控制el-dialog的顯示與隱藏。比如說,我們定義一個isDialogVisible變量,當它為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ù)的傳遞更加靈活 。
實際應用場景
經(jīng)過二次封裝實現(xiàn)動態(tài)調(diào)用的el-dialog在實際項目中的應用場景可太廣泛了 。比如說在用戶信息編輯場景,當用戶點擊 “編輯” 按鈕,就可以動態(tài)彈出我們封裝好的對話框,里面填充好用戶當前的信息,用戶修改完成后點擊確認,就能提交新的信息,整個過程非常流暢自然 。
在文件上傳確認場景,當用戶選擇好文件準備上傳時,彈出對話框讓用戶確認文件信息,比如文件名、文件大小等 。如果沒問題再點擊上傳,這樣可以避免用戶誤操作上傳錯誤的文件 。還有在權(quán)限管理中,當管理員要給某個用戶分配新的權(quán)限時,通過動態(tài)調(diào)用對話框,展示所有權(quán)限選項,管理員勾選后提交,就能完成權(quán)限分配,操作簡單又高效 。
總結(jié)
經(jīng)過二次封裝實現(xiàn)動態(tài)調(diào)用的el-dialog組件,不僅大大提高了代碼的復用性,讓我們在不同的業(yè)務場景中能夠輕松應對對話框的各種需求,還增強了項目的靈活性和可維護性。以前寫一堆重復代碼的日子一去不復返啦!
強烈建議各位小伙伴在自己的項目中也嘗試應用這種二次封裝的方法 ,相信你們會發(fā)現(xiàn)它的強大之處。要是在實踐過程中有什么經(jīng)驗,或者遇到了問題,都歡迎在評論區(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導致數(shù)據(jù)失去響應式的原因和解決方案
在 Vue 的響應式系統(tǒng)中,reactive 對象是一個深度代理,它會追蹤對象屬性的變更,但如果你將整個對象重新賦值,那么 Vue 無法繼續(xù)追蹤新的對象,本文給大家介紹了使用reactive導致數(shù)據(jù)失去響應式的原因和解決方案,需要的朋友可以參考下2024-09-09
誤引用vuex-persistedstate導致用戶信息無法清除問題及解決
這篇文章主要介紹了誤引用vuex-persistedstate導致用戶信息無法清除問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04
使用element-ui的Pagination分頁的注意事項及說明
這篇文章主要介紹了使用element-ui的Pagination分頁的注意事項及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02

