[Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习)

在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(饿了么)element-ui中el-message的实现,来总结一下Vue中比较好的一种全局遮罩的实现方式。

调用遮罩的方式

一般由两种写法:

1.(类似el-dialog的一种写法)

在html文件中写好结构,控制元素的显示与隐藏的实现遮罩。

  1. <div class="container">
  2. <div class="mask">。。。。。。。。。。</div>
  3. </div>
  4. <style>
  5. .mask {
  6. position: fixed;
  7. left: 0;
  8. right: 0;
  9. top: 0;
  10. bottom: 0;
  11. background: rgba(0, 0, 0, .5);
  12. z-index: 999;
  13. }
  14. </style>

比如在上述结构中,通过控制mask的显示与隐藏来实现全局遮罩,mask的样式如上,通过position:fixed定位脱离文档流来实现占据全屏空间。可以适用于大部分场景。
但是,position:fixed有他自己的特性
position:fixed:
不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。(引自MDN)
也就是说,如果父元素样式中具有transform时,不会做到全局遮罩哦。
在此我制作了2个demo.
正常全局遮罩
非正常全局遮罩(父元素container有transform)(chrome,firefox,edge打开)
非正常全局遮罩样式:

  1. <div class="container">
  2. <div class="mask"><h1>非正常全局遮罩</h1></div>
  3. </div>
  4. <style>
  5. .container {
  6. height: 111px;
  7. transform: translateX(1px);
  8. }
  9. .mask {
  10. position: fixed;
  11. left: 0;
  12. right: 0;
  13. top: 0;
  14. bottom: 0;
  15. background: rgba(0, 0, 0, .5);
  16. z-index: 999;
  17. color: white;
  18. }
  19. </style>

2. 动态添加(document.body.appendChild)

  1. this.$message.success('登录成功')

第二种就像原生的alert一样,如el-meaasge,通过命令的方式来调用。(虽然提示信息未全局遮罩,添加思路相同)

  1. document.body.appendChild(mask);

在document.body动态添加,以此来利用position:fixed来实现,一般对body我们不会加上transform这种属性,因此避免了上述问题,所以适用性更广一些,element-ui也是这种思路。

Vue如何优雅的动态添加

这里我们需要用到vue的实例化,首先我们来看element-ui的思路,贴一段源码

  1. let MessageConstructor = Vue.extend(Main);//使用基础 Vue 构造器,创建一个“子类”。
  2. let instance;//当前message
  3. let instances = [];//正在显示的所有message
  4. let seed = 1;//相当于id,用于标记message
  5. const Message = function (options) {
  6. if (Vue.prototype.$isServer) return;//当前 Vue 实例是否运行于服务器。
  7. options = options || {};
  8. if (typeof options === 'string') {
  9. options = {
  10. message: options
  11. };
  12. }
  13. let userOnClose = options.onClose;
  14. let id = 'message_' + seed++;
  15. // 简单包装一下
  16. options.onClose = function () {
  17. Message.close(id, userOnClose);//关闭第id个message,并调用回调
  18. };
  19. instance = new MessageConstructor({
  20. data: options
  21. });
  22. instance.id = id;
  23. if (isVNode(instance.message)) {
  24. instance.$slots.default = [instance.message];//html模板 TODO
  25. instance.message = null;
  26. }
  27. instance.vm = instance.$mount();
  28. instance.vm.visible = true;
  29. document.body.appendChild(instance.vm.$el);
  30. instance.dom = instance.vm.$el;
  31. instance.dom.style.zIndex = PopupManager.nextZIndex();//统一管理 z-index
  32. instances.push(instance);//加入本实例
  33. return instance.vm;
  34. };
  35. ['success', 'warning', 'info', 'error'].forEach(type => {
  36. Message[type] = options => {
  37. if (typeof options === 'string') {
  38. options = {
  39. message: options
  40. };
  41. }
  42. options.type = type;
  43. return Message(options);
  44. };
  45. });
  46. Message.close = function (id, userOnClose) {
  47. for (let i = 0, len = instances.length; i < len; i++) {
  48. if (id === instances[i].id) {
  49. if (typeof userOnClose === 'function') {
  50. userOnClose(instances[i]);
  51. }
  52. instances.splice(i, 1);//从正在显示的所有message中移除id这个message
  53. break;
  54. }
  55. }
  56. };
  57. Message.closeAll = function () {
  58. for (let i = instances.length - 1; i >= 0; i--) {
  59. instances[i].close();// 关闭所有message
  60. }
  61. };
  62. export default Message;

阅读代码我们可以知道,通过Vue.extend我们获取到一个子类的构造器。
在初始化并mount()(挂载)之后,将该message动态的加载document.body()中。

  1. this.$el.parentNode.removeChild(this.$el);//移除dom节点

注意,message关闭的时候会把我们添加的el移除哦。
若要了解main.vue,完整的注释代码见此处

Vue.extend

这里再学习一些Vue.extend的知识。主要是我在染陌大神的注释的基础上加了一点点注释,见染陌大神github

  1. export function initExtend (Vue: GlobalAPI) {
  2. /**
  3. * Each instance constructor, including Vue, has a unique
  4. * cid. This enables us to create wrapped "child
  5. * constructors" for prototypal inheritance and cache them.
  6. */
  7. /*
  8. 每个构造函数实例(包括Vue本身)都会有一个唯一的cid
  9. 它为我们能够创造继承创建自构造函数并进行缓存创造了可能
  10. */
  11. Vue.cid = 0
  12. let cid = 1
  13. /**
  14. * Class inheritance
  15. */
  16. /*
  17. 使用基础 Vue 构造器,创建一个“子类”。
  18. 其实就是扩展了基础构造器,形成了一个可复用的有指定父类组件功能的子构造器。
  19. 参数是一个包含组件option的对象。 https://cn.vuejs.org/v2/api/#Vue-extend-options
  20. */
  21. Vue.extend = function (extendOptions: Object): Function {
  22. extendOptions = extendOptions || {}//继承
  23. /*父类的构造*/
  24. const Super = this
  25. /*父类的cid*/
  26. const SuperId = Super.cid
  27. const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  28. /*如果构造函数中已经存在了该cid,则代表已经extend过了,直接返回*/
  29. if (cachedCtors[SuperId]) {
  30. return cachedCtors[SuperId]
  31. }
  32. //组件name
  33. const name = extendOptions.name || Super.options.name
  34. if (process.env.NODE_ENV !== 'production') {
  35. /*name只能包含字母与连字符*/
  36. if (!/^[a-zA-Z][\w-]*$/.test(name)) {
  37. warn(
  38. 'Invalid component name: "' + name + '". Component names ' +
  39. 'can only contain alphanumeric characters and the hyphen, ' +
  40. 'and must start with a letter.'
  41. )
  42. }
  43. }
  44. /*
  45. Sub构造函数其实就一个_init方法,这跟Vue的构造方法是一致的,在_init中处理各种数据初始化、生命周期等。
  46. 因为Sub作为一个Vue的扩展构造器,所以基础的功能还是需要保持一致,跟Vue构造器一样在构造函数中初始化_init。
  47. */
  48. const Sub = function VueComponent (options) {
  49. this._init(options)//和vue初始化相同,再次不再详述
  50. }
  51. /*继承父类*///比如_init就从此继承而来
  52. Sub.prototype = Object.create(Super.prototype)
  53. /*构造函数*/
  54. Sub.prototype.constructor = Sub
  55. /*创建一个新的cid*/
  56. Sub.cid = cid++
  57. /*将父组件的option与子组件的合并到一起(Vue有一个cid为0的基类,即Vue本身,会将一些默认初始化的option何入)*/
  58. Sub.options = mergeOptions(
  59. Super.options,
  60. extendOptions
  61. )
  62. /*es6语法,super为父类构造*/
  63. Sub['super'] = Super
  64. // For props and computed properties, we define the proxy getters on
  65. // the Vue instances at extension time, on the extended prototype. This
  66. // avoids Object.defineProperty calls for each instance created.
  67. /*在扩展时,我们将计算属性以及props通过代理绑定在Vue实例上(也就是vm),这也避免了Object.defineProperty被每一个实例调用*/
  68. if (Sub.options.props) {
  69. /*初始化props,将option中的_props代理到vm上*/
  70. initProps(Sub)
  71. }
  72. if (Sub.options.computed) {
  73. /*处理计算属性,给计算属性设置defineProperty并绑定在vm上*/
  74. initComputed(Sub)
  75. }
  76. // allow further extension/mixin/plugin usage
  77. /*加入extend、mixin以及use方法,允许将来继续为该组件提供扩展、混合或者插件*/
  78. Sub.extend = Super.extend
  79. Sub.mixin = Super.mixin
  80. Sub.use = Super.use
  81. // create asset registers, so extended classes
  82. // can have their private assets too.
  83. /*使得Sub也会拥有父类的私有选项(directives、filters、components)*/
  84. ASSET_TYPES.forEach(function (type) {
  85. Sub[type] = Super[type]
  86. })
  87. // enable recursive self-lookup
  88. /*把组件自身也加入components中,为递归自身提供可能(递归组件也会查找components是否存在当前组件,也就是自身)*/
  89. if (name) {
  90. Sub.options.components[name] = Sub
  91. }
  92. // keep a reference to the super options at extension time.
  93. // later at instantiation we can check if Super's options have
  94. // been updated.
  95. /*保存一个父类的options,此后我们可以用来检测父类的options是否已经被更新*///_init时检查
  96. Sub.superOptions = Super.options
  97. /*extendOptions存储起来*/
  98. Sub.extendOptions = extendOptions
  99. /*保存一份option,extend的作用是将Sub.options中的所有属性放入{}中*/
  100. Sub.sealedOptions = extend({}, Sub.options)
  101. // cache constructor
  102. /*缓存构造函数(用cid),防止重复extend*/
  103. cachedCtors[SuperId] = Sub
  104. return Sub
  105. }
  106. }
  107. /*初始化props,将option中的_props代理到vm上*/
  108. function initProps (Comp) {
  109. const props = Comp.options.props
  110. for (const key in props) {
  111. proxy(Comp.prototype, `_props`, key)
  112. }
  113. }
  114. /*处理计算属性,给计算属性设置defineProperty并绑定在vm上*/
  115. function initComputed (Comp) {
  116. const computed = Comp.options.computed
  117. for (const key in computed) {
  118. defineComputed(Comp.prototype, key, computed[key])
  119. }
  120. }

染陌大神注释很详尽,Vue.extend主要是继承父类的各种属性来产生一个子类构造器.详细请看源码。

demo

最后展示一下demo吧,比较简陋,随意看一下就好。
lightbox在线预览

如何实现全屏遮罩(附Vue.extend和el-message源码学习)的更多相关文章

  1. jQuery10种不同动画效果的响应式全屏遮罩层

    遮罩层有很多今天介绍这个jQuery10种不同动画效果的响应式全屏遮罩层 效果预览 下载地址 实例代码 <div class="container"> <head ...

  2. Vue源码学习三 ———— Vue构造函数包装

    Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...

  3. Vue.js 源码学习笔记

    最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些.那么,就让我来吧:) 程序 ...

  4. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  5. Vue源码学习1——Vue构造函数

    Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...

  6. Vue源码学习(一):调试环境搭建

    最近开始学习Vue源码,第一步就是要把调试环境搭好,这个过程遇到小坑着实费了点功夫,在这里记下来 一.调试环境搭建过程 1.安装node.js,具体不展开 2.下载vue项目源码,git或svn等均可 ...

  7. Vue源码学习二 ———— Vue原型对象包装

    Vue原型对象的包装 在Vue官网直接通过 script 标签导入的 Vue包是 umd模块的形式.在使用前都通过 new Vue({}).记录一下 Vue构造函数的包装. 在 src/core/in ...

  8. 源码学习VUE之Observe

    在文章 源码学习VUE之响应式原理我们大概描述了响应式的实现流程,主要写了observe,dep和wather的简易实现,以及推导思路.但相应代码逻辑并不完善,今天我们再来填之前的一些坑. Obser ...

  9. 最新 Vue 源码学习笔记

    最新 Vue 源码学习笔记 v2.x.x & v3.x.x 框架架构 核心算法 设计模式 编码风格 项目结构 为什么出现 解决了什么问题 有哪些应用场景 v2.x.x & v3.x.x ...

随机推荐

  1. Jmeter+ SeureCRT + Pinpoint

    1.环境配置 [相关操作] 下载jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.h ...

  2. Python文件读写基本操作

    https://www.jianshu.com/p/eab35af27e1c 1.打开文件操作 方法一: f = open('/mypy/test.txt') print f # 输出:<ope ...

  3. Trailing Zeroes (III) LightOJ - 1138 不找规律-理智推断-二分

    其实有几个尾零代表10的几次方但是10=2*510^n=2^n*5^n2增长的远比5快,所以只用考虑N!中有几个5就行了 代码看别人的: https://blog.csdn.net/qq_422797 ...

  4. Spring Boot系列(四) Spring Cloud 之 Config Client

    Config 是通过 PropertySource 提供. 这节的内容主要是探讨配置, 特别是 PropertySource 的加载机制. Spring Cloud 技术体系 分布式配置 服务注册/发 ...

  5. Web高级 JavaScript中的数据结构

    复杂度分析 大O复杂度表示法 常见的有O(1), O(n), O(logn), O(nlogn) 时间复杂度除了大O表示法外,还有以下情况 最好情况时间复杂度 最坏情况时间复杂度 平均情况时间复杂度 ...

  6. Git-第二篇廖雪峰Git教程学习笔记(1)基本命令,版本回退

    1.安装Git-2.16.2-64-bit.exe后,设置用户名,用户邮箱 #--global参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地 ...

  7. linux驱动模型——platform(1)

    一.驱动模型包含什么? 1.1. 类class 1.1.2. 它能够自动创建/dev下的设备节点,不需要mknod /dev/xxx c x x创建.当然class还有其另外的作用,且自动创建设备节点 ...

  8. OtterTune源码解析

    为了方便后面对ottertune进行魔(hu)改(gao),需要先搞清楚它的源码结构和pipeline OtterTune分为两大部分: server side: 包括一个MySQL数据库(用于存储调 ...

  9. c#中decimal的去0显示

    在近来的开发中,遇到到了decimal中显示0的问题,搞了很久才搞好了,现在就简单介绍一下其中一小部分,其他的网上很上很多 public static string DecimalToString(d ...

  10. MyEclipse项目向IDEA项目迁移

    1.首先选择File->New->Project from Existing sources/Project form Version Control,如果项目在你的本地则选择 Proje ...