基于element-ui封装组件库,我想要实现的效果是:
开发者想要正常使用element的普通组件时,可以正常按需引入,正常使用。
另外想要使用本组件库二次封装的组件时,也能按需引入使用。
接着之前的文章,现在开始二次封装一个element的dialog组件。我希望它能有默认的头部和底部。并且使用方法和element-ui的dialog一样。
一,二次封装Dialog:
先写package/Dialog/index.js,为按需引入做准备:
import BiuDialog from "./src";
BiuDialog.install = function (Vue) {
Vue.component(BiuDialog.name, BiuDialog);
};
export default BiuDialog;
然后编写dialog组件的封装代码:package/Dialog/src/index.vue:
<template>
<div class="biu-dialog-box">
<el-dialog
:custom-class="customClass"
:title="$slots.title ? '' : title"
:visible.sync="show"
:width="width"
:top="top"
:append-to-body="appendToBody"
:modal="modal"
:fullscreen="fullscreen"
:destroy-on-close="destroyOnClose"
:modal-append-to-body="modalAppendToBody"
:before-close="beforeClose"
:close-on-click-modal="closeOnClickModal"
:show-close="false"
@open="open"
@opened="opened"
@close="close"
@closed="closed"
>
<template v-if="$slots.title">
<span slot="title">
<slot name="title" />
</span>
</template>
<template v-if="!$slots.title">
<div slot="title" class="biu-default-header-box">
<div class="biu-default-header-title">{{ title }}</div>
<div
class="biu-default-header-close"
@click="beforeClose2"
v-if="showClose"
>
<span class="biu-icon-guanbi2"></span>
</div>
</div>
</template>
<slot />
<template v-if="$slots.footer">
<span slot="footer">
<slot name="footer" />
</span>
</template>
<template v-if="!$slots.footer">
<div slot="footer" class="biu-default-header-box">
<el-button class="btn" @click="cancel">取消</el-button>
<el-button class="btn sure" @click="submit">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
name: "biuDialog",
props: {
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "提示",
},
appendToBody: {
type: Boolean,
default: true,
},
modalAppendToBody: {
type: Boolean,
default: true,
},
modal: {
type: Boolean,
default: true,
},
fullscreen: {
type: Boolean,
default: false,
},
destroyOnClose: {
type: Boolean,
default: true,
},
width: {
type: String,
default: "30%",
},
top: {
type: String,
default: "15vh",
},
customClass: {
type: String,
default: "biu-dialog",
},
showClose: {
type: Boolean,
default: false,
},
closeOnClickModal: {
type: Boolean,
default: true,
},
beforeClose: {
type: Function,
},
},
computed: {
show: {
get() {
return this.visible;
},
set(val) {
console.log(val);
this.$emit("update:visible", val);
},
},
},
data() {
return {};
},
methods: {
beforeClose2() {
this.beforeClose(() => {
this.show = false;
});
},
open() {
this.$emit("open");
},
opened() {
this.$emit("opened");
},
close() {
this.$emit("close");
},
closed() {
this.$emit("closed");
},
cancel() {
this.$emit("cancel");
},
submit() {
this.$emit("submit");
},
},
};
</script>
<style scoped lang="scss">
:deep(.el-dialog) {
min-width: 320px;
.el-dialog__header {
padding: 0;
color: #d37332;
font-weight: 500;
height: 50px;
display: flex;
flex-direction: column;
justify-content: center;
border-bottom: 2px solid #e9e8e8;
font-size: 14px;
.biu-default-header-box {
padding: 0 20px;
display: flex;
line-height: 20px;
.biu-default-header-title {
flex: 1;
}
.biu-default-header-close {
width: 15px;
height: 15px;
}
}
}
.el-dialog__footer {
padding: 0;
text-align: center;
height: 88px;
border-top: 2px solid #e9e8e8;
display: flex;
justify-content: center;
flex-direction: column;
.btn {
width: 120px;
height: 40px;
background: #e9e8e8;
border-radius: 2px;
}
.sure {
color: #ffffff;
background: #de9a6c;
margin-left: 66px;
}
}
}
</style>
二,封装的注意点
1,无需变化的props的透传
参考element的dialog的props,因为封装后,我希望开发者照着element的文档也能开发,而不需要额外的学习,所以一些不需要变更的props可以透传出来:
<el-dialog
:width="width"
:top="top"
>
</el-dialog>
props: {
width: {
type: String,
default: "30%",
},
top: {
type: String,
default: "15vh",
},
},
这样子,使用组件时就可以和element的dialog一样,使用width和top属性:
<biu-dialog
:visible.sync="dialogshow"
title="标题"
width="800px"
top="100px"
></biu-dialog>
2,visible属性的处理
在element的文档中,这个属性是这样用的:
:visible.sync="show"
也就是dialog组件内部修改了传入的visible属性时,会直接触发更新父组件的show的值(这里用show命名是为了避免命名重复)
而我们使用biu-dialog组件的时候,按照element文档,也是需要属性绑定传值的,为了保持一致,就不能使用props(单向数据流,不修改父组件的dialogshow属性),而是应该用computed做一层代理:
computed: {
show: {
get() {
return this.visible;
},
set(val) {
console.log(val);
this.$emit("update:visible", val);
},
},
},
然后父组件的使用:
<biu-dialog
:visible.sync="dialogshow"
></biu-dialog>
this.$emit(“update:visible”, val)会直接变更dialogshow的值。
这样biu-dialog中就不需要定义修改dialogshow的函数了。
3,头部使用自定义的头部
<el-dialog
:title="$slots.title ? '' : title"
:visible.sync="show"
:show-close="false"
>
<template v-if="$slots.title">
<span slot="title">
<slot name="title" />
</span>
</template>
<template v-if="!$slots.title">
<div slot="title" class="biu-default-header-box">
<div class="biu-default-header-title">{{ title }}</div>
<div
class="biu-default-header-close"
@click="beforeClose2"
v-if="showClose"
>
<span class="biu-icon-guanbi2"></span>
</div>
</div>
</template>
<slot />
</el-dialog>
主要是通过$slots.title判断开发者在使用组件时有没有传入头部插槽组件,没有的话,就采用自定义的头部,底部的话,也是同样的道理。
4,样式的修改
可以注意到的是封装后的弹窗的html:
虽然说可以直接修改我们自定义的部分结构的样式,但是如果我们想要修改element组件内置的样式,则需要样式穿透处理一下:
<style scoped lang="scss">
:deep(.el-dialog) {
min-width: 320px;
.el-dialog__header {
//其他样式
}
.el-dialog__footer {
//其他样式
}
}
</style>
三,文档的编写
1,文档模块中引入element和我们封装的组件库
这个在之前的文章已经说过,需要在docs/.vuepress/enhanceApp.js文件夹下注册引入组件库:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import BiuUi from "../../packages/index.js";
export default async ({
Vue
}) => {
if (typeof process === 'undefined') {
Vue.use(ElementUI)
Vue.use(BiuUi);
}
}
2,新增dialog文档
3,文档运行的效果
四,用户的使用
1,全局引入element
npm install element-ui --save--dev
用户在项目代码中需要全局引入element-ui,或者按需引入我们使用到的el-dialog和el-button组件:
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
Vue.use(ElementUI);
2,项目中引入本组件库:
先npm安装之后,项目入口文件引入:
import BiuUi from "biu-ui";
Vue.use(BiuUi);
3,页面使用
直接参考element的文档即可。
<template>
<div id="app">
<div>
<i class="biu-icon-basic_qrcode"></i>
<biu-dialog
:visible.sync="dialogshow"
title="标题"
:showClose="true"
:before-close="handleClose"
@cancel="cancel"
@submit="submit"
></biu-dialog>
</div>
</div>
</template>
<script>
export default {
name: "App",
components: {},
data() {
return {
dialogshow: true,
};
},
methods: {
handleClose(done) {
this.$confirm("确认关闭?")
.then(() => {
done();
})
.catch(() => {});
},
cancel() {
this.dialogshow = false;
},
submit() {
console.log("点击确定");
this.dialogshow = false;
},
},
};
</script>
<style></style>
效果:
|