首页 > 其他分享 >vue的组件封装

vue的组件封装

时间:2023-01-06 18:34:06浏览次数:43  
标签:box vue 封装 插槽 弹框 dialog 组件 height

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

 

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


好处显而易见,可以增加代码的复用性、灵活性,从而提高开发效率。试想如果一个项目中在很多页面都能用到一个弹出框,若在每个页面都去写一套弹出框的结构样式和对应js的逻辑。这样的话,开发效率会大打折扣; 当然现在有很多优秀的组件库.不过我们只是通过这个案例来熟悉组件的封装

 

组件的封装目前用到的三个技能点:


1父往子传值
2.子往父传值
3.插槽技术 (文末有介绍)
另外在本文中还有一些琐碎知识点: 属性修饰符.sync 能够让我们子组件修改父组件传递过来的属性
时间修饰符 .self .stop
vue里面的内置动画标签 <transition>
父组件修改子组件里面的数据

 

二、dialog组件结构搭建



dialog对话框,整体有一个动画效果,vue的动画效果,使用transition包裹需要动画展示的元素,那么这个元素在显示/隐藏时自动添加一些类名,此例详见后续代码

 

需求:

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

分析:
对话框分为三部分:
1、头部:左侧为标题,使用具名插槽 title 占位,右侧为按钮/图标(关闭)
2、主体内容,使用不具名的插槽占位
3、底部:一般都是一些操作,使用具名插槽 footer 占位,通常内容是取消/确认按钮

需要传入的参数:
title:头部标题
width:对话框宽度(我觉得这里也可以在父级用deep指定)
top:对话框距离顶部的距离
visible:对话框的显示/隐藏

 

具体实现代码:
1.在src根目录下面的components里面新建一个Dialog.vue文件

<template>
  <!-- 打开弹框的动画 -->
  <!-- vue的内置动画标签,会根据name属性,形成6个样式
    动画进入的三个状态
    dialog-fade-enter   准备进入
    dialog-fade-enter-active  进入的过程中
    dialog-fade-enter-to  动画进入结束

    dialog-fade-leave    动画准备离开
     dialog-fade-leave-active 动画离开过程中
      dialog-fade-leave-to 动画离开完成
    -->
  <transition name="dialog-fade">
    <!-- div整个屏幕的半透明阴影 -->
    <div
      class="dialogBox"
      :class="{ isShowMask: mask == true }"
      v-show="isShowDialog"
      @click.self="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">X </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
    }
  },
  data () {
    return {}
  },
  methods: {
    // 关闭弹出框
    close () {
      this.$emit('update:isShowDialog', false)
    },
    // 点击遮罩层关闭弹框
    clickMaskCloseFn () {
      if (this.clickMaskClose === true) {
        this.$emit('update:isShowDialog', false)
      } else {
        /* 这里要控制一下冒泡事件,注意第十行使用@click.stop
           不控制冒泡的话,点击内容区也会导致弹出框关闭 */
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.dialogBox {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  .dialogBoxContent {
    width: 500px;
    height: 220px;
    border: 2px solid #e9e9e9;
    border-radius: 20px;
    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);
}
.dialog-fade-enter,
.dialog-fade-leave-to {
  opacity: 0;
}
.dialog-fade-enter-active,
.dialog-fade-leave-active {
  transition: opacity 0.3s;
}

// 还可以这样定义动画哦
// 进入动画
// .dialog-fade-enter-active {
//   animation: dialog-fade-in 0.4s;
// }
// // 离开动画
// .dialog-fade-leave-active {
//   animation: dialog-fade-out 0.4s;
// }

// @keyframes dialog-fade-in {
//   0% {
//     transform: translate3d(0, -20px, 0);
//     opacity: 0;
//   }
//   100% {
//     transform: translate3d(0, 0, 0);
//     opacity: 1;
//   }
// }

// @keyframes dialog-fade-out {
//   0% {
//     transform: translate3d(0, 0, 0);
//     opacity: 1;
//   }
//   100% {
//     transform: translate3d(0, -20px, 0);
//     opacity: 0;
//   }
// }
</style>

 


2全局注册组件(main.js)

import NewDialog from '@/components/Dialog'
Vue.component('myDialog', NewDialog)

 


3.业务组件中的调用

<!--
      sync:事件修饰符,是一个语法糖写法,实现子组件修改父组件传入的props
      父:visible.sync="visible"
      子:this.$emit("update:visible", false)
    -->
    <my-dialog
      :isShowDialog.sync="isShowDialog"
      title="设置标题"
      :showCloseIcon="true"
      :mask="true"
      :clickMaskClose="true"
    >
      <!-- 要与组件的具名插槽对应 -->
      <template #header> 具名插槽 </template>
      <template> 默认插槽 </template>
      <!-- 要与子组件的插槽对应 -->
      <template #footer>
        <van-button size="small" @click="isShowDialog = false">取消</van-button>
        <van-button type="primary" size="small" @click="isShowDialog = false"
          >确认</van-button
        >
      </template>
    </my-dialog>
    <van-button @click="isShowDialog = true" type="primary">模态框</van-button>

 

 

三、知识点回顾总结

 

为什么要有插槽


插槽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>

 

效果图

 

 

 

标签:box,vue,封装,插槽,弹框,dialog,组件,height
From: https://www.cnblogs.com/z-bky/p/17031314.html

相关文章

  • vue项目服务器部属源码调试解决办法
    一、问题来源   希望在远程发布的测试服务器上直接启用vscode 的调试模试,来解决项目实际部属时的问题。也就是在调试模式下,会有子域名的问题。二、如何在调试模......
  • 如何封装一个vue的插件
    首先在toast文件夹去创建一个index.js文件,在js文件中创建一个对象,将他导出//index.jsconsttoast={}exportdefaulttoast  在main.js中导入这个文件,并且use......
  • 第一节:环境准备、项目结构详解、App.vue/main.js/uni.scss详解、各种引用(组件/js/css
    一. 环境准备1. HbuilderX 下载地址:https://www.dcloud.io/hbuilderx.html 2. 微信开发工具 下载地址:https://developers.weixin.qq.com/miniprogram/dev......
  • vue导航触发流程
    导航被触发。在失活的组件里调用beforeRouteLeave守卫。调用全局的beforeEach守卫。在重用的组件里调用beforeRouteUpdate守卫(2.2+)。在路由配置里调用befor......
  • vue3+vite配置多页面
    通过配置多页面应用,从而将给子模块依赖分隔开各自加载,可以减少初始资源的请求,加快页面的访问速度。比如我们有很多H5页面,并且相互独立,比如报修,购卡,计价规则等等,那我们如......
  • vite+vue3项目在chrome中debuger源码
    在vue文件的script中打上一个debugger,本以为可以开心的debugger了,结果控制台看到的是这个样子查了一些方案,这样就可以解决了......
  • python + selenium 常用公共方法封装
     selenium环境配置及浏览器驱动的安装:https://www.cnblogs.com/gancuimian/p/16435300.htmluiautomator2常用公共方法封装见之前的帖子:https://www.cnblogs.com/gancu......
  • uni-app HBuilderX 工程转换 vue-cli 工程
    1、使用vue-cli4新建空工程vuecreate-pdcloudio/uni-preset-vuemy-project地址不行就直接下载,使用本地地址2、将HBuilderX工程内的文件(除unpackage、node......
  • vue3+ts利用el-table实现可编辑的表格
    说明在对表格数据进行操作时,如果数据项比较少,可通过自定义实现直接在表格中编辑。界面展示实现要点使用slot来自定义单元格,实现输入、选择等操作使用slot来自定义表......
  • vue 如何处理列表中展示进度的问题
      <template><divref="columnarStrip"class="columnarStrip"><divv-if="normal()"class="columnBox"><div:style="{width:widthPercent}"cl......