怎樣完成全屏遮罩(附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在線預覽

    原文作者:178096413
    原文地址: https://segmentfault.com/a/1190000014698743
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞