在作我的項目的時候須要作一個相似於電子相冊瀏覽的控件,實現過程當中首先要實現全局遮罩,結合本身的思路並閱讀了(餓了麼)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在線預覽