尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Element UI Dialog 对话框组件完全指南:从基础用法到源码级原理

Element UI Dialog 对话框组件完全指南:从基础用法到源码级原理 Element UI Dialog 对话框组件完全指南从基础用法到源码级原理【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/element导读el-dialog是 Element UIVue.js 2.0 UI Toolkit中最常用的弹层组件之一用于在保留当前页面状态的情况下告知用户并承载相关操作。本文将基于 Element UI 官方中文文档 的完整内容结合 对话框组件源码、Popup 弹层混入 与 PopupManager 弹出层管理器 等仓库实现系统讲解 Dialog 的 18 个配置属性、3 个具名插槽、4 个生命周期事件并深入剖析visible.sync双向绑定、懒渲染、before-close拦截、遮罩层级管理zIndex 栈与 ESC 关闭等底层原理。读完本文你将能熟练地在业务中落地各类 Dialog 场景表格、表单、嵌套、确认关闭并理解其弹层机制为何如此设计。基本用法visible驱动的开关式弹窗Dialog 弹出一个对话框适合需要定制性更大的场景。它的显示与隐藏完全由一个Boolean类型的visible属性驱动——当它为true时显示 Dialog为false时隐藏。el-button typetext clickdialogVisible true点击打开 Dialog/el-button el-dialog title提示 :visible.syncdialogVisible width30% :before-closehandleClose span这是一段信息/span span slotfooter classdialog-footer el-button clickdialogVisible false取 消/el-button el-button typeprimary clickdialogVisible false确 定/el-button /span /el-dialog script export default { data() { return { dialogVisible: false }; }, methods: { handleClose(done) { this.$confirm(确认关闭) .then(_ { done(); }) .catch(_ {}); } } }; /script结构上Dialog 分为两个部分body和footer。body是默认插槽slot的内容footer需要具名为footer的插槽。title属性用于定义标题它是可选的默认值为空字符串。关于before-close的一个关键注意点提示before-close仅当用户通过点击关闭图标或遮罩关闭 Dialog 时起效。如果你在footer具名 slot 里添加了用于关闭 Dialog 的按钮那么可以在按钮的点击回调函数里加入before-close的相关逻辑。这一点在源码中体现得很清晰handleClose()是唯一会调用beforeClose的入口component.vue而它由两个来源触发——关闭图标按钮的clickhandleClose和遮罩层上的click.selfhandleWrapperClick后者在closeOnClickModal为真时继续调用handleClose。而 footer 里的按钮只是普通按钮点击后直接修改dialogVisible并不会走before-close流程因此需要开发者自己在回调中执行确认逻辑。为什么before-close能暂停关闭因为handleClose的关闭动作被设计成延迟执行当存在beforeClose函数时组件不会立即关闭而是把真正关闭的能力以done即内部hide方法的形式交给你由你在异步回调如$confirm的 Promise里决定何时调用done()完成关闭handleClose() { if (typeof this.beforeClose function) { this.beforeClose(this.hide); // 把 hide 作为 done 传出 } else { this.hide(); } }对应仓库单元测试 before close 验证了点击 Dialog 时beforeClose回调会被调用且调用done()后弹窗正常关闭。自定义内容Dialog 里放表格与表单Dialog 组件的内容可以是任意的甚至可以是表格或表单。官方文档给出了应用 Element Table 和 Form 组件的两个样例!-- Table -- el-button typetext clickdialogTableVisible true打开嵌套表格的 Dialog/el-button el-dialog title收货地址 :visible.syncdialogTableVisible el-table :datagridData el-table-column propertydate label日期 width150/el-table-column el-table-column propertyname label姓名 width200/el-table-column el-table-column propertyaddress label地址/el-table-column /el-table /el-dialog !-- Form -- el-button typetext clickdialogFormVisible true打开嵌套表单的 Dialog/el-button el-dialog title收货地址 :visible.syncdialogFormVisible el-form :modelform el-form-item label活动名称 :label-widthformLabelWidth el-input v-modelform.name autocompleteoff/el-input /el-form-item el-form-item label活动区域 :label-widthformLabelWidth el-select v-modelform.region placeholder请选择活动区域 el-option label区域一 valueshanghai/el-option el-option label区域二 valuebeijing/el-option /el-select /el-form-item /el-form div slotfooter classdialog-footer el-button clickdialogFormVisible false取 消/el-button el-button typeprimary clickdialogFormVisible false确 定/el-button /div /el-dialogbody区域在模板中对应.el-dialog__bodycomponent.vue样式上带有word-break: break-all可容纳任意复杂内容。从源码看footer区域只有在存在具名插槽$slots.footer时才渲染v-if$slots.footer因此不传 footer 时底部区域不会占用 DOM 空间。嵌套的 Dialog必须配合append-to-body正常情况下官方不建议使用嵌套的 Dialog如果需要在页面上同时显示多个 Dialog可以将它们平级放置。对于确实需要嵌套 Dialog 的场景Element 提供了append-to-body属性将内层 Dialog 的该属性设置为true它就会插入至body元素上从而保证内外层 Dialog 和遮罩层级关系的正确。template el-button typetext clickouterVisible true点击打开外层 Dialog/el-button el-dialog title外层 Dialog :visible.syncouterVisible el-dialog width30% title内层 Dialog :visible.syncinnerVisible append-to-body /el-dialog div slotfooter classdialog-footer el-button clickouterVisible false取 消/el-button el-button typeprimary clickinnerVisible true打开内层 Dialog/el-button /div /el-dialog /template为什么嵌套时必须用append-to-body这与两个因素有关zIndex 栈管理所有弹层统一由 PopupManager 管理弹层与遮罩的 z-index 通过全局递增的nextZIndex()分配初始值来自Vue.prototype.$ELEMENT.zIndex || 2000见 popup-manager.js遮罩之间通过modalStack栈维护后打开的盖住先打开的。DOM 树位置与 fixed 定位Dialog 外层容器.el-dialog__wrapper是position: fixed但若内层 Dialog 挂在外层 Dialog 内部而外层又带有overflow或 transform 相关上下文fixed 定位与层级关系都可能被打乱。将内层 Dialog 通过append-to-body直接挂到document.body上component.vue可以脱离父级影响让两个 Dialog 作为平级弹层被 zIndex 栈正确排序。对应的测试 append to body 直接断言了dialog.$el.parentNode document.body。注意append-to-body模式下组件销毁时还会主动把 DOM 节点从 body 移除见 component.vue。居中布局center只影响标题与底部标题和底部可水平居中。将center设置为true即可el-dialog title提示 :visible.synccenterDialogVisible width30% center span需要注意的是内容是默认不居中的/span span slotfooter classdialog-footer el-button clickcenterDialogVisible false取 消/el-button el-button typeprimary clickcenterDialogVisible false确 定/el-button /span /el-dialog提示center仅影响标题和底部区域。Dialog 的内容是任意的在一些情况下内容并不适合居中布局。如果需要内容也水平居中请自行为其添加 CSS。从样式源码看.el-dialog--center只设置了text-align: center并让body恢复text-align: initial内容不居中、footer 继承居中dialog.scss与文档说明完全一致。两个重要的使用提示懒渲染与 Vuex 场景下的.syncDialog 内容是懒渲染的提示Dialog 的内容是懒渲染的即在第一次被打开之前传入的默认 slot 不会被渲染到 DOM 上。因此如果需要执行 DOM 操作或通过ref获取相应组件请在open事件回调中进行。这是组件设计中一个非常实用的细节模板中.el-dialog__body用v-ifrendered控制component.vue而rendered只有在visible变为true或组件mounted时visible已为真才置为truecomponent.vue、component.vue。rendered一旦为真便不再复位因此懒渲染指的是首次打开前不渲染之后保持渲染。首次打开前用this.$refs.xxx取内部组件会拿到空值正确做法是监听open事件后再操作。Vuex store 中的visible与.sync的冲突提示如果visible属性绑定的变量位于 Vuex 的 store 内那么.sync不会正常工作。此时需要去除.sync修饰符同时监听 Dialog 的open和close事件在事件回调中执行 Vuex 中对应的 mutation 更新visible属性绑定的变量的值。原因在于.sync修饰符的本质子组件通过$emit(update:visible, false)通知父组件更新见hide方法中的this.$emit(update:visible, false)component.vue父组件对应地执行visible false的赋值。但 Vuex 严格模式下不允许直接修改 store 状态赋值会被strict: true校验拦截。此时应改为el-dialog :visibledialogVisible openhandleOpen closehandleClosemethods: { handleOpen() { this.$store.commit(SET_DIALOG_VISIBLE, true); }, handleClose() { this.$store.commit(SET_DIALOG_VISIBLE, false); } }Attributes完整参数详解以下参数表完整继承自官方文档并补充了源码中的默认值与实现细节可对照 component.vue 的 props 定义验证参数说明类型可选值默认值visible是否显示 Dialog支持.sync修饰符boolean—falsetitleDialog 的标题也可通过具名 slot见下表传入string——widthDialog 的宽度string—50%fullscreen是否为全屏 Dialogboolean—falsetopDialog CSS 中的 margin-top 值string—15vhmodal是否需要遮罩层boolean—truemodal-append-to-body遮罩层是否插入至 body 元素上若为 false则遮罩层会插入至 Dialog 的父元素上boolean—trueappend-to-bodyDialog 自身是否插入至 body 元素上。嵌套的 Dialog 必须指定该属性并赋值为 trueboolean—falselock-scroll是否在 Dialog 出现时将 body 滚动锁定boolean—truecustom-classDialog 的自定义类名string——close-on-click-modal是否可以通过点击 modal 关闭 Dialogboolean—trueclose-on-press-escape是否可以通过按下 ESC 关闭 Dialogboolean—trueshow-close是否显示关闭按钮boolean—truebefore-close关闭前的回调会暂停 Dialog 的关闭function(done)done 用于关闭 Dialog——center是否对头部和底部采用居中布局boolean—falsedestroy-on-close关闭时销毁 Dialog 中的元素boolean—false关键参数与源码实现的对应关系width / top / fullscreen三者共同参与style计算component.vue。非全屏时设置margin-top top且仅当显式传入width时才覆盖默认的50%全屏时则完全不设置 marginTop 与 width由样式类.is-fullscreen撑满width: 100%; height: 100%dialog.scss。测试 fullscreen 验证了全屏时 width 不生效。modal / modal-append-to-body / lock-scroll这些属于 Popup 混入 的能力。开启modal时PopupManager 会创建并复用唯一的.v-modal遮罩 DOMpopup-manager.jsmodalAppendToBody为 false 时遮罩会挂到 Dialog 父元素下dom.parentNode.appendChild(modalDom)。lockScroll开启时Popup 会在打开时把document.body加上el-popup-parent--hidden类overflow: hidden并补偿滚动条宽度到padding-right关闭后通过restoreBodyStyle恢复popup/index.js、popup.scss。close-on-click-modal点击遮罩关闭由外层 wrapper 的click.selfhandleWrapperClick实现component.vue.self保证只有点到遮罩空白处才触发点弹窗内部不会误关。close-on-press-escapeESC 关闭由 PopupManager 在window上统一监听keydownkeyCode 27实现且只对当前 zIndex 栈顶的弹层生效popup-manager.js——这保证了多层弹层同时打开时 ESC 只会关闭最上层。custom-class追加到.el-dialog的 class 上用于自定义样式覆盖测试 custom-class。destroy-on-close关闭时通过递增内部key强制重建.el-dialog子树从而清空表单输入等 DOM 状态component.vue。测试 destroyOnClose 验证了在输入框填入内容、关闭再重新打开后输入值被清空。Slot三个可定制区域name说明—Dialog 的内容titleDialog 标题区的内容footerDialog 按钮操作区的内容模板实现上title具名插槽包裹在.el-dialog__header中不传时渲染{{ title }}文本component.vuefooter插槽仅在存在时渲染component.vue。Events四个生命周期事件事件名称说明回调参数openDialog 打开的回调—openedDialog 打开动画结束时的回调—closeDialog 关闭的回调—closedDialog 关闭动画结束时的回调—open在visible变为true的 watcher 中同步触发component.vueopened与closed则挂在transition namedialog-fade的after-enter/after-leave钩子上component.vue分别对应进入/离开动画结束的时机。因此数据就绪 DOM 渲染完成的组合诉求应放在opened之后处理。单元测试 events 对四个事件在开合过程中的触发顺序与参数做了完整验证。幕后机制遮罩、zIndex 栈与动画全局唯一的遮罩 DOM 与模态栈所有弹层Dialog、MessageBox、Drawer 等共用同一个.v-modal遮罩元素由 PopupManager 以引用计数 栈的方式维护modalStack。打开时入栈并把遮罩 z-index 调到当前弹层的 z-index关闭时出栈并把遮罩 z-index 回退到新栈顶的层级只有当栈为空时才真正移除遮罩popup-manager.js。这解释了为什么多个 Dialog 平级打开时遮罩只有一层且始终盖在最老的弹层之下、新弹层之上。打开/关闭动画Dialog 自身使用dialog-fade过渡进入/离开各 300ms包含translate3d(0, -20px, 0)的位移与透明度变化dialog.scss遮罩使用v-modal-in/v-modal-out各 200ms 的淡入淡出popup.scss。配合上述opened/closed事件可以精确感知动画结束时机。结语el-dialog虽然使用起来只是一个visible属性 两个具名插槽但其背后是 Element 统一的弹层基础设施Popup 混入提供遮罩、滚动锁定与延迟开合PopupManager 提供 zIndex 栈与 ESC 键全局处理Vue 的 transition 钩子提供动画事件。理解这些机制后无论是做嵌套弹窗、Vuex 驱动的弹窗还是自定义关闭拦截你都能快速定位问题所在并写出符合组件设计意图的代码。如需继续深入可查阅Dialog 官方文档、组件源码、Popup 混入、PopupManager、样式源码 与 单元测试。【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/element创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表