模仿element-ui封裝vue元件庫(dialog)
封裝一個element-ui風格的dialog元件
前置知識:
vue過渡動畫
sync修飾符
具名插槽與v-slot指令
引數支援:
引數名 |
引數描述 | 引數型別 | 預設值 |
title | 對話方塊標題 | string | 提示 |
width | 寬度 | string | 50% |
top | 與頂部的距離 | string | 15vh |
visible | 是否顯示dialog(支援sync修飾符) | boolean | false |
事件支援:
事件名 |
事件描述 |
opened | 模態框顯示事件 |
closed | 模態框關閉事件 |
插槽說明:
插槽名稱 |
插槽描述 |
default | dialog的內容 |
title | dialog的標題 |
footer | dialog的底部操作區 |
4.1dialog元件的基本框架和樣式
首先搭建起來dialog元件的框架,暫時不加入插槽,只構建出基本的框架和樣式。
框架分為三個部分,頭部(header)、內容(body)、底部(footer),基本框架如下:
<template> <div class="ra-dialog_wrapper"> <div class="ra-dialog"> <div class="ra-dialog_header"> <span class="ra-dialog_title">提示</span> <button class="ra-dialog_headerbtn"> <i class="ra-icon-close"></i> </button> </div> <div class="ra-dialog_body"> <span>這是一段資訊</span> </div> <div class="ra-dialog_footer"> <ra-button>取消</ra-button> <ra-button type="primary">確定</ra-button> </div> </div> </div> </template>
樣式如下:
<style lang="scss" scoped> .ra-dialog_wrapper{ position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; z-index: 2001; background-color: rgba(0,0,0,0.5); .ra-dialog{ position: relative; margin: 15vh auto 50px; background: #fff; border-radius: 2px; box-shadow: 0 1px 3px rgba(0,0,0,0.3); box-sizing: border-box; width: 30%; &_header{ padding: 20px 20px 10px; .ra-dialog_title{ line-height: 24px; font-size: 18px; color: #303133; } .ra-dialog_headerbtn{ position: absolute; top: 20px; right: 20px; padding: 0; background: transparent; border: none; outline: none; cursor: pointer; font-size: 16px; .ra-icon-close{ color:909399 } } } &_body{ padding: 30px 20px; color: #606266; font-size: 14px; word-break: break-all; } &_footer{ padding: 10px 20px 20px; text-align: right; box-sizing: border-box; ::v-deep .ra-button:first-child{ margin-right: 20px; } } } } </style>
在main.js註冊後,在app.vue中引用。即可展示。
4.2自定義title內容
title標題部分除了普通的標題內容外,也應該可以設定標題的樣式,比如設定為h1紅色的自定義標題內容,所以在這裡我們就使用到了插槽,可以在使用時按照需求自定義標題內容和樣式。
4.2.0將標題span標籤放到slot插槽下,這樣便於控制span的內容和樣式。
<template> <div class="ra-dialog_wrapper"> <div class="ra-dialog"> <div class="ra-dialog_header"> <slot name="title"> <!-- 將span放到slot內,這樣不僅可以定義title文字,還可以定義樣式等 --> <span class="ra-dialog_title"> {{title}} </span> </slot> <button class="ra-dialog_headerbtn"> <i class="ra-icon-close"></i> </button> </div> <div class="ra-dialog_body"> <span>這是一段資訊</span> </div> <div class="ra-dialog_footer"> <ra-button>取消</ra-button> <ra-button type="primary">確定</ra-button> </div> </div> </div> </template>
4.2.1通過父子元件之間得傳值以及slot指定元件自定義title內容和樣式。
<ra-dialog title="溫馨提示"> <!-- 使用v-slot指定插槽進行編輯 --> <template v-slot:title> <h3 style="color:red">我是標題</h3> </template> </ra-dialog>
4.3自定義dialog的寬度和距離頂部的距離
4.3.1實現在元件呼叫時控制dialog元件的寬度以及位置。
只需要在父元件中傳遞寬度和高度,並且在子元件中獲取並且使用即可。
父元件傳值:
<ra-dialog width="80%" top="200px"></ra-dialog>
子元件使用:
<template> <div class="ra-dialog_wrapper"> <div class="ra-dialog" :style="{width:width,marginTop:top}"> ··· </div> </div> </template>
4.4自定義body內容
body內容可能是除span以外的其他內容,比如列表等,所以在這裡使用插,並且在這裡使用匿名插槽,使用匿名插槽的好處就是在使用時不需要使用template標籤指定內容,直接在元件標籤下編寫內容即可。
4.4.1在body中使用匿名元件
<div class="ra-dialog_body"> <slot></slot> </div>
4.4.2在父元件中,只需要在標籤下直接編輯內容即可,不需要再使用template標籤繫結插槽或者父子元件傳值了
<ra-dialog> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> </ra-dialog>
4.5自定義footer內容
footer中使用slot插槽,在父元件中的定義底部內容。
4.5.1設定footer插槽,如果沒有指定footer插槽,則不顯示
<div class="ra-dialog_footer"> <!-- 如果footer不傳遞內容,則不顯示footer --> <slot name="footer" v-if="$slots.footer"></slot> </div>
4.5.2父元件中的定義footer插槽內容
<template v-slot:footer> <ra-button>取消</ra-button> <ra-button type="primary">確定</ra-button> </template>
4.6dialog的顯示與隱藏
dialog元件的顯示與隱藏,需要使用到sync語法糖。這裡簡單介紹以下什麼是sync語法糖,sync通俗來說,是父子元件傳值過程中提供的一種模式,這種模式有兩個功能:1.將父元件向子元件傳值;2.子元件回撥一個值給父元件。
打個比方,如下程式碼需要兩步才能實現上述功能:1.向子元件傳值;2.接收子元件回撥的值
//父元件傳值 <demo :visible="visible" :money="money" @update:aa="fn1"></demo> //子元件回撥 methods: { fn () { this.$emit('aa', 200) } }
使用sync語法糖後,父元件不需要單獨宣告一個方法,只需要在回撥時宣告一個update繫結的回撥函式(這個繫結值是傳值自身)這樣在父元件中就不需要再次定義回撥函式進行接收了。
//父元件中的使用sync語法糖,傳遞和接收引數 <demo :visible.sync="visible" :money.sync="money"></demo> //子元件中使用update繫結引數的方法進行回撥 methods: { fn () { this.$emit('update:money', 200) this.$emit('update:visible', true) } }
根據上面對於sync語法糖的介紹,我們在dialog顯示和隱藏中要進行兩種處理
控制dialog的顯示和隱藏,我們首先在子元件中使用v-show對於組建的顯示與隱藏進行控制。
<div class="ra-dialog_wrapper" v-show="visible" @click.self="handleClose"> ··· </div>
4.6.1父元件控制dialog的顯示和隱藏
父元件中的直接通過傳遞一個引數visible,使用點選方法控制這個引數的布林值即可。
<ra-dialog :visible.sync="visible"> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> <template v-slot:footer> <ra-button @click="switchDialog">取消</ra-button> <ra-button type="primary">確定</ra-button> </template> </ra-dialog>
4.6.2子元件控制dialog的顯示和隱藏
子元件控制dialog的顯示和隱藏,不能直接修改父元件傳遞過來的值,需要使用回撥觸發父元件中的值進行修改,這裡就使用到了上面介紹的sync語法糖。
首先在父元件中使用:visible.sync="visible"向子元件進行傳值並且接收子元件回撥。
<div class="row"> <ra-dialog :visible.sync="visible"> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> <template v-slot:footer> <ra-button @click="switchDialog">取消</ra-button> <ra-button type="primary">確定</ra-button> </template> </ra-dialog>
子元件通過自身定義的方法,控制dialog元件的顯示與隱藏,然後將visible屬性回撥給父元件。
<template v-slot:footer> <ra-button>取消</one-button> <ra-button type="primary">確定</ra-button> </template> 回撥方法: method{ handleClose () { this.$emit('update:visible', false) } }
4.7dialog的動畫效果
使用transition包裹一個元素後,這個元素就會被自動新增類名,這部分vue.js文件都有介紹。
4.7.1使用transition包裹整個dialog框架
<template> <transition name="dialog-fade"> <div class="ra-dialog_wrapper" v-show="visible" @click.self="handleClose"> ··· </div> </transition> </template>
4.7.2使用vue動畫進行處理
這裡先定義了fade動畫,然後在dialog元件顯示和隱藏的時候呼叫(反向呼叫)這個動畫。
.dialog-fade-enter-active
animation: fade .3s;
}
.dialog-fade-leave-active{
animation: fade .3s reverse;
}
@keyframes fade{
0% {
opacity: 0;
transform: translateY(-20px);
}
100%{
opacity: 1;
transform: translateY(0);
}
}
-------------------------------------------------------至此,dialog元件封裝完成!-----------------------------------------------
附元件程式碼:
<template> <transition name="dialog-fade"> <div class="ra-dialog_wrapper" v-show="visible" @click.self="handlerClose"> <div class="ra-dialog" :style="{width:width,marginTop:top}"> <div class="ra-dialog_header"> <slot name="title"> <!-- 將span放到slot內,這樣不僅可以定義title文字,還可以定義樣式等 --> <span class="ra-dialog_title"> {{title}} </span> </slot> <button class="ra-dialog_headerbtn" @click="handlerClose"> <i class="ra-icon-close"></i> </button> </div> <div class="ra-dialog_body"> <slot></slot> </div> <div class="ra-dialog_footer"> <!-- 如果footer不傳遞內容,則不顯示footer --> <slot name="footer" v-if="$slots.footer"></slot> </div> </div> </div> </transition> </template> <script> export default { name: 'ra-dialog', props: { title: { type: String, default: '提示' }, width: { type: String, default: '50%' }, top: { type: String, default: '50%' }, visible: { type: Boolean, default: false } }, methods: { handlerClose () { this.$emit('update:visible', false) } } } </script> <style lang="scss" scoped> // scope會給當前元件模板中的所有元素都新增一個隨機的屬性 // scope會給當前元件中所有的樣式頁都新增一個對應的屬性選擇器 .ra-dialog_wrapper{ position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; z-index: 2001; background-color: rgba(0,0,0,0.5); .ra-dialog{ position: relative; margin: 15vh auto 50px; background: #fff; border-radius: 2px; box-shadow: 0 1px 3px rgba(0,0,0,0.3); box-sizing: border-box; width: 30%; &_header{ padding: 20px 20px 10px; .ra-dialog_title{ line-height: 24px; font-size: 18px; color: #303133; } .ra-dialog_headerbtn{ position: absolute; top: 20px; right: 20px; padding: 0; background: transparent; border: none; outline: none; cursor: pointer; font-size: 16px; .ra-icon-close{ color:#909399; } } } &_body{ padding: 30px 20px; color: #606266; font-size: 14px; word-break: break-all; } &_footer{ padding: 10px 20px 20px; text-align: right; box-sizing: border-box; ::v-deep .ra-button:first-child{ margin-right: 20px; } } } } .dialog-fade-enter-active{ animation: fade .3s; } .dialog-fade-leave-active{ animation: fade .3s reverse; } @keyframes fade{ 0% { opacity: 0; transform: translateY(-20px); } 100%{ opacity: 1; transform: translateY(0); } } </style>