ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-05-12 02:01:46  阅读:205  来源: 互联网

标签:遮罩 Vue Sub extend instance 源码 message options


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

调用遮罩的方式

一般由两种写法:

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

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

 

.mask {
position: fixed;

left: 0;

right: 0;

top: 0;

bottom: 0;

background: rgba(0, 0, 0, .5);

z-index: 999;

}

 

比如在上述结构中,通过控制mask的显示与隐藏来实现全局遮罩,mask的样式如上,通过position:fixed定位脱离文档流来实现占据全屏空间。可以适用于大部分场景。

但是,position:fixed有他自己的特性

position:fixed:

不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。(引自MDN)

也就是说,如果父元素样式中具有transform时,不会做到全局遮罩哦。

在此我制作了2个demo.

正常全局遮罩

非正常全局遮罩(父元素container有transform)(chrome,firefox,edge打开)

非正常全局遮罩样式:

非正常全局遮罩

.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;

}

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主要是继承父类的各种属性来产生一个子类构造器.详细请看源码。

 

标签:遮罩,Vue,Sub,extend,instance,源码,message,options
来源: https://www.cnblogs.com/coderwhytop/p/14758170.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有