1. 程式人生 > 其它 >模仿element-ui封裝vue元件庫(dialog)

模仿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>