天天看點

高仿Element.ui搭建自己的元件(2)

補充sync和self兩個事件修飾符

self有點隻可意會不能言傳啊,就比如我們再有遮罩層的彈窗,我們想點選遮罩層,彈窗消失,但我們卻發現點選彈窗,彈窗也消失了,這個時候我們可以給遮罩層事件加上.self,見下文内容。

至于sync,當子元件要修改父元件傳遞來的值時,往往要通過自定義事件,我們可以搭配update:變量和 :變量.sync的方式來實作

話不多說,直接上代碼吧

父元件

<!-- 原本自定義事件改變父元件傳遞的值 -->
<!-- <test-sync :price='price' @changePrice='changePriceClick'></test-sync> -->
<test-sync :price.sync='price'></test-sync>
           
data () {
  return {
    price: 200,
    visible: false
  }
}
           

子元件

<h2>sync文法</h2>
<span>{{price}}</span>
<input type="number" v-model="inputPrice">
<h-button type='info' @click="changePriceClick">點選</h-button>
           
props: {
  price: {
    type: Number,
    default: 100
  }
},
data () {
  return {
    inputPrice: '',
    showRun: false
  }
},
methods: {
  // 子元件不要去修改父元件傳遞來的值
  // changePriceClick () {
  //   this.price = 200
  // }
  changePriceClick () {
    this.$emit('update:price', this.inputPrice - 0)
  }
}
           

補充vue動畫和css動畫

給要添加動畫的内容加上transition 标簽,配合name和enter/leave-active來使用

<h2>測試vue動畫</h2>
<transition name='testRun'>
  <span v-show="showRun">動畫效果</span>
</transition>
<h-button type='primary' @click="showRun = !showRun">動畫</h-button>
           
data () {
  return {
    inputPrice: '',
    showRun: false
  }
},
           
// vue提供的動畫配合css動畫
.testRun-enter-active{
  animation: run 0.5s;
}
.testRun-leave-active {
  animation: run .5s reverse;
}
@keyframes run{
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
           

搭建dialog元件

  1. 具名插槽(template配合v-slot指定占位符slot的name屬性值即可,這樣友善我們添加樣式等等)
  2. props通信和校驗
  3. sync文法

如果以上知識點沒問題(參考高仿系列第一篇文章),以及明确了封裝元件的一般套路,我們直接看源代碼吧

彈窗标題,彈窗寬度,距頂部距離,主體内容,關閉彈窗字型圖示,底部按鈕,彈窗顯示隐藏,彈出動畫效果,等等都可以自定義

效果圖

高仿Element.ui搭建自己的元件(2)

附源代碼

/dialog.vue

<template>
  <!-- <div class="hDialog_wrapper">
    <div class="hDialog">
        <div class="hDialog_header">
            <span class="hDialog_title">提示</span>
            <button class="hDialog_headerbtn">
                <i class="one-icon-close"></i>
            </button>
        </div>
        <div class="hDialog_body">
            <span>這是一段資訊</span>
        </div>
        <div class="hDialog_footer">
            <h-button>取消</h-button>
            <h-button type="primary">确定</h-button>
        </div>
    </div>
 </div> -->
 <!-- 給整個彈窗加一個動畫 -->
 <transition name="showDialog">
     <!-- hDialog_wrapper外層遮罩層 -->
    <div class="hDialog_wrapper" v-show="visible" @click.self="handleClose">
        <!-- hDialog實實在在的彈框 -->
        <div class="hDialog" :style="{width,marginTop}">
            <div class="hDialog_header">
                <!-- 借助具名插槽友善給标題定義樣式 -->
                <slot name="title">
                    <span class="hDialog_title">{{title}}</span>
                </slot>
                <button class="hDialog_headerbtn">
                    <i :class="[icon]" @click="handleClose"></i>
                </button>
            </div>
            <div class="hDialog_body">
                <slot name="bodyContent">
                    <div>{{bodyContent}}</div>
                </slot>
            </div>
            <div class="hDialog_footer">
                <slot name="footer">
                    <div>{{footer}}</div>
                </slot>
            </div>
        </div>
    </div>
 </transition>
</template>

<script>
export default {
    name: 'HDialog',
    props: {
        title: {
            type: String,
            default: ''
        },
        width: {
            type: String,
            default: ''
        },
        marginTop: {
            type: String,
            default: ''
        },
        icon: {
            type: String,
            default: ''
        },
        bodyContent: {
            type: String,
            default: ''
        },
        footer: {
            type: String,
            default: ''
        },
        visible: {
            type: Boolean,
            default: false
        }
    },
    methods: {
        // sync修飾符的作用
        handleClose () {
            this.$emit('update:visible', false)
        }
    }
}
</script>

<style lang='scss' scoped>
    .hDialog_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);
        .hDialog{
            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;
            .hDialog_title{
                line-height: 24px;
                font-size: 18px;
                color: #303133;
            }
            .hDialog_headerbtn{
                position: absolute;
                top: 20px;
                right: 20px;
                padding: 0;
                background: transparent;
                border: none;
                outline: none;
                cursor: pointer;
                font-size: 16px;
                .one-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 .one-button:first-child{
                    margin-right: 20px;
                }
            }
        }
    }
    .showDialog-enter-active{
        animation: show .4s;
    }
    .showDialog-leave-active{
        animation: show .4s reverse; 
    }
    @keyframes show {
        0% {
            opacity: 0;
            transform: translateY(-20px);
        }
        100% {
            opacity: 1;
            transform: translateY(0px);
        }
    }
</style>
           

/App.vue

<!-- 彈窗标題,彈窗寬度,距頂部距離,主題内容,關閉彈窗等等都可以自定義 -->
<h2>dialog元件</h2>
<h-button @click="visible = true" type='primary'>點選顯示dialog</h-button>
<h-dialog width='50%' marginTop='200px' icon='hButton-icon-cha' :visible.sync = 'visible'>
  <!-- 使用v-slot指定名稱的插槽 -->
  <template v-slot:title>
    <h3 style="color:red">我是标題</h3>
  </template>
  <template v-slot:bodyContent>
    <div class="bodyContent">
      <span>一個段落</span>
      <h-button type='primary'>一個按鈕</h-button>
      <span style="margin-left:20px">一個span</span>
    </div>
    <hr>
    <div class="bodyContent">
      <span>一個段落</span>
      <h-button type='primary'>一個按鈕</h-button>
      <span style="margin-left:20px">一個span</span>
    </div>
  </template>
  <template v-slot:footer>
    <h-button type='primary' @click="visible = false">取消</h-button>
    <h-button type='primary' @click="visible = false">确定</h-button>
  </template>
</h-dialog>
           

/main.js

import Vue from 'vue'
import App from './App.vue'
import HButton from './components/button'
import HDialog from './components/dialog'
import './static/iconfont.css'

Vue.config.productionTip = false
Vue.component(HButton.name, HButton)
Vue.component(HDialog.name, HDialog)

new Vue({
  render: h => h(App)
}).$mount('#app')