modal组件 vue_如何使用vue.js构造modal组件

本文介绍了在Vue.js中构建Modal组件时遇到的问题,如z-index管理、共享遮罩层、自我管理、状态保留、组件位置和单例、快捷方式支持以及模态阻止滚动的解决方案。通过设置Popup组件基类,实现z-index自动计算和共享遮罩层的控制。此外,讨论了如何通过props和事件处理内外部交互,以及是否重绘弹层的控制策略。
摘要由CSDN通过智能技术生成

匿名用户

1级

2017-07-07 回答

弹出型的组件,无论是 Dialog、Notification、Toast 还是其它的,都有一些共通的问题需要解决:
1. 多个弹层同时出现时,z-index 的顺序
2. 多个弹层同时出现时,共享遮罩层
3. 是否可以自我管理,典型的,定时或者点击遮罩层关闭弹层。
4. 有些弹层需要保留状态,比如你编辑某篇文章,不小心点叉了,再打开还在;而有些需要每次打开都是新的,比如后台中创建某个条目的弹出式表单,你创建过一个条目,下一次再开这个弹层,不应该保留上一个条目的信息
5. 组件放在哪,是否单例,这个和第4点放一起说
6. 如何支持快捷方式,比如喜闻乐见的挂在一个全局方法上,用时创建一个,关闭后销毁
7. 模态,阻止滚动
这些都是之前自己写组件库时遇到的问题,分享一点自己的实践~
1. z-index 的管理
通过设置 position 为 fixed 或 absolute 的方式制作弹层,多个弹层同时打开时,默认是以 dom 树中的位置来确定遮照次序的。当然,我们可以手动设置 z-index,但这麻烦不说,还容易出错。
我的做法是设置一个弹层基类组件,比如叫 Popup,更上层的 Dialog、Toast 之类都是基于它开发的。这个 Popup 组件保持一个单例计数器,比如这样
<script lang="coffe">
$count = 0
module.exports =
props:
# 控制打开还是关闭
open:
type: Boolean
default: false
mounted: ->
# 装载时递增
if @open
$count++
@zIndex = $count
watch:
open: (open) ->
# open 变化时,如果是打开就递增,否则递减
if open
$count++
@zIndex = $count
else
$count--
</script>
这样一来所有基于 Popup 的组件的 z-index 都是自动计算的,后打开的总是在前面。
2. 共享遮罩层
如果所有对话框实例都有自己的半透明遮照,同时打开后层层叠加会很难看(不过,也许有的设计师就喜欢这种效果?)。为了解决这个问题,可以在 Popup 上设置一个单例遮照层。也就是说,若两个以上 Dialog 打开时,Popup 判断已经有一个遮罩层了,那么自动将后面 Dialog 的透明度设为 0。
这里需要注意的是,并不是所有基于 Popup 的弹层式组件都有相同的透明度(比如有的是0.4,有的是 0.8),这里应该设置一个单例变量 maxOpacity,记录并设置为最深的遮照。
3. 外部触发与自我触发的折中
拿最常见的需求来说,Dialog 需要能够点击遮罩层关闭。Vue 1.0 中通过 sync 修饰符很容易做到内外部共享状态,但 2.0 中已经取消了 sync,可能是为了更清晰的状态变化管理,但我觉得 sync 在一些特殊场合确实很有用。
那么现在,一个典型的做法是完全交由外部来控制 Dialog 的开关,比如父组件通过 prop 设置一个布尔值 open,Dialog 发觉遮照被点击时,可以向上抛出个 click-mask 事件,由父组件来决定是否可以关闭。
其实通过 v-model 也可以实现类似 sync 的功效,我不确定语义上是否一定准确,点击遮照关闭对话框也算是用户输入,触发一个 input 事件是合理的,但类似 Toast 2 秒后关闭,这可能说不通。但是共享状态后,内外部都可以控制,实在是很方便,这个可以权衡?
4. 弹层每次打开,是否应该重绘。
我的做法是暴露一个 prop 布尔值 auto-reset 让使用者决定。如果需要每次都重回,就将 open 绑在 v-if 上,否则绑在 v-show 上:
<template lang="jade">
.soil-popup(
v-if="reset? open: true",
v-show="open"
)
slot
</template>
5. 组件放在哪,是否单例
这个根据弹层的功能需求决定。比如后台运营系统里,点击条目列表弹出个条目编辑框,这种天然高内聚的需求,可以与条目列表放在一个条目管理者组件中。而像 AlertDialog、ConfirmDialog 之类则适合放在全局位置。
单例也是看需求,像模态化的 AlertDialog 就一个出口,你肯定得先关闭一个再打开另一个,只需单例。其他需要同时存在的弹层类型,就要考虑多个了。这也是我不喜欢用声明式的写法去调用弹层的原因,看第6点。
6. 挂一个快捷方法,用时创建一个弹层,用完销毁。
很多弹层需要几个同时存在是不确定的( Notification、Toast 等 ),而且对于通用型的弹层,会在很多疙瘩角落里调用…… 我觉得虽然快捷方法违反 Vue 状态化管理组件的特性,但特殊需求还是应该特殊对待。
所以我会设置一个全局 alert 方法( CoffeeScript ):
Constructor = require './AlertDialog.vue'
VueComponent = Vue.extend Constructor
alert = (message, onOK) ->
vm = new VueComponent().$mount()
vm.message = message
vm.$on 'ok', onOK if onOK
vm.$on 'ok', ->
vm.open = false
document.body.removeChild(vm.$el)
setTimeout (-> vm.$destroy()), 0
document.body.appendChild(vm.$el)
vm.open = true
记得销毁实例前要设置 open = false,因为要通知那个全局 z-index 计数器去递减
7. 模态,阻止滚动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值