如何实现全屏遮罩(附Vue.extend和el-message源码学习)
[Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习)
在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(饿了么)element-ui中el-message的实现,来总结一下Vue中比较好的一种全局遮罩的实现方式。
调用遮罩的方式
一般由两种写法:
1.(类似el-dialog的一种写法)
在html文件中写好结构,控制元素的显示与隐藏的实现遮罩。
<div class="container">
<div class="mask">。。。。。。。。。。</div>
</div>
<style>
.mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, .5);
z-index: 999;
}
</style>
比如在上述结构中,通过控制mask的显示与隐藏来实现全局遮罩,mask的样式如上,通过position:fixed定位脱离文档流来实现占据全屏空间。可以适用于大部分场景。
但是,position:fixed有他自己的特性
position:fixed:
不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。(引自MDN)
也就是说,如果父元素样式中具有transform时,不会做到全局遮罩哦。
在此我制作了2个demo.
正常全局遮罩
非正常全局遮罩(父元素container有transform)(chrome,firefox,edge打开)
非正常全局遮罩样式:
<div class="container">
<div class="mask"><h1>非正常全局遮罩</h1></div>
</div>
<style>
.container {
height: 111px;
transform: translateX(1px);
}
.mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, .5);
z-index: 999;
color: white;
}
</style>
2. 动态添加(document.body.appendChild)
this.$message.success('登录成功')
第二种就像原生的alert一样,如el-meaasge,通过命令的方式来调用。(虽然提示信息未全局遮罩,添加思路相同)
document.body.appendChild(mask);
在document.body动态添加,以此来利用position:fixed来实现,一般对body我们不会加上transform这种属性,因此避免了上述问题,所以适用性更广一些,element-ui也是这种思路。
Vue如何优雅的动态添加
这里我们需要用到vue的实例化,首先我们来看element-ui的思路,贴一段源码
let MessageConstructor = Vue.extend(Main);//使用基础 Vue 构造器,创建一个“子类”。
let instance;//当前message
let instances = [];//正在显示的所有message
let seed = 1;//相当于id,用于标记message
const Message = function (options) {
if (Vue.prototype.$isServer) return;//当前 Vue 实例是否运行于服务器。
options = options || {};
if (typeof options === 'string') {
options = {
message: options
};
}
let userOnClose = options.onClose;
let id = 'message_' + seed++;
// 简单包装一下
options.onClose = function () {
Message.close(id, userOnClose);//关闭第id个message,并调用回调
};
instance = new MessageConstructor({
data: options
});
instance.id = id;
if (isVNode(instance.message)) {
instance.$slots.default = [instance.message];//html模板 TODO
instance.message = null;
}
instance.vm = instance.$mount();
instance.vm.visible = true;
document.body.appendChild(instance.vm.$el);
instance.dom = instance.vm.$el;
instance.dom.style.zIndex = PopupManager.nextZIndex();//统一管理 z-index
instances.push(instance);//加入本实例
return instance.vm;
};
['success', 'warning', 'info', 'error'].forEach(type => {
Message[type] = options => {
if (typeof options === 'string') {
options = {
message: options
};
}
options.type = type;
return Message(options);
};
});
Message.close = function (id, userOnClose) {
for (let i = 0, len = instances.length; i < len; i++) {
if (id === instances[i].id) {
if (typeof userOnClose === 'function') {
userOnClose(instances[i]);
}
instances.splice(i, 1);//从正在显示的所有message中移除id这个message
break;
}
}
};
Message.closeAll = function () {
for (let i = instances.length - 1; i >= 0; i--) {
instances[i].close();// 关闭所有message
}
};
export default Message;
阅读代码我们可以知道,通过Vue.extend我们获取到一个子类的构造器。
在初始化并mount()(挂载)之后,将该message动态的加载document.body()中。
this.$el.parentNode.removeChild(this.$el);//移除dom节点
注意,message关闭的时候会把我们添加的el移除哦。
若要了解main.vue,完整的注释代码见此处
Vue.extend
这里再学习一些Vue.extend的知识。主要是我在染陌大神的注释的基础上加了一点点注释,见染陌大神github
export function initExtend (Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
/*
每个构造函数实例(包括Vue本身)都会有一个唯一的cid
它为我们能够创造继承创建自构造函数并进行缓存创造了可能
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
*/
/*
使用基础 Vue 构造器,创建一个“子类”。
其实就是扩展了基础构造器,形成了一个可复用的有指定父类组件功能的子构造器。
参数是一个包含组件option的对象。 https://cn.vuejs.org/v2/api/#Vue-extend-options
*/
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}//继承
/*父类的构造*/
const Super = this
/*父类的cid*/
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
/*如果构造函数中已经存在了该cid,则代表已经extend过了,直接返回*/
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
//组件name
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production') {
/*name只能包含字母与连字符*/
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'can only contain alphanumeric characters and the hyphen, ' +
'and must start with a letter.'
)
}
}
/*
Sub构造函数其实就一个_init方法,这跟Vue的构造方法是一致的,在_init中处理各种数据初始化、生命周期等。
因为Sub作为一个Vue的扩展构造器,所以基础的功能还是需要保持一致,跟Vue构造器一样在构造函数中初始化_init。
*/
const Sub = function VueComponent (options) {
this._init(options)//和vue初始化相同,再次不再详述
}
/*继承父类*///比如_init就从此继承而来
Sub.prototype = Object.create(Super.prototype)
/*构造函数*/
Sub.prototype.constructor = Sub
/*创建一个新的cid*/
Sub.cid = cid++
/*将父组件的option与子组件的合并到一起(Vue有一个cid为0的基类,即Vue本身,会将一些默认初始化的option何入)*/
Sub.options = mergeOptions(
Super.options,
extendOptions
)
/*es6语法,super为父类构造*/
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
/*在扩展时,我们将计算属性以及props通过代理绑定在Vue实例上(也就是vm),这也避免了Object.defineProperty被每一个实例调用*/
if (Sub.options.props) {
/*初始化props,将option中的_props代理到vm上*/
initProps(Sub)
}
if (Sub.options.computed) {
/*处理计算属性,给计算属性设置defineProperty并绑定在vm上*/
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
/*加入extend、mixin以及use方法,允许将来继续为该组件提供扩展、混合或者插件*/
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
/*使得Sub也会拥有父类的私有选项(directives、filters、components)*/
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
/*把组件自身也加入components中,为递归自身提供可能(递归组件也会查找components是否存在当前组件,也就是自身)*/
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
/*保存一个父类的options,此后我们可以用来检测父类的options是否已经被更新*///_init时检查
Sub.superOptions = Super.options
/*extendOptions存储起来*/
Sub.extendOptions = extendOptions
/*保存一份option,extend的作用是将Sub.options中的所有属性放入{}中*/
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
/*缓存构造函数(用cid),防止重复extend*/
cachedCtors[SuperId] = Sub
return Sub
}
}
/*初始化props,将option中的_props代理到vm上*/
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
/*处理计算属性,给计算属性设置defineProperty并绑定在vm上*/
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
染陌大神注释很详尽,Vue.extend主要是继承父类的各种属性来产生一个子类构造器.详细请看源码。
demo
最后展示一下demo吧,比较简陋,随意看一下就好。
lightbox在线预览
如何实现全屏遮罩(附Vue.extend和el-message源码学习)的更多相关文章
- jQuery10种不同动画效果的响应式全屏遮罩层
遮罩层有很多今天介绍这个jQuery10种不同动画效果的响应式全屏遮罩层 效果预览 下载地址 实例代码 <div class="container"> <head ...
- Vue源码学习三 ———— Vue构造函数包装
Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...
- Vue.js 源码学习笔记
最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些.那么,就让我来吧:) 程序 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- Vue源码学习1——Vue构造函数
Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...
- Vue源码学习(一):调试环境搭建
最近开始学习Vue源码,第一步就是要把调试环境搭好,这个过程遇到小坑着实费了点功夫,在这里记下来 一.调试环境搭建过程 1.安装node.js,具体不展开 2.下载vue项目源码,git或svn等均可 ...
- Vue源码学习二 ———— Vue原型对象包装
Vue原型对象的包装 在Vue官网直接通过 script 标签导入的 Vue包是 umd模块的形式.在使用前都通过 new Vue({}).记录一下 Vue构造函数的包装. 在 src/core/in ...
- 源码学习VUE之Observe
在文章 源码学习VUE之响应式原理我们大概描述了响应式的实现流程,主要写了observe,dep和wather的简易实现,以及推导思路.但相应代码逻辑并不完善,今天我们再来填之前的一些坑. Obser ...
- 最新 Vue 源码学习笔记
最新 Vue 源码学习笔记 v2.x.x & v3.x.x 框架架构 核心算法 设计模式 编码风格 项目结构 为什么出现 解决了什么问题 有哪些应用场景 v2.x.x & v3.x.x ...
随机推荐
- Pku2054 Color a Tree
有一个N个结点的有根树,1是这个树的根.现在要对这N个结点依次进行染色,每个结点染色要花费1个单位的时候,同时要满足一个结点仅在其父亲被染色后才可被染色,每个结点有个权值Ci,如果我们在第Ti时间对i ...
- Mac015--在Mac下安装使用Vagrant
网址:http://yansu.org/2014/04/10/install-vagrant-in-mac.html 一.安装Vagrant 下载地址在http://www.vagrantup.com ...
- C# 保留N位小数
1.只要求保留N位不四舍五入 float f = 0.55555f; int i =(int)(f * 100); f = (float)(i*1.0) ...
- mysql使用触发器生成唯一订单号,
需求:订单号唯一,并且期望是时间格式加其他字符串, 实现:采用触发机制,在新增时根据新增id值加1作为订单生成的随机且确定唯一的数,因为id唯一: 遇到问题:新增时不能提前知道id值, 解决:取到当前 ...
- oracle--约束(主键、非空、检查)
问题1:学号重复了,数据还可以插入成功 使用主键约束:学号是唯一标识一条数据的,所以必须唯一且不能为空 ---(1).在确定为主键的字段后添加 primary key关键字 ---(2).在创建表的后 ...
- Jmeter使用SSL(HTTPS协议)
Jmeter是apache一款开源.小巧的性能测试工具,平时测试web http协议经常使用,其实jmeter同样支持ssl.方法如下: 需要装有目标网站证书的密钥库,即testclient.keys ...
- 将java项目转换成javaWeb项目
1.Ctrl+Shift+R快捷键:找到此项目中的.project文件,打开修改文件内容 在<natures> </natures>代码标签中,添加些内容: <natur ...
- SQL取年月日的不同格式
Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AM Select CONVERT(varchar(100), GETDATE( ...
- 《剑指offer》面试题19 二叉树的镜像 Java版
书中方法:这道题目可能拿到手没有思路,我们可以在纸上画出简单的二叉树来找到规律.最后我们发现,镜像的实质是对于二叉树的所有节点,交换其左右子节点.搞清楚获得镜像的方法,这道题实际上就变成了一道二叉树遍 ...
- dfs(魔力转圈圈)
http://oj.jxust.edu.cn/contest/problem?id=1563&pid=4 题目描述 Storm有一个m行n列的整数矩阵. 他会从(1,1)开始,顺时针螺旋访问该 ...