当前位置: 首页 > Web前端 > HTML5

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

时间:2023-04-05 18:44:29 HTML5

[Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习)做个人项目时,需要做一个类似电子相册浏览的控件,在实现过程中,首先要实现全局mask,结合自己的思路,看一下element-ui中el-message的实现(饿了么),总结一下Vue中比较好的全局mask是如何实现的。mask的调用方式一般有两种:1.(类似el-dialog的方式)在html文件中编写结构体,控制元素的显示和隐藏来实现mask。。.........

例如,在上面的结构中,通过控制mask的显示和隐藏来实现全局mask。蒙版样式如上,position:固定定位脱离文档流占据全屏空间。可以适用于大部分场景。但是position:fixed有自己的特点position:fixed:不为元素保留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素的位置。滚动屏幕时元素的位置不会改变。打印时,该元素会出现在每一页的固定位置。fixed属性创建一个新的堆叠上下文。当元素的祖先具有非无转换属性时,容器从视口更改为该祖先。(引用自MDN)也就是说,如果父元素样式中有transform,是不会实现全局遮罩的。这里我做了2个demo。正常全局遮罩不正常全局遮罩(父元素容器有transform)(chrome、firefox、edgeopen)异常全局遮罩样式:

异常全局掩码

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的思路,贴出一段源码letMessageConstructor=Vue.extend(Main);//使用基本的Vue构造函数创建一个“子类”。letinstance;//当前messageletinstances=[];//所有显示的messageletseed=1;//相当于id,用于标记消息constMessage=function(options){if(Vue.prototype.$isServer)return;//当前Vue实例是否在服务器上运行。选项=选项||{};if(typeofoptions==='string'){options={message:options};}让userOnClose=options.onClose;让id='message_'+seed++;//简单打包一下options.onClose=function(){Message.close(id,userOnClose);//关闭第id个message,并调用回调};instance=newMessageConstructor({data:options});实例.id=id;if(isVNode(instance.message)){instance.$slots.default=[instance.message];//html模板TODOinstance.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-indexinstances.push(instance);//加入本实例returninstance.vm;};['success','warning','info','error'].forEach(type=>{Message[type]=options=>{if(typeofoptions==='string'){options={message:options};}options.type=type;返回消息(选项);};});Message.close=function(id,userOnClose){for(leti=0,len=instances.length;i=0;i--){instances[i].close();//关闭所有消息}};导出默认消息;看完代码我们可以知道,通过Vue.extend我们得到了一个子类的构造函数。在初始化和mount()(挂载)之后,消息被动态加载到document.body()中。this.$el.parentNode.removeChild(this.$el);//移除dom节点注意我们添加的el会在消息关闭的时候移除。理解main.vue,完整的注释代码看这里Vue.extend,学习Vue.extend的一些知识点这里。主要是我在乱摸大神评论的基础上加了一点注释,见乱摸大神githubexportfunctioninitExtend(Vue:GlobalAPI){/***每个实例构造函数,包括Vue,都有一个唯一的*cid。这使我们能够为原型继承创建包装的“子*构造函数”并缓存它们。*//*每个构造函数实例(包括Vue本身)都有一个唯一的cid,它允许我们从构造函数创建继承并创建缓存*/Vue.cid=0letcid=1/***类继承*//*使用基础Vue构造函数,创建一个“子类”。实际上,它扩展了基本构造函数,形成了一个可重用的子构造函数,具有指定父类组件的功能。参数是包含组件选项的对象。https://cn.vuejs.org/v2/api/#Vue-extend-options*/Vue.extend=function(extendOptions:Object):Function{extendOptions=extendOptions||复制代码{}//继承/*父类构造*/constSuper=this/*父类的cid*/constSuperId=Super.cidconstcachedCtors=extendOptions._Ctor||(extendOptions._Ctor={})/*如果构造函数中已经存在cid,则代表已经扩展,直接返回*/if(cachedCtors[SuperId]){returncachedCtors[SuperId]}//组件名constname=扩展选项名称||Super.options.nameif(process.env.NODE_ENV!=='production'){/*名称只能包含字母和连字符*/if(!/^[a-zA-Z][\w-]*$/.test(name)){warn('无效的组件名称:“'+name+'”。组件名称'+'只能包含字母数字字符和连字符,'+'并且必须以字母开头。')}}/*Sub构造函数其实是一个_init方法,这个和Vue的构造方法是一致的,在_init中处理各种数据初始化,生命周期等。因为Sub是Vue的扩展构造函数,所以基本功能还是要保持一致,和Vue构造函数一样在构造函数中初始化_init。*/constSub=functionVueComponent(options){this._init(options)//同vue初始化,不再赘述}/*继承父类*///比如_init继承自Sub.prototype=Object.create(Super.prototype)/*Constructor*/Sub.prototype.constructor=Sub/*新建一个cid*/Sub.cid=cid++/*合并父组件的option和子组件的option(Vue有一个cid为0的基类,即Vue本身,会包含一些默认初始化的options)*/Sub.options=mergeOptions(Super.options,extendOptions)/*es6语法,super为父类构造*/Sub['super']=Super//对于props和计算属性,我们在扩展时在扩展原型上的Vue实例上定义代理getter。这//避免为每个创建的实例调用Object.defineProperty。/*在展开的时候,我们将计算出来的属性和props通过代理绑定到Vue实例(也就是vm)上,这样也可以防止Object.defineProperty被每个实例调用*/if(Sub.options.props){/*初始化props,代理_propsinoptiontovm*/initProps(Sub)}if(Sub.options.computed){/*处理计算属性,为计算属性设置defineProperty并将它们绑定到vm*/initComputed(Sub)}//Allowfurtherextension/mixin/pluginusage/*添加extend、mixin和use方法,允许以后继续为该组件提供extends、mixin或plugins*/Sub.extend=Super.extendSub.mixin=Super.mixinSub.use=Super.use//创建资产寄存器,因此扩展类//也可以拥有它们的私有资产。/*MakeSub也有父类的私有选项(指令、过滤器、组件)*/ASSET_TYPES.forEach(function(type){Sub[type]=Super[type]})//启用递归自查找/*将组件自身添加到组件中,提供自身递归的可能性(递归组件还会检查组件中是否存在当前组件,即自身)*/if(name){Sub.options.components[name]=Sub}//在扩展时保留对超级选项的引用。//稍后在实例化时,我们可以检查Super的选项是否已更新//。/*保存一个父类的options,然后我们可以用它来检测父类的options是否被更新了*///_initcheckSub.superOptions=Super.options/*extendOptionsstored*/Sub.extendOptions=extendOptions/*保存一个option,extend的作用是将Sub.options中的所有属性都放到{}中*/Sub.sealedOptions=extend({},Sub.options)//缓存构造函数/*缓存构造函数(使用cid)防止重复extend*/cachedCtors[SuperId]=SubreturnSub}}/*初始化props,proxy_propsinoptiontovm*/functioninitProps(Comp){constprops=Comp.options.propsfor(constkeyinprops){proxy(Comp.prototype,`_props`,key)}}/*处理计算属性,为计算属性设置defineProperty并绑定到vm上*/functioninitComputed(comp){constcomputed=Comp.options.computedfor(constkeyincomputed){defineComputed(Comp.prototype,key,computed[key])}}严墨注释的很详细,Vue.extend主要是继承父类的各种属性生成子类的构造函数。详情请看源码demo,最后展示demo。比较简单,看看就知道了。灯箱在线预览