封装dialog组件前的插槽技术温习

为什么要封装组件(组件化开发)

组件化开发(封装组件)的益处

益处不言而喻,能够减少代码的复用性、灵活性,从而进步开发效率。试想如果一个我的项目中在很多页面都能用到一个弹出框,若在每个页面都去写一套弹出框的构造款式和对应js的逻辑。这样的话,开发效率会大打折扣。毕竟当初产品经理都提倡麻利开发(跑得快游戏)。

组件化开发的了解

假如咱们封装好一个公共的弹框组件,哪里须要应用就援用到哪里就行了。

什么???不同页面的弹出框有的数据不太一样?有的是新增数据弹出框、有的是批改数据弹出框、还有的是删除数据弹出框。没关系!依据弹出框文字不同,往里面传不同参就行了。比方新增弹出框,传入‘新增’二字;批改弹出框,就传入‘批改’二字,顺次类推。

什么???不同弹出框有的宽一点有的高一点款式稍微不同,没关系,应用深度作用域选择器/deep/去设置就行啦

什么???不同弹出框点击确认按钮,最终的业务逻辑不一样,有的是减少一条数据、有的是删除一条数据等。没关系组件间的数据传递,事件的相干触发的语法记住就行啦

封装组件比拟罕用的技术之一就是插槽,在咱们封装dialog组件之前,咱们先来再温习一下插槽的相干常识。

为什么要有插槽

插槽api的诞生源自于vue数据传递的需要,因为平时咱们应用props父向子传递数据,传递的数据都是对象、数组、字符串等"js类型的数据"。当咱们想要传递大量的html类型的片段数据怎么办?

有这样的需要,于是插槽的技术就应运而生了。

组件化编程中,css不怎么须要传递,因为咱们能够通过深度作用域选择器,如/deep/去在父组件当选中子组件中的dom元素去设置款式

插槽的分类

  • 默认插槽(又叫:一般插槽、单个插槽,匿名插槽。即不带名字的,不必设置name属性 <slot></slot>这样的)
  • 具名插槽(带个名字的 <slot name="footer"></slot>这样的,领有name属性的)
  • 作用域插槽(这个是插槽的稍微高级点用法,就案例而言,在饿了么UI中的表格中有用到)

插槽能够在饿了么UI或者antD中封装的组件中看到,以el-dialog为例,其应用到了默认插槽和具名插槽。能够这样说,UI组件中基本上都应用了插槽技术,大家没事能够去看看饿了么UI的封装组件的源码,还是很有播种的

门路如下:在vue我的项目中,关上node_modules,外面有很多的包/组件,找到element-ui下的packages,外面都是饿了么封装的组件

作用域插槽本篇文章临时用不到,所以先按下不表,后续再独自写一篇对于作用域插槽的文章

匿名(默认)插槽的应用

子组件搁置匿名插槽

<template>  <div class="box">    <h1>我是子组件</h1>    <!-- 第一步,在子组件外面,找个中央插入一个槽,因为槽能够盛放货色,又因为子组件会引入到父组件中                所以子组件插入的这个插槽具体装什么货色,必定是由父组件去装货色,父组件装html片段,                在子组件中就能够失去html片段。所以:插槽实现了父组件向子组件传递数据的成果     -->    <slot></slot>    <!-- 子组件写了一个这样的slot标签能力接管到父组件传递过去的html片段。不写的话父组件传了也是白传            即不会生成DOM,也不会渲染     -->  </div></template><script>export default {  name: "DemoChildslot",};</script><style lang="less" scoped>.box {  width: 200px;  height: 200px;  background-color: #baf;}</style>

父组件应用匿名插槽传递HTML

<template>  <div id="app">    <!-- 第二步            应用子组件,在子组件标签两头写入代码,就能够实现插槽的数据传递了。 -->    <child-slot>      <i>我是父组件传递过来的</i>      <!--        也能够这样写,因为默认就是slot="default",不过个别不这样写,略麻烦        <i slot="default">我是父组件传递过来的</i>       -->    </child-slot>  </div></template><script>// 引入子组件import childSlot from "./childSlot";export default {  components: {    childSlot, // 注册子组件  },};</script><style lang="less" scoped>#app {  width: 100%;  min-height: 100vh;  box-sizing: border-box;  padding: 50px;}</style>

效果图

具名插槽的应用

比方咱们想要封装一个弹框组件,弹框弹出当前,在弹框的内容区,有弹框头部内容区、弹框次要内容区、弹框底部内容区。弹框次要内容区咱们能够应用匿名插槽、如果弹框要指定头部的内容、指定弹框的底部内容,能够通过具名插槽指定具体内容。构造代码如下:

子组件搁置具名插槽

<template>  <div class="box">    <h1>弹框</h1>    <div class="dialogHead">        <!-- 带名字的插槽,作用是指定插槽地位 -->        <slot name="headhead"></slot>    </div>    <div class="dialigMain">        <slot></slot>    </div>    <div class="dialogFoot">        <!-- 带名字的插槽,作用是指定插槽地位 -->        <slot name="footfoot"></slot>    </div>  </div></template><script>export default {  name: "DemoChildslot",};</script><style lang="less" scoped>.box {  width: 170px;  height: 200px;  background-color: #baf;}</style>

父组件中应用具名插槽传递HTML

<template>  <div id="app">    <child-slot>      <!-- 写法一:slot属性写在标签上              具名插槽在父组件中传递数据的时候地位无所谓,因为具名插槽的所传递的数据会依据具名插槽的名字              找对应子组件中的具名插槽。       -->      <!-- <p slot="footfoot">底部底部</p>      <i>我是父组件传递过来的</i>      <p slot="headhead">头部头部</p> -->      <!-- 写法二:slot属性写在模板标签上,模板标签不会渲染成DOM -->        <template slot="headhead">          <p>模板标签头部</p>        </template>        <i>我是父组件传递过来的</i>        <template slot="footfoot">          <p>模板标签底部</p>        </template>    </child-slot>  </div></template><script>// 引入子组件import childSlot from "./childSlot";export default {  components: {    childSlot, // 注册子组件  },};</script><style lang="less" scoped>#app {  width: 100%;  min-height: 100vh;  box-sizing: border-box;  padding: 50px;}</style>

效果图

开始封装dialog组件

性能如下

第一步就是封装组件并注册,这个步骤本篇文章就不赘述了,如果忘了步骤的敌人能够先看看我的这边文章,写的有全局组件的封装步骤,也蕴含组件间的数据传值。附上地址:https://segmentfault.com/a/11...

  • 弹出框弹出或敞开设置过渡动画
  • 应用默认插槽做次要内容区
  • 应用具名插槽指定对应弹框头部内容和弹框底部内容
  • 弹框遮罩层背景的开启或敞开
  • 点击遮罩层管制弹框的敞开与否
  • 是否显示敞开的叉号小图标
  • 自定义弹框的title题目
  • 等...

代码如下

封装的组件代码

<template>  <!-- 关上弹框的动画 -->  <transition name="animation">    <div      class="dialogBox"      :class="{ isShowMask: mask == true }"      v-show="isShowDialog"      @click="clickMaskCloseFn"    >      <div class="dialogBoxContent" @click.stop>        <div class="headhead">          <!-- 这样写能够做到若有传递过去的title就用传递过去的title          若有传递过去的插槽,就以插槽的为准 -->          <slot name="header">            <span>{{ title }}</span>          </slot>          <i class="el-icon-close" @click="close" v-show="showCloseIcon"> </i>        </div>        <div class="bodybody">          <!-- 内容区咱们应用默认插槽 -->          <slot></slot>        </div>        <div class="footfoot">          <!-- 底部应用命名插槽 -->          <slot name="footer"></slot>        </div>      </div>    </div>  </transition></template><script>export default {  name: "dialogComponent",  props: {    // 管制是否展现或暗藏对话框    isShowDialog: {      type: Boolean,      default: false,    },    // 父组件传过来的题目值    title: {      type: String,      default: "",    },    // 是否显示敞开小图标    showCloseIcon: {      type: Boolean,      default: true,    },    // 是否开启背景遮罩层    mask: {      type: Boolean,      default: true,    },    // 是否点击遮罩层mask敞开弹出框    clickMaskClose: {      type: Boolean,      default: false,      require:true    },  },  data() {    return {};  },  methods: {    // 敞开弹出框    close() {      this.$emit("beforeClose", false);    },    // 点击遮罩层敞开弹框    clickMaskCloseFn() {      if (this.clickMaskClose == true) {        this.$emit("beforeClose", false);      } else {        /* 这里要管制一下冒泡事件,留神第十行应用@click.stop           不管制冒泡的话,点击内容区也会导致弹出框敞开*/         return;      }    },  },};</script><style lang="less" scoped>.dialogBox {  width: 100%;  height: 100%;  position: fixed;  top: 0;  left: 0;  display: flex;  justify-content: center;  align-items: center;  .dialogBoxContent {    width: 600px;    height: 400px;    border: 2px solid #e9e9e9;    border-radius: 2px;    background-color: #fff;    .headhead {      width: 100%;      height: 60px;      line-height: 60px;      border-bottom: 1px solid #e9e9e9;      box-sizing: border-box;      padding: 20px;      display: flex;      justify-content: space-between;      align-items: center;      span {        font-size: 24px;      }      i {        font-size: 24px;        cursor: pointer;      }    }    .bodybody {      width: 100%;      height: calc(100% - 120px);    }    .footfoot {      width: 100%;      height: 60px;      line-height: 60px;      box-sizing: border-box;      border-top: 1px solid #e9e9e9;      padding: 0 20px;      .el-button {        margin-left: 12px;      }    }  }}.isShowMask {  background-color: rgba(0, 0, 0, 0.3);}.animation-enter,.animation-leave-to {  opacity: 0;}.animation-enter-active,.animation-leave-active {  transition: opacity 0.3s;}</style>

应用组件传参加事件代码

<template>  <div id="app">    <my-dialog      :isShowDialog.sync="isShowDialog"      title="设置题目"      :showCloseIcon="true"      @beforeClose="beforeClose"      :mask="true"      :clickMaskClose="true"    >      <!-- 要与组件的具名插槽对应 -->      <template slot="header"> 具名插槽 </template>      <template> 默认插槽 </template>      <!-- 要与子组件的插槽对应 -->      <template slot="footer">        <el-button size="small" @click="isShowDialog = false">勾销</el-button>        <el-button type="primary" size="small" @click="isShowDialog = false"          >确认</el-button        >      </template>    </my-dialog>    <br />    <br />    <br />    <el-button @click="isShowDialog = true" type="primary">      关上弹框    </el-button>  </div></template><script>export default {  data() {    return {      isShowDialog: false,    };  },  methods: {    // 敞开前的回调    beforeClose(flag) {      //   this.$confirm("是否敞开", "提醒", {      //     confirmButtonText: "确认敞开",      //     cancelButtonText: "勾销",      //     type: "warning",      //   })      //     .then(() => {      this.isShowDialog = flag;      // })      // .catch(() => {      //   // 不敞开      // });    },  },};</script><style lang="less" scoped>#app {  width: 100%;  min-height: 100vh;  box-sizing: border-box;  padding: 50px;}</style>

最终效果图

总结

代码中正文写的比拟全了,比拟不便的形式就是新建一个vue的我的项目,而后把代码别离粘过来,留神封装的myDialog组件别忘了要注册一下哦

本文论述封装组件化插槽应用流程思维,具体业务场景灵便批改就行啦。