Vue2:如何不使用el-dialog而实现一个更轻量弹窗
在使用Vue2进行开发时,Element UI提供的el-dialog组件是一个常见的弹窗解决方案。但是,有时我们可能需要一个更轻量、更可定制化的弹窗组件。这篇文章将介绍如何不使用el-dialog,而是通过自定义组件来实现一个弹窗。
1、需求分析
在开始编写代码之前,我们需要明确弹窗的基本需求:
- 可见性控制:弹窗需要有显示和隐藏的功能。
- 关闭功能:弹窗需要能够通过点击关闭按钮或点击背景区域来关闭。
- 标题和内容区域:弹窗需要有一个标题区域和一个内容区域。
- 动画效果:弹窗的显示和隐藏需要有过渡动画效果。
- 参数传递:弹窗组件应该能够接受参数,以便动态配置内容和行为。
2、设计思路
实现自定义弹窗的核心思路包括以下几个方面:
- 定义弹窗组件:创建一个 Vue 组件来实现弹窗的基本结构和样式。
- 控制弹窗的显示与隐藏:通过组件的数据和过渡效果控制弹窗的显示与隐藏。
- 实现弹窗的动态创建与销毁:通过 JavaScript 动态创建和销毁弹窗实例,以支持弹窗的重复使用。
- 封装弹窗的调用接口:提供一个全局的调用接口,使得使用者可以方便地创建和管理弹窗。
3、实现步骤
3.1、创建弹窗组件
- 首先,我们需要定义一个基本的弹窗组件。以下是一个 Vue 组件的示例,它实现了弹窗的基本结构、样式以及关闭功能。
- alert.vue
<template>
<transition name="el-fade-in-linear">
<div
v-show="visible"
:id="id"
:style="alertStyle"
class="alert"
:class="alertClass"
@click.self="!isClickNotClose ? close() : ''"
>
<div
:style="{ width: alertWidth, height: alertHeight }"
class="alert__body"
>
<div v-if="showHeader" class="alert__body__title">
{{ alertTitle }}
<span class="el-icon-close" @click="close"></span>
</div>
<component
:is="component"
:params="params"
class="alert__body__main"
/>
</div>
</div>
</transition>
</template>
<script>
export default {
data() {
return {
// 控制弹窗是否可见
visible: false,
// 关闭弹窗的回调函数
onClose: null,
// 控制弹窗是否已关闭
closed: false
};
},
watch: {
closed(newVal) {
if (newVal) {
this.visible = false;
// 使用 `addEventListener` 方法向组件的根元素 (`$el`) 添加一个 `transitionend` 事件监听器。
// 监听器回调函数是 `this.destroyElement`,这意味着当 CSS 过渡结束时,会调用 `destroyElement` 方法。
this.$el.addEventListener('transitionend', this.destroyElement);
}
}
},
methods: {
close() {
this.closed = true;
if (typeof this.onClose === 'function') {
this.onClose(this);
}
}
},
mounted() {
// 给传入component的params赋一个close函数,以供component调用关闭
this.params.close = this.close;
}
};
</script>
<style lang="less">
.alert-fixed(@z-index) {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: @z-index;
}
/* 弹窗样式 */
.alert {
.alert-fixed(200);
top: 0;
background: rgba(0, 0, 0, 0.4);
overflow-y: auto;
&__body {
flex: none;
display: flex;
flex-direction: column;
max-height: 700px;
overflow-y: hidden;
background-color: white;
font-size: 14px;
// padding:24px;
box-sizing: border-box;
border-radius: 4px;
width: 0;
margin: 100px auto;
transition: width 0.5s;
&__title {
flex: none;
font-weight: bold;
color: #666;
width: 100%;
font-size: 16px;
box-sizing: border-box;
// margin-bottom:24px;
padding: 16px 24px;
border-bottom: 2px solid #d8dbdf;
span {
font-size: 20px;
color: #c4c6c9;
float: right;
cursor: pointer;
&:hover {
color: #666;
}
}
}
&__main {
flex: auto;
padding: 24px 24px 24px;
overflow-y: auto;
max-height: 600px;
}
& .alert-footer__btns {
.alert-fixed(9);
background-color: white;
height: 80px;
line-height: 80px;
border-top: 1px solid #c4c6c9;
text-align: center;
& .btn-plain {
font-family: PingFangSC-Medium;
font-size: 12px;
color: #c4c6c9;
letter-spacing: 0;
margin-right: 25px;
cursor: pointer;
}
& .btn {
width: 213px;
box-sizing: border-box;
border-radius: 100px;
font-family: PingFangSC-Medium;
letter-spacing: 0;
padding: 6px 12px;
}
& .btn-primary {
color: white;
}
}
}
}
</style>
3.2 控制弹窗的显示与隐藏
在上面的组件中,我们通过 v-show
指令控制弹窗的显示与隐藏。visible
数据属性用于控制弹窗的可见性。当需要关闭弹窗时,我们设置 closed
为 true
,触发 CSS 过渡效果,并在过渡结束后销毁弹窗元素。
3.3 实现弹窗的动态创建与销毁
-
我们可以通过一个工厂函数动态创建和销毁弹窗实例。以下是一个示例实现,它包括了弹窗的创建、显示、关闭以及销毁的处理逻辑。
-
main.js
import Alert from './alert.vue';
let instance; // 保存当前正在创建的对话框实例
const instances = []; // 保存所有已创建的对话框实例。
let seed = 1; // 用于生成唯一ID的计数器。
const DialogAlert = function (Vue) {
/**
* 生成弹窗实例的函数。
* @param {Object} options - 弹窗的配置选项。
* @returns {Object} 创建的弹窗实例。
*/
const genFunc = function (options) {
// 扩展 Vue 构造函数创建 Alert 组件实例。
const DialogAlertConstructor = Vue.extend(Alert);
// 如果在服务器端,则不进行后续操作。
if (Vue.prototype.$isServer) return;
// 初始化选项,默认值。
options = options || {};
options.isClickNotClose = options.isClickNotClose || false;
options.alertWidth = options.alertWidth || '350px';
options.alertHeight = options.alertHeight || '550px';
options.showHeader =
typeof options.showHeader === 'boolean' ? options.showHeader : true;
options.alertTitle = options.alertTitle || '';
options.alertStyle = options.alertStyle || {};
options.alertClass = options.alertClass || '';
options.params = options.params || {};
const userOnClose = options.onClose;
const id = options.alertId || 'dialog_' + seed++;
// 关闭弹窗的处理函数。
options.onClose = function () {
genFunc.close(id, userOnClose);
};
// 创建并挂载 Alert 组件实例。
instance = new DialogAlertConstructor({
data: options
});
instance.id = id;
// 挂载组件,实例的 `vm` 属性被赋值为调用 `$mount` 方法的结果。这意味着实例实际上是一个 Vue 组件,`$mount` 方法用于将该组件挂载到一个新的 DOM 元素上。
instance.vm = instance.$mount();
// 将挂载好的组件的根元素(`vm.$el`)添加到文档的 `body` 中。这使得组件在页面上可见。
document.body.appendChild(instance.vm.$el);
// 修改文档 `body` 的 `overflow` 样式属性为 `hidden`。这通常是为了防止滚动条出现,可能是因为弹出框或模态窗口覆盖了整个屏幕。
document.body.style.overflow = 'hidden';
// 显示组件
instance.vm.visible = true;
// 将 `instance.vm.$el` 赋值给 `instance.dom`,这样可以在后续操作中直接访问组件的根 DOM 元素。
instance.dom = instance.vm.$el;
// 存储实例
instances.push(instance);
// 返回挂载后的Vue实例,供外部控制和使用
return instance.vm;
};
/**
* 关闭指定 ID 的弹窗实例。
* @param {string} id - 弹窗的唯一标识。
* @param {Function} userOnClose - 用户定义的关闭回调函数。
*/
genFunc.close = function (id, userOnClose) {
for (let i = 0, len = instances.length; i < len; i++) {
if (id === instances[i].id) {
if (typeof userOnClose === 'function') {
userOnClose(instances[i]);
}
instances[i].vm.$destroy();
document.body.style.overflow = 'auto';
instances.splice(i, 1);
document
.querySelector('#' + id)
.parentNode.removeChild(document.querySelector('#' + id));
break;
}
}
};
/**
* 关闭所有弹窗实例。
*/
genFunc.closeAll = function () {
for (let i = instances.length - 1; i >= 0; i--) {
instances[i].close();
}
};
return genFunc;
};
export default DialogAlert;
3.4 封装弹窗的调用接口
为了方便调用,我们将弹窗的创建方法封装到一个全局函数中,使得用户可以通过简单的接口来创建和管理弹窗。以下是如何在 Vue 实例中使用这个全局函数的示例:
- index.js
import DialogAlert from './main.js';
DialogAlert.install = function(Vue) {
Vue.component(DialogAlert.name, DialogAlert);
};
export default DialogAlert;
- main.js
import DialogAlert from './components/dialog-alert';
Vue.prototype.$dialogAlert = DialogAlert(Vue);
// 调用示例
this.$dialogAlert({
params: {
callBack: (param) => {
console.log(param);
}
},
component: () => import('./layout.vue'),
alertWidth: '600px',
alertHeight: 'auto',
alertTitle: '标题',
});
通过这种方式,我们可以灵活地创建和管理自定义弹窗,而不依赖于 el-dialog
。这个实现方案提供了自定义弹窗的完整功能,包括内容展示、动画效果、关闭逻辑以及全局调用接口。
4、 总结
自定义弹窗组件的实现可以帮助我们更好地满足特定的需求和设计要求。通过 Vue 的强大功能,我们能够创建出灵活、可定制的弹窗组件,并且通过动态创建和销毁的方式来优化资源的使用。希望这篇文章能为你在实现自定义弹窗时提供一些有价值的思路和实践经验。
5、$dialogAlert 参数说明
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
component | 必须 vue组件对象,该对象必须要声明一个名称为params 的prop,具体模板代码见下方 | VueComponent | -- | -- |
params | 可选 可在component中通过param访问的对象。注意,对象的close 属性已被占用,该close方法用于在component中直接关闭弹窗 | Object | -- | -- |
isClickNotClose | 可选 默认情况下,点击弹窗以外的页面会关闭弹窗,该属性为true时点击弹窗以外的页面不会关闭弹窗 | Boolean | true/false | false |
showHeader | 可选 是否显示头,默认显示 | Boolean | true/false | true |
alertWidth | 可选 弹窗的宽度 | String | 有效的css宽度值 | 350px |
alertHeight | 可选 弹窗的高度度 | String | 有效的css高度值 | 550px |
alertTitle | 可选 弹窗的标题 | String | -- | -- |
alertClass | 可选 给弹窗添加类名 | String | -- | -- |
alertStyle | 可选 弹窗的样式 | |||
Object | 类似于html节点的style属性,但以json形式书写 | -- | ||
onClose | 可选 处理弹窗关闭事件 | Function(inst:VueInstance) | -- | -- |
转载自:https://juejin.cn/post/7397028630209413171