封裝Vue Element的dialog彈窗組件
我本沒有想著說要封裝一個彈窗組件,但有同行的朋友在問我,而且彈窗組件也確實在項目開發中用的比較多。思前想後,又本著樣式統一且修改起來方便的原則,還是再為大家分享一個我所封裝的彈窗組件吧。
其實,並不是所有封裝組件的方式都是一成不變的,你可以採用函數式組件這種能提高性能的方式,也可以使用帶有狀態和生命周期的普通組件的封裝方式。但像dialog這種包含很多點擊事件如確定或提交事件、取消或重置事件、右上角那個小叉叉的關閉事件等,又有可能包含嵌套其他組件如表格組件、表單組件、樹形組件、穿梭框組件等的公共組件,其成分略微複雜,功能不太單一,你若要採用函數式組件的方式來封裝也不是不可以,只是可能xue微要麻煩一些,我自己建議是不採用這種封裝方式,就採用普通的封裝方式就好。
至於普通組件的封裝方式,我想大家平時在開發的過程中對所接觸的普通組件即帶有狀態和生命周期,也能快樂地使用this關鍵詞的組件已經是非常熟悉了,所以這種封裝方式我就不會再做過多的介紹了。以下是具體的實現過程。
照例還是想來張效果圖:
1、所封裝的彈窗組件dialog.vue
<template>
<el-dialog
top="20vh"
class="el-dialog-cus"
v-bind="{...$attrs, ...{title, width, center}}"
:visible="visible"
:before-close="beClose"
append-to-body
>
<slot></slot>
<div slot="footer">
<el-button @click="cancel" plain>{{btnTxt[0]}}</el-button>
<el-button @click="confirm" type="primary" v-if="btnTxt[1]">{{btnTxt[1]}}</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
inheritAttrs: false,
props: {
title: {
type: String,
default: "提示",
},
width: {
type: String,
default: "420px",
},
center: {
type: Boolean,
default: true,
},
autoClose: {
type: Boolean,
default: true,
},
beforeClose: {
type: Function,
default: () => {}
},
btnTxt: {
type: Array,
default: () => ["取消", "確定"],
},
},
data() {
return {
visible: false,
};
},
methods: {
open(ok) {
this.ok = ok;
this.visible = true;
},
cancel() {
this.visible = false;
},
confirm() {
let cancel = () => this.cancel();
this.ok(cancel);
this.autoClose && cancel();
},
beClose(done) {
done();
this.beforeClose();
this.cancel();
},
},
};
</script>
<style lang="scss">
.el-dialog-cus {
.el-dialog {
padding: 8px;
}
.el-dialog__title {
font-weight: bold;
}
.el-dialog__header {
padding: 20px 0 12px;
}
.el-dialog__headerbtn {
top: 8px;
right: 8px;
}
.el-dialog--center .el-dialog__body {
padding: 0 24px;
text-align: center;
}
.el-dialog__footer {
padding: 20px;
.el-button {
padding: 8px 20px;
& + .el-button {
margin-left: 40px;
}
}
}
}
</style>
對於以上的一些程式碼,我需要做一些特別的說明:
open(ok) {
this.ok = ok;
this.visible = true;
}
這段程式碼是彈出彈窗的方法,為的是在使用彈窗組件時,我們只需點擊一個按鈕並使用ref來獲取彈窗組件的這個方法即可打開彈窗,剩下的關閉彈窗的操作就交給彈窗的確定或取消按鈕來完成即可。我們不用再額外的寫關閉彈窗的方法並將關閉彈窗的props參數傳給彈窗組件。另外,在打開彈窗的方法中我還保存了一個ok事件,這個ok事件是用於在點擊了彈窗組件的確定或提交按鈕後所觸發的回調方法,比如我們點擊了彈窗的提交按鈕,我們需要調一個介面來完成數據的存儲或修改,那麼這個ok事件就是為它實現的,畢竟彈窗組件充當的只是一個我們用於處理業務邏輯的中間橋樑。
confirm() {
let cancel = () => this.cancel();
this.ok(cancel);
this.autoClose && cancel();
}
這段程式碼是在點擊彈窗的確定或提交按鈕時觸發的,但為什麼要給一個之前保存的ok回調函數傳一個關閉的方法參數呢,這是因為有時我們在點擊了確定或提交的按鈕後並不想立即關閉這個彈窗,而是想在幾秒鐘的倒計時後再關閉這個彈窗並跳轉到其他頁面,亦或是在A彈窗的基礎上又彈出另外一個B彈窗,在B彈窗的基礎上又彈出一個C彈窗。關閉C彈窗時,還能看到B彈窗,而不用在A彈窗的基礎上通過點擊事件再彈出B彈窗。這個時候就需要把關閉的方法當作參數傳遞給ok回調函數,讓調用彈窗組件的人自行控制在什麼時候關閉彈窗,這難道不香嗎?只不過這個時候可能需要多給彈窗組件傳一個參數autoClose來通知它是不是需要前端自行控制什麼時候來關閉彈窗,畢竟彈窗組件在大多數情況下都是點擊了確定或提交按鈕後就直接被關閉了。
beClose(done) {
done();
this.beforeClose();
this.cancel();
}
這段程式碼是彈窗組件的關閉前before-close
方法,element的官方解釋是「關閉前的回調,會暫停Dialog的關閉」,官方還給了一個特別的說明:
before-close
僅當用戶通過點擊關閉圖標或遮罩關閉Dialog
時起效。如果你在footer
具名slot
里添加了用於關閉Dialog
的按鈕,那麼可以在按鈕的點擊回調函數里加入before-close
的相關邏輯。
它接收一個參數done,用於關閉Dialog。而this.beforeClose()
是用來自定義關閉前所要做的一些事情的方法。
還有一點需要注意的是:普通組件所有未聲明的屬性都會被解析到$attrs裡面,並自動掛載到組件根元素上面。因為本次封裝的彈窗組件的外面已經沒有根元素了,也就是標籤el-dialog的外面沒有再包裹一層div標籤,所以前邊這句話的意義已經不大了。如果標籤el-dialog的外面又包裹了一層div,那麼那句話就有意義了,也就是說這些未聲明的屬性也會出現在最外層的div上,如果不想讓這些未聲明的屬性也出現在最外層的div上,那麼就可以用inheritAttrs:false
來禁止。但本次封裝的彈窗組件的外面沒有根元素,所以加不加這個inheritAttrs:false
都無所謂了。
2、彈窗組件的使用:
<template>
<div>
<el-button @click="open">點我打開</el-button>
<Dialog ref="dialog" :title="title" :width="width" :center="center" :btnTxt="btnTxt" :beforeClose="beforeClose"><span>this is a dialog</span></Dialog>
</div>
</template>
<script>
import Dialog from "@/components/dialog";
export default {
components: {
Dialog,
},
data() {
return {
width: '500px',
title: '溫馨提示',
center: true,
btnTxt: ['取消', '提交'],
};
},
methods: {
open() {
this.$refs.dialog.open(cancel => {
// cancel();
console.log('點擊提交按鈕了')
});
},
beforeClose(){
console.log('關閉前');
},
}
};
</script>
以上具體的使用方法中:
open() {
this.$refs.dialog.open(cancel => {
// cancel();
console.log('點擊提交按鈕了')
});
}
這段程式碼就是用來打開或彈出彈窗組件,這裡就是採用ref來獲取彈窗組件的open方法,並向彈窗組件的open方法傳一個回調函數,而這個回調函數的參數就是組件中ok事件觸發時所返回的函數參數cancel,如果不需要前端來自行控制彈窗的關閉,則不接收這個cancel參數即可。