如何給element添加一個(gè)抽屜組件的方法步驟
近來因?yàn)闃I(yè)務(wù)需要,對比iview和element庫,發(fā)現(xiàn)element確實(shí)要比實(shí)習(xí)期間使用的iview強(qiáng)大點(diǎn),尤其文檔更為友好,但是iview的組件功能更多一點(diǎn),比如分割線和抽屜組件
今天特意手寫一個(gè)抽屜組件,方便自己使用element庫,寫好的組件我已經(jīng)放在我的githup了, 點(diǎn)這里
一、實(shí)踐
1.分析
一個(gè)抽屜組件的z-index必定是在當(dāng)前頁面之上的,在抽屜主體之外的區(qū)域還會(huì)有一層半透明的遮罩層,知道這些就很容易了
// drawer.vue
<template>
<div class="mask"></div>
<div class="drawer">
<div class="drawer_body"></div>
</div>
</template>
<style scoped>
.drawer {
position: absolute;
height: 100vh;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 1000000 !important;
}
.drawer .drawer_body {
height: 100%;
position: absolute;
z-index: 1000001;
background-color: #fff;
}
.mask {
height: 100vh;
width: 100vw;
position: absolute;
z-index: 1000000;
top: 0;
left: 0;
background-color: #000;
opacity: 0.5;
}
</style>
現(xiàn)在已經(jīng)是我們想要的樣子了,接下來是給drawer_body添加樣式

作為一個(gè)靈活的組件庫,我們希望樣式是可以隨時(shí)定制的,所以,接下要添加的樣式都 使用props
動(dòng)態(tài)綁定的
參考iview的樣式,除了抽屜的寬度,還需要設(shè)置抽屜的方向,當(dāng)我們需要抽屜時(shí)讓它顯示出來,不需要時(shí)隱藏它,或者為了更加顯眼,甚至給抽屜更換背景色......,這些都是可以實(shí)現(xiàn)的,看下面的代碼
<script>
export default {
props: {
// 是否顯示drawer
drawerVisible: Boolean,
// drawer方向
direction: {
type: String,
validator(val) {
return ["right", "left"].indexOf(val) !== -1;
}
},
// drawer寬度
width: {
type: Number,
default: 400
},
// drawer背景色
background: {
type: String,
default: "#ffffff"
},
// 是否顯示遮罩層
mask: {
type: Boolean,
default: true
}
}
};
</script>
對于寬度和背景色,你還需要額外的處理下
<div
class="drawer_body"
:style="{'right':direction=='right'?'0':'auto',
'left':direction=='left'?'0':'auto',
'width':width+'px','background':background}"
>drawer</div>
你只需在使用的地方引入組件,然后提供你想修改的props值
//index.vue
<template>
<div>
...
<el-button size="mini" @click="visible">顯示抽屜</el-button>
<Drawer
:drawerVisible="drawerVisible"
direction="right"
:mask="true"
background="aquamarine"
></Drawer>
</div>
</template>
<script>
export default {
data() {
return {
drawerVisible: false
};
},
methods:{
// 打開抽屜
visible() {
this.drawerVisible = true;
}
}
}
</script>

2.關(guān)閉抽屜
在點(diǎn)擊遮罩層的時(shí)候,我們希望可以關(guān)閉已經(jīng)打開的抽屜組件,這里如果你直接修改父組件傳過來的drawerVisible值,會(huì)報(bào)錯(cuò)如下
vue.esm.js:629 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders. Instead, use a data or computed property based on the
prop's value. Prop being mutated: "drawerVisible"
這是因?yàn)関ue是單向數(shù)據(jù)流的,如果想改變父元素的值,必須使用監(jiān)聽事件的方式,但是2.3.0之后添加了.sync修飾符,所以,正確的做法是使用.sync修飾符
...
<div v-if="drawerVisible" class="mask"></div>
<transition :name="this.direction=='left'?'slide-right':'slide-left'">
<div v-if="drawerVisible" @click.stop="closeBtn?'':close" class="drawer">
<div
class="drawer_body"
:style="{
'right':direction=='right'?'0':'auto',
'left':direction=='left'?'0':'auto',
'width':width+'px',
'background': background}"
>
</div>
</div>
</transition>
...
methods: {
close() {
this.$emit("update:drawerVisible", false);
}
}
另外,我們還希望在關(guān)閉抽屜組件時(shí),我們可以監(jiān)聽到這個(gè)事件然后做出反應(yīng)
methods: {
close() {
this.$emit("update:drawerVisible", false);
this.$emit("close");
}
}
此時(shí)需要在抽屜組件上添加
<Drawer
:drawerVisible.sync="drawerVisible"
@close="close"
>
</Drawer>
methods:{
close(){
// 關(guān)閉抽屜組件時(shí)你要做的事
}
}
2.動(dòng)畫
動(dòng)畫是UI的靈魂,所以接下來給抽屜組件的顯示和隱藏添加動(dòng)畫,我們使用transition的css動(dòng)畫做動(dòng)畫過度效果
//drawer.vue
<div class="drawer">
<div class="mask"></div>
<!-- 不同方向使用不用的動(dòng)畫名稱,如果抽屜在左邊,則進(jìn)入方向是朝 → -->
<transition :name="this.direction=='left'?'slide-right':'slide-left'">
<div
class="drawer_body"
v-if="drawerVisible"
:style="{'right':direction=='right'?'0':'auto',
'left':direction=='left'?'0':'auto',
'width':width+'px',
'background':background}"
>drawer</div>
</transition>
</div>
</template>
<style scoped>
/*
* ...
*這里省略了寫過的樣式
*/
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
will-change: transform;
transition: transform 300ms;
position: absolute;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.slide-right-enter,
.slide-right-leave-active {
transform: translate(-100%, 0);
}
.slide-left-leave-active,
.slide-left-enter {
transform: translate(100%, 0);
}
</style>
雖然現(xiàn)在已經(jīng)完全實(shí)現(xiàn)了抽屜的功能,但是本著更加精美的原則,我還打算使用slot給它添加辯題和頁腳
3.添加標(biāo)題
標(biāo)題solt的name值是header
添加標(biāo)題的目的是為了讓抽屜組件看起來更加清楚,此外,除了添加標(biāo)題,我還想添加個(gè)關(guān)閉按鈕
// 需要添加幾個(gè)props屬性
<script>
export default {
props: {
// drawer標(biāo)題
title: String,
// 是否顯示關(guān)閉按鈕
closeBtn: {
type: Boolean,
default: false
},
}
};
</script>
你可以選擇是否添加標(biāo)題,是否添加關(guān)閉按鈕,值的注意的是如果添加了關(guān)閉按鈕,點(diǎn)擊遮罩層就不會(huì)自動(dòng)關(guān)閉抽屜組件了

<!--這里要啰嗦下布局,如果你只選擇開啟關(guān)閉按鈕,那justify-content布局是flex-end
如果兩者都開啟,那justify-content布局是space-between-->
<slot name="header">
<div
v-if="title||closeBtn"
:style="{'justify-content':title?'space-between':'flex-end'}"
class="title"
>
<div v-if="title">{{title}}</div>
<el-button
v-if="closeBtn"
circle
size="mini"
icon="el-icon-close"
@click="close"
></el-button>
</div>
</slot>
我是這么做到禁用遮罩層點(diǎn)擊事件的
<div v-if="drawerVisible" @click.stop="closeBtn?'':close" class="mask"></div>
當(dāng)然這些你可以使用具名插槽自定義的
<Drawer :width="400" direction="right" :mask="true" title="抽屜組件" > <div v-slot:header>這里是自定義標(biāo)題</div> <div style="height:100px"></div> </Drawer>
4.添加頁腳
頁腳solt的name值是footer
為了使得頁腳和標(biāo)題有一定的距離,我給主體內(nèi)容添加了最小高度
<div style="min-height:82vh;padding: 5px 0"> <slot></slot> </div>
方法是很類似的,只是我多添加了兩個(gè)監(jiān)聽事件,確定和取消
//drawer.vue <slot name="footer"> <div class="footer"> <el-button size="mini" type="primary" @click="footerOk">確認(rèn)</el-button> <el-button size="mini" @click="footerCal">取消</el-button> </div> </slot>
//引入的頁面 <Drawer :width="400" direction="right" :mask="true" title="抽屜組件" :footer-ok="footerOk" :footer-cal="footerCal" > </Drawer>
還需要在props中添加對應(yīng)的值
props: {
footerOk: Function,
footerCal: Function
},
關(guān)于頁腳的布局是這樣的

.footer {
border-top: 0.1px solid #ddd;
display: flex;
justify-content: flex-end;
padding-top: 10px;
}
當(dāng)然這些你也是可以使用具名插槽自定義的
<Drawer :width="400" direction="right" :mask="true" title="抽屜組件" > <div v-slot:header>這里是自定義標(biāo)題</div> <div style="height:100px"></div> <div v-slot:footer>這里是自定義頁腳</div> </Drawer>
5.主體是否可以滾動(dòng)
前面說過給主體添加了最小高度,但是超過最小高度,可能會(huì)被撐開布局,所以我給它添加了滾動(dòng)功能
// props添加
// 是否開啟滾動(dòng)
scroll: {
type: Boolean,
default: false
}
在drawer_body的樣式末尾追加overflow-y樣式
<div
class="drawer_body"
:style="{
'right':direction=='right'?'0':'auto',
'left':direction=='left'?'0':'auto',
'width':width+'px',
'background': background,
'overflow-y':scroll?'scroll':'hidden'}"
>
</div>
scroll默認(rèn)是不開啟的,如果你的抽屜要放的內(nèi)容少,就不用理這個(gè)屬性,但是當(dāng)內(nèi)容撐開抽屜時(shí)記得手動(dòng)開啟這個(gè)功能復(fù)制代碼
6.細(xì)節(jié)的優(yōu)化
這里說下自己的不足之處,并且如何改進(jìn)它
a.滾動(dòng)條bug
當(dāng)選擇抽屜在右邊時(shí),動(dòng)畫過程中會(huì)出現(xiàn)滾動(dòng)條,看起來讓我的UI組件大打折扣,針對這個(gè)問題我打算在組件中監(jiān)聽drawerVisible,當(dāng)它需要被展示時(shí)禁用body的滾動(dòng)效果,當(dāng)它不需要被展示時(shí),還原body的展示效果
watch: {
drawerVisible(n, o) {
if (n == true) {
document.documentElement.style.overflowY = "hidden";
document.documentElement.style.overflowX = "hidden";
}
}
},
b.向下冒泡bug
在點(diǎn)擊抽屜以外的區(qū)域可以正常關(guān)閉抽屜,但是我發(fā)現(xiàn)當(dāng)我點(diǎn)擊抽屜非按鈕區(qū)域時(shí),也會(huì)關(guān)閉抽屜,這是向下冒泡的bug,這個(gè)bug我的解決方案是在drawer_body上添加個(gè)無意義的事件,并阻止向上冒泡
<div
@click.stop="clickBg_" // 注意這里
class="drawer_body"
:style="{
'right':direction=='right'?'0':'auto',
'left':direction=='left'?'0':'auto',
'width':width+'px',
'background': background,
'overflow-y':scroll?'scroll':'hidden'}"
>
</div>
二、API文檔
1.屬性
| 屬性 | 描述 | 類型 | 默認(rèn) |
|---|---|---|---|
| drawerVisible | 是否顯示drawer | Boolean | false |
| direction | drawer方向 | String | left |
| width | drawer寬度 | Number | 400 |
| background | drawer背景色 | String | #ffffff |
| mask | 是否顯示遮罩層 | Boolean | true |
| title | drawer標(biāo)題 | Boolean | true |
| closeBtn | 是否顯示關(guān)閉按鈕 | String | --- |
| scroll | 是否開啟滾動(dòng) | Boolean | false |
2.事件
| 事件 | 描述 | 返回值 |
|---|---|---|
| close | 監(jiān)聽關(guān)閉事件 | 無 |
| footerOk | 頁腳確認(rèn)綁定事件,使用默認(rèn)頁腳時(shí)有效 | 無 |
| footerCal | 頁腳取消綁定事件,使用默認(rèn)頁腳時(shí)有效 | 無 |
3.slot
| name | 描述 |
|---|---|
| header | 頁頭插槽名稱 |
| default | 抽屜主體部分,可省略 |
| footer | 頁腳插槽名稱 |
注意:插槽里的按鈕都是使用element內(nèi)置的組件,如果你的項(xiàng)目里沒有引入element庫
那最好請使用具名插槽重寫頁頭和頁腳部分
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue使用video插件vue-video-player的示例
這篇文章主要介紹了vue使用video插件vue-video-player的示例,幫助大家更好的理解和使用vue插件,感興趣的朋友可以了解下2020-10-10
Vue數(shù)據(jù)驅(qū)動(dòng)模擬實(shí)現(xiàn)5
這篇文章主要介紹了Vue數(shù)據(jù)驅(qū)動(dòng)模擬實(shí)現(xiàn)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
easycom模式開發(fā)UNI-APP組件調(diào)用必須掌握的實(shí)用技巧
uni-app基于VUE開發(fā),通常組件的使用都是先安裝,然后全局或者局部引入,注冊,今天通過本文給大家分享easycom模式開發(fā)UNI-APP組件調(diào)用必須掌握的實(shí)用技巧,需要的朋友一起看看吧2021-08-08
Vue 2.5 Level E 發(fā)布了: 新功能特性一覽
很高興Vue 2.5 Level E 發(fā)布了。在這篇文章中,我們將重點(diǎn)介紹一些更重要的的變化:更好的 TypeScript 集成,更好的錯(cuò)誤處理,更好地支持單文件組件中的函數(shù)式組件以及與環(huán)境無關(guān)的服務(wù)端渲染2017-10-10
vue通過指令(directives)實(shí)現(xiàn)點(diǎn)擊空白處收起下拉框
這篇文章主要介紹了vue通過指令(directives)實(shí)現(xiàn)點(diǎn)擊空白處收起下拉框,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12
vue子組件實(shí)時(shí)獲取父組件的數(shù)據(jù)實(shí)現(xiàn)
本文主要介紹了vue子組件實(shí)時(shí)獲取父組件的數(shù)據(jù)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12
Vue2響應(yīng)式系統(tǒng)之set和delete
這篇文章主要介紹了Vue2響應(yīng)式系統(tǒng)之set和delete,通過為對象收集依賴,將對象、數(shù)組的修改、刪除也變成響應(yīng)式的了,同時(shí)為用戶提供了和方法,下文詳細(xì)介紹需要的朋友可以參考一下2022-04-04

