共计 6317 个字符,预计需要花费 16 分钟才能阅读完成。
封装 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 组件别忘了要注册一下哦
本文论述封装组件化插槽应用流程思维,具体业务场景灵便批改就行啦。