热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

『你不知道的ElementUI』被隐藏的神级组件:Popper和它的管家

『你不知道的ElementUI』被隐藏的神级组件:Popper和它的管家-阅读本文?你将:认识两个ElementUI官方文档没有记载,但实际已经被内置的组件。学会使用它们


阅读本文?

你将:



  • 认识两个 ElementUI 官方文档没有记载,但实际已经被内置的组件。

  • 学会使用它们,并获得 demo 一份。

  • 了解 ElementUI 弹出层组件的基本原理,以后面对要不要 appendToBody 及相关问题成竹于胸。


『你不知道的ElementUI』系列

本系列将会深入 ElementUI,为你提供一些只有 ElementUI 资深使用者甚至代码贡献者才会知道的『神级知识』。
它们无论是在日常工作使用中,或是在面试装哔时,都非常实用。
另外:本系列也适合 Element Plus 的用户观看,因为 Element 的核心原理没有大的变化。


一、ElementUI 里所有弹出层的两种模式

ElementUI 的弹出层(包括但不限于:dialog, select, popover, date-picker 等)在元素定位上,都有两种实现方式,分别是:



  • 方案一: append-to-body 式。此模式下,弹出层会被放在 元素上,通过 position:fixed 定位,配合动态的 topleft 属性,完成弹出元素的定位。

  • 方案二: 非 append-to-body 式。此模式下,弹出层通过 position:absolute 定位,配合其父元素 position:relative 来完成弹出元素的定位。

在大多数情况下, ElementUI 都是默认使用的 『方案一:append-to-body 式』。
原因很简单,因为『方案二: 非 append-to-body 式』 存在严重副作用,只有迫不得已的情况下才需要使用。

例如,当弹出层组件的父元素拥有 position: relative; overflow: auto 样式时,是否 append-to-body 可能直接影响组件的显示:

示例代码:https://github.com/zhangshichun/blog-vue2-demos/tree/master/src/views/about-append-to-body


为什么『非 append-to-body 模式』会导致这个问题,而『append-to-body 模式』则不会?这是一道css基础题,欢迎把你的理解打在评论里。


除了上述场景,『非 append-to-body 模式』还会在多种场景下导致类似的问题。

这也是为什么 ElementUI 会把所有弹出层设置为『append-to-body 模式』的根本原因。


二、ElementUI 是如何管理弹出层的z-index的?

第一节已经聊到了,除非特别指定,ElementUI 都会自动使用 『append-to-body 模式』来进行弹出层的实现。
在这个前提下,不妨假象自己是 ElementUI 的开发人员,你会怎么实现以下效果:



  • 应该如何保证 dialog 组件里,select 组件的弹出层高于 dialog 的弹出层?

  • 应该如何保证 dialog-1 上再次弹出的 dialog-2 的层级高于 dialog-1的层级?

ElementUI 项目组的人员在设计这块的时候,想出了一个简单却让人拍案叫绝的办法:只要让新出现的弹出层,永远比之前所有弹出层的层级要高,就不会有『新弹层』被『旧弹层』遮盖的事情发生。

假设一个场景:“弹窗-1”上点击某个按钮弹出“弹窗-2”,而“弹窗-2”上又有个“下拉框组件”,此时它们的 z-index 会是什么样的呢?

如上图所示,只要保证每次“弹出层”在弹出时,都在当前最大 z-index 的基础上 +1,就可以保证没有弹出层会被无故挡住。

记住这个机制,它是 ElementUI 弹出层的核心实现机制。

那么,又是有谁来管理整个项目的 z-index 的呢?

当然是—— popup管家 啦~


三、认识:老管家(PopupManager)


当蝙蝠侠在哥谭市和形形色色的反派殊死搏斗时,他的老管家阿福总能帮他将后方安排得妥妥贴贴。


ElementUI 里,所有弹出层的背后,都有这样一位辛勤的老管家,它就是 PopupManager

PopupManager: 为弹出层提供获取实例、注册、注销 等各种能力,但其最重要的能力,是提供了 z-index 的层级管理能力。

如何使用它?


效果如下:

发现没,第一次 PopupManager.nextZIndex() 会返回2000,然后每次点击都会 +1

后续的 +1 很好理解,每次弹出层要获取到的最新可用 z-index 就是要比之前的那一层多 1

但是为什么一开始会是 2000 呢?

其实这是 ElementUI 内置的一个“弹出层 z-index 基数”,但它是可以进行修改的。

在安装 ElementUI 时,通过以下代码可以让内置的 2000 变成 3000

// 这可以让弹出层的 z-index 从 3000 开始递增
Vue.use(Element, { zIndex: 3000 });

认识老管家的作用,不仅仅是理解 ElementUIz-index 管理机制那么简单,它在实际生产和项目中也可以非常有用。

示例代码:https://github.com/zhangshichun/blog-vue2-demos/tree/master/src/views/popup-manager


四、实战:一个更灵活的全屏组件


官方全屏API虽然好,但产品经理说官方不懂业务。


众所周知,浏览器是有官方的全屏API的:Element.requestFullscreen() ,它可以让一个元素立刻铺满视窗,并且置于所有元素之上。

虽然,这是个看起来很美的 API,但架不住产品经理总有一些奇葩的要求:“这个页面支持全屏,然后它上面还要有一些弹窗,弹窗里面是一些复杂的表单....”

当时听到这我就麻了:


“官方全屏是设定层面的高于一切,那些 append-to-body的弹窗,无论 z-index 再高,也绝对不可能被显示出来。”



“而那些非 append-to-body 模式的弹出层,确实会在某些业务场景不符合要求。)”


左思右想,还得封装一个“符合 ElementUI 层级标准的全屏组件”。

思路很简单:

和浏览器官方API实现全屏的思路基本一致,但不同的地方在于:



  • 官方全屏会默认置顶,z-index无限大

  • 自己封装的全屏,z-index符合 PopupManager 管家的规范。

因此我写了一个简陋的二十几行的 demo


效果上和浏览器官方API进行了对比:

虽然效果上肯定比不了官方的直接全屏,自行封装的还必须配合手动 F11,但完全印证了一个问题:

PopupManager 完全可以用于生产,解决实际问题。

示例代码:https://github.com/zhangshichun/blog-vue2-demos/tree/master/src/views/full-screen


五、认识:万能弹出组件(vue-popper)


虽然管家阿福很厉害,但蝙蝠侠不能骑着阿福去战斗。于是有了蝙蝠战车。


vue-popper 的就是这辆“蝙蝠战车”。一点也不夸张。
我们所能接触到的,ElementUI 中的大部分弹出层都是基于 vue-popper 组件来实现的。

简单罗列一下:selectdate-picker族级联cascaderdropdownpopovertooltip...等等,这些组件都是基于 vue-popper 组件来实现弹出层的。

那么 vue-popper 要怎么使用呢?

通常来说,它的主要用法是 混入(mixins)。使用起来三步走:

最典型的例子,代码太多我就不列了,可以看看 ElementUI dropdown-menu 里对它的具体使用。代码见:https://github.com/ElemeFE/element/blob/dev/packages/dropdown/src/dropdown-menu.vue

混入的例子太复杂,有没有不用混入,直接把它当做组件的用法呢?

当然有!


五、实战:完全自定义的弹出层

如何使用 vue-popper 编写一个简单的“自定义弹出层”的 demo

按照以下三步即可:



  1. 首先我们引入 vue-popper,在模板中引用该组件,并定义一个弹出层元素,一个定位元素。



    2. 给 `vue-popper` 实例指定**弹出层**和**定位层**。
    ```Javascript
    mounted() {
    this.$refs.popper.popperElm = this.$refs['fly-piece'];
    this.$refs.popper.referenceElm = this.$el;
    }


    1. 通过控制 vue-popperprops.value 来控制是否弹出。

      this.showPopper = !this.showPopper

      就能实现如下效果:



    是不是很简单?

    有了这个组件,当你想实现一些自定义的复杂组件的时,是不是很实用?

    示例代码:https://github.com/zhangshichun/blog-vue2-demos/tree/master/src/views/my-picker


    总结

    ok,让我们回忆一下,本文我们讲了哪些知识点:



    1. ElementUI 弹出层的两种模式,以及它们的实现原理。

    2. ElementUI 如何管理弹出层的 z-index

    3. PopupManager 的用法及实战。

    4. vue-popper 的用法及实战。




推荐阅读
  • 深入理解SAP Fiori及其核心概念
    本文详细介绍了SAP Fiori的基本概念、发展历程、核心特性、应用类型、运行环境以及开发工具等,旨在帮助读者全面了解SAP Fiori的技术框架和应用场景。 ... [详细]
  • 本文总结了在使用React Native开发过程中遇到的一些常见问题及其解决方法,包括配置错误、依赖问题和特定组件的使用技巧。 ... [详细]
  • 图神经网络模型综述
    本文综述了图神经网络(Graph Neural Networks, GNN)的发展,从传统的数据存储模型转向图和动态模型,探讨了模型中的显性和隐性结构,并详细介绍了GNN的关键组件及其应用。 ... [详细]
  • 本文探讨了Flutter和Angular这两个流行框架的主要区别,包括它们的设计理念、适用场景及技术实现。 ... [详细]
  • 本文探讨了Thrift作为一款支持多语言的服务开发框架,其在体积、功能、扩展性以及多协议支持等方面的显著优势。特别地,Thrift作为一种RPC(远程过程调用协议)框架,非常适合用于构建可扩展且低耦合的分布式服务系统。文章通过多种编程语言对Thrift服务进行了性能测试,并提供了详细的测试结果。 ... [详细]
  • Lua基本语法lua与C#的交互(相当简单详细的例子)
    lua脚本与C#的交互本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验——高分辨率用户请根据需求调整网页缩放比例)1LuaAndC#——L ... [详细]
  • Python安全实践:Web安全与SQL注入防御
    本文旨在介绍Web安全的基础知识,特别是如何使用Python和相关工具来识别和防止SQL注入攻击。通过实际案例分析,帮助读者理解SQL注入的危害,并掌握有效的防御策略。 ... [详细]
  • Web3隐私协议Manta Network与区块链互操作性平台Axelar达成战略合作,共同推进跨链资产的隐私保护。 ... [详细]
  • 本文详细介绍了如何利用go-zero框架从需求分析到最终部署至Kubernetes的全过程,特别聚焦于微服务架构中的网关设计与实现。项目采用了go-zero及其生态组件,涵盖了从API设计到RPC调用,再到生产环境下的监控与维护等多方面内容。 ... [详细]
  • Java 中的 LinkedTransferQueue isEmpty()方法 ... [详细]
  • 多项目环境下的代码复用策略
    在前端开发中,面对多个并行项目的场景,如何有效地实现代码复用成为了一个重要的议题。本文将探讨一种利用npm包管理来实现跨项目组件共享的方法。 ... [详细]
  • TunnelWarfareTimeLimit:1000MS MemoryLimit:131072KTotalSubmissions:7307 ... [详细]
  • 本文详细探讨Java中Scanner类的两个重要方法——nextInt()和nextDouble(),并通过实例代码演示如何使用这些方法来处理用户输入。 ... [详细]
  • 本文介绍如何使用Java实现AC自动机(Aho-Corasick算法),以实现高效的多模式字符串匹配。文章涵盖了Trie树和KMP算法的基础知识,并提供了一个详细的代码示例,包括构建Trie树、设置失败指针以及执行搜索的过程。 ... [详细]
  • 在现代移动应用开发中,尤其是iOS应用,处理来自服务器的JSON数据是一项基本技能。无论是使用Swift还是PHP,有效地解析和利用JSON数据对于提升用户体验至关重要。本文将探讨如何在Swift中优雅地处理JSON,以及PHP中处理JSON的一些技巧。 ... [详细]
author-avatar
烧饼来一个则_815
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有