我本沒有想著說要封裝一個彈窗元件,但有同行的朋友在問我,而且彈窗元件也確實在專案開發中用的比較多。思前想後,又本著樣式統一且修改起來方便的原則,還是再為大家分享一個我所封裝的彈窗元件吧。
其實,並不是所有封裝元件的方式都是一成不變的,你可以採用函式式元件這種能提高效能的方式,也可以使用帶有狀態和生命週期的普通元件的封裝方式。但像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引數即可。