在作我的项目的时候须要作一个相似于电子相册浏览的控件,实现过程当中首先要实现全局遮罩,结合本身的思路并阅读了(饿了么)element-ui中el-message的实现,来总结一下Vue中比较好的一种全局遮罩的实现方式。html
通常由两种写法:vue
在html文件中写好结构,控制元素的显示与隐藏的实现遮罩。git
<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打开)
非正常全局遮罩样式:es6
<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>
this.$message.success('登陆成功')
第二种就像原生的alert同样,如el-meaasge,经过命令的方式来调用。(虽然提示信息未全局遮罩,添加思路相同)github
document.body.appendChild(mask);
在document.body动态添加,以此来利用position:fixed来实现,通常对body咱们不会加上transform这种属性,所以避免了上述问题,因此适用性更广一些,element-ui也是这种思路。chrome
这里咱们须要用到vue的实例化,首先咱们来看element-ui的思路,贴一段源码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()中。api
this.$el.parentNode.removeChild(this.$el);//移除dom节点
注意,message关闭的时候会把咱们添加的el移除哦。
若要了解main.vue,完整的注释代码见此处缓存
这里再学习一些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吧,比较简陋,随意看一下就好。
lightbox在线预览