资讯专栏INFORMATION COLUMN

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

malakashi / 2756人阅读

摘要:如何优雅的动态添加这里我们需要用到的实例化,首先我们来看的思路,贴一段源码使用基础构造器,创建一个子类。因为作为一个的扩展构造器,所以基础的功能还是需要保持一致,跟构造器一样在构造函数中初始化。

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

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

调用遮罩的方式

一般由两种写法:

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

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

。。。。。。。。。。

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

非正常全局遮罩

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在线预览

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107741.html

相关文章

  • 如何实现全屏遮罩Vue.extendel-message源码学习

    摘要:如何优雅的动态添加这里我们需要用到的实例化,首先我们来看的思路,贴一段源码使用基础构造器,创建一个子类。因为作为一个的扩展构造器,所以基础的功能还是需要保持一致,跟构造器一样在构造函数中初始化。 [Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习) 在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(...

    Zack 评论0 收藏0
  • 彻底搞懂elementUI指令与服务模式原理

    摘要:不甘做轮子的搬运工记录一个实习菜鸟写图片预览组件的艰辛道路很多组件中使用了指令模式和服务模式,比如以下以组件为例指令模式全屏覆盖服务模式跟大多数萌新一样,啥是服务先看看的目录结构打开目录,找到其下目录文件中有一大坨组件注册信息重点找 不甘做轮子的搬运工!!! 记录一个实习菜鸟写图片预览组件的艰辛道路~ elementUI很多组件中使用了指令模式和服务模式,比如:loading、mes...

    freewolf 评论0 收藏0
  • React组件库封装初探--Modal

    摘要:组件实现基本简介类似于实现的组件,首先基本结构分析遮罩层内容包装层主体内容层,包含固定定位布局,全屏遮盖显示,所以内容自定义实现功能目标两种调用方式一些内容遮罩层和的显示与否,单击是否可关闭其他必备功能结构布局攻克基本布局遮 Madal组件实现基本简介 showImg(https://segmentfault.com/img/bVbqhvl?w=1848&h=834); 类似于an...

    ybak 评论0 收藏0
  • React组件库封装初探--Modal

    摘要:组件实现基本简介类似于实现的组件,首先基本结构分析遮罩层内容包装层主体内容层,包含固定定位布局,全屏遮盖显示,所以内容自定义实现功能目标两种调用方式一些内容遮罩层和的显示与否,单击是否可关闭其他必备功能结构布局攻克基本布局遮 Madal组件实现基本简介 showImg(https://segmentfault.com/img/bVbqhvl?w=1848&h=834); 类似于an...

    codecraft 评论0 收藏0

发表评论

0条评论

malakashi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<