点击遮罩关闭弹窗的实现与代码优化思考

187 阅读5分钟

项目背景

时间为下午五点左右,今晚就是项目上线的最后期限,还剩最后两个小功能要做,其中一个功能为解决目前不能点击遮罩区域关闭弹窗的问题。老板希望 小A 和 小B 加加班,毕竟都是小问题,应该很快能解决。小A 觉得也不难,接下了解决无法通过点击遮罩关闭弹窗的任务。

项目技术栈

  • 原生微信小程序

需实现的功能

  • 实现点击弹窗外的遮罩区域关闭弹窗,但点击弹窗内容区域不会关闭
  • 页面中有多个底部弹窗,都需要实现该功能

技术要点

  • 需区分不同的弹窗,一个页面中有多个底部弹窗
  • 阻止点击子元素(弹窗内容)进行事件冒泡传递

项目原有实现

  • 每个底部弹窗都单独有一个布尔值用于控制是否显示
  • 每个底部弹窗有单独的方法来控制对应弹窗的布尔值

实现过程

小A 的实现过程:

  1. 通过搜索很快得出如何实现点击遮罩关闭弹窗,需要给遮罩加上关闭弹窗方法,并给弹窗内容区(非遮罩区域)加上阻止冒泡事件,以免点击弹窗内容也能关闭弹窗
  2. 通过文档知道微信小程序阻止事件冒泡的方法 catchtap,需要绑定一个方法(可以给一个定义的空的函数)
  3. 结合1.和2.实现对一个弹窗点击遮罩关闭弹窗的功能
  4. 因为一个页面有多个底部弹出窗,想到使用微信小程序的自定义组件,这样省去重复定义的逻辑
  5. 查询文档,了解小程序自定义组件如何实现
  6. 尝试提取底部弹窗为一个单独的组件并在页面中引入,对引入的底部弹窗组件,每个弹窗传入单独的isShow属性,并定义打开和关闭为同一个方法,方法内部获取isShow值并进行对应逻辑判断
  7. 发现自定义的组件在某个页面中使用,样式和另一个页面不一样,对比看都是一样的,但最终显示的就是不一样,开始进行问题排查
  8. 过了一段时间,小A 发现样式问题还是不能解决,决定这个页面还是不用自定义底部弹窗组件,按照之前单个的实现来解决,因为时间来不及了,已经八点多了
  9. 此时微信响了,老板来问改好了没,并@小B。小B表示,除了小A 的那个任务没解决,其他都解决了。小A 赶紧回复还差一点点,马上就好并提交了代码准备合并。老板@小A 讲这个其实很简单,简述了实现逻辑。小A 回复明白,代码已经提交了,在准备合并。
  10. 老板看到小A 的代码后表示,你写的太复杂了,为什么要用组件,我来改吧。小A 回复因为觉得这样可以重复利用逻辑,老板回复逻辑是这样没错。
  11. 小A 默默关闭了要合并的 pr。老板修改完毕,并将修改后的那次 commit 记录分享到群里@小A 和小B 看看。

下面来对比看看 小A 和 老板的实现有什么不同。

自定义底部弹出组件(小A)

  • 每个弹窗用自定义的底部弹出组件,在页面中引入
  • 给组件传入是否显示的布尔值,如传入 isShow 值为 false,默认不显示该弹窗
  • 把阻止弹窗内容冒泡的定义,放到了自定义弹窗组件中
  • 对页面引入的每个底部弹窗组件,都传入一个单独的一个布尔值,并单独定义一个方法用于控制弹窗的显隐,内部判断区分显隐的不同逻辑

示意代码:

逻辑

handleOpenAddScheduleModal(e) {
    const { isShow } = e.detail;
    if (isShow === undefined) {
        // 弹窗打开后的相关逻辑
        // 主要的功能实现
        // 更新 showAddScheduleContentModal 为 true
    }
    
    if (isShow === false) {
        // 更新 showAddScheduleContentModal 为 false
        // 更新其他相关属性进行初始化 
        this.onCancelAddScheduleModal();
    }
}

页面

<bottomModal isShow="{{showAddScheduleContentModal}}" bind:showChange="handleOpenAddScheduleModal">
    <view>弹窗内容</view>
</bottomModal>

页面内定义(老板)

  • 修改多个定义的布尔值,合并为一个 showingModal,值为字符串/null
  • 每个弹窗打开,更新 showingModal 的值为指定对应的字符串,如 showingModal: 'updateSchedule'
  • 关闭弹窗,调用统一的 hideModal 方法,设置 showingModal 值为null,showingModal: null以及其他一些值的初始化
  • 在页面中对每个弹窗的显隐的判断,采用如 {{ showingModal === 'updateSchedule' ? 'show' : '' }}的方式来进行判断

示意代码:

逻辑

handleOpenAddScheduleModal(e) {
    // 弹窗打开后的相关逻辑
    // 主要的功能实现
    // 更新 showingModal 值为 updateSchedule
}

hideModal() {
    // 更新 showingModal 值为 null
    // 更新其他值 进行初始化
}

页面

<view class="cu-modal bottom-modal {{ showingModal === 'updateSchedule' ? 'show' : '' }}" bindtap="hideModal">
    <view class="cu-dialog rounded-custom rounded-xl" catchtap>
        <view>弹窗内容</view>
    </view>
</view>

注:catchtap 不绑定一个事件,微信开发者工具会有以下输出

Component "pages/index/index" does not have a method "true" to handle event "tap".

总结

  1. 组件提取的目的:提取组件的主要目标是简化代码结构和提高可重用性。然而,在本次实现中,小A 的方法未能有效降低代码复杂度,反而引入了不必要的逻辑判断,导致实现过程变得繁琐。
  2. 功能优先,优化为辅:在项目开发中,首要任务是确保功能的实现,优化应根据实际需求进行,而不是盲目追求代码的简洁性。确保在紧迫的时间框架内完成核心功能是关键。
  3. 思维方式的灵活性:在处理条件判断时,应打破思维定式,考虑多种可能性。例如,状态的判断不仅限于布尔值,还可以通过对变量值的比较来实现更灵活的逻辑。
  4. 实际情况的评估:在进行代码优化时,需结合项目的实际情况,评估提取组件或简化逻辑的必要性,以确保最终实现的高效性和可维护性。在本项目中,优化的关键在于将多个布尔值合并为一个字符串,以简化判断逻辑,从而提高代码的可读性和维护性。