模擬系統的消息提示框而實現的一套模態對話框組件,用於消息提示、確認消息和提交內容。html
概述:該組件的結構、原理與Toast 組件相似,因此這篇文章會減小組件開發的介紹,而增長一些Vue.extend
、Vue
生命週期相關源碼的解讀。vue
Vue.extend
、$mount
原理以及相應的優化點。代碼git
<!-- 基礎用法 -->
this.$confirm({
title: "自定義提示",
content: `<h1 style="color: red;">自定義HTML</h1>`,
onConfirm: () => this.$message({
content: "肯定"
}),
onCancel: () => this.$message({
type: "warn",
content: "取消"
})
});
this.$alert({
title: "標題名稱",
content: "這是一段內容",
onConfirm: () =>
this.$message({
content: "肯定"
})
});
複製代碼
實例地址:Hover-Tip 實例github
代碼地址:Github UI-Libraryapi
將Message-Box分爲兩部分:緩存
this.$alert({...})
或this.$confirm({...})
在頁面中掛載 Message-Box 組件。首先開發Message-Box組件,其基本template
以下app
<div class="mock" v-if="visible">
<div :class="['message-box']">
<!-- header -->
<h5 class="message-box-header c-size-l">
<span>{{ title }}</span>
<fat-icon v-if="showClose" name="close" class="close-btn" @click.stop="close" />
</h5>
<!-- content -->
<div class="message-box-content c-size-m" v-html="content" >
</div>
<!-- footer -->
<div class="message-box-footer">
<fat-button size="mini" v-if="cancelButtonText && type !== 'alert'" @click.stop="handleClick('cancel')" >{{ cancelButtonText }}</fat-button>
<fat-button size="mini" type="success" v-if="confirmButtonText" @click.stop="handleClick('confirm')" >{{ confirmButtonText }}</fat-button>
</div>
</div>
</div>
複製代碼
基本結構很是簡單,清晰的三段:函數
visible
狀態用於控制整個模態框的顯示、消失,header部分,包含title
,以及關閉按鍵;content
,爲了支持HTML,因此採用v-html
;alert
則只有confirm
,若是爲confirm
,則須要添加cancel
。所涉及的data
、methods
以下post
export default {
data() {
return {
// 控制模態框的顯示
visible: true
}
},
watch: {
visible(newValue) {
if (!newValue) {
// 過渡結束後註銷組件
this.$el.addEventListener('transitionend', this.destroyElement)
}
}
},
mounted() {
document.body.appendChild(this.$el)
},
destroyed() {
this.$el.parentNode.removeChild(this.$el)
},
methods: {
destroyElement() {
this.$destroy()
},
close() {
// 關閉模態框
this.visible = false
},
handleClick(type) {
// 處理對應的點擊事件
this.$emit(type);
this.close();
}
}
}
複製代碼
能夠看到在該組件的mounted
、destroyed
兩個生命週期中完成組件在頁面中的掛載與註銷優化
mounted() {
document.body.appendChild(this.$el)
},
destroyed() {
this.$el.parentNode.removeChild(this.$el)
}
複製代碼
其中註銷的順序是:
visible
狀態爲false
,使它在頁面中消失,而後觸發模態框transition
的過分動畫;addEventListener('transitionend', this.destroyElement)
,若是過渡結束,就觸發對應的destroyElement
觸發組件的生命週期destroyed
,在組件中註銷this.$el.parentNode.removeChild(this.$el)
。相對於註銷,掛載則要複雜的一些,因爲這部分涉及到了封裝,因此一塊兒梳理。
// 引入上述Message-Box組件
import messageBox from './messagebox.vue'
// 生成Message-Box對應的構造器
const Constructor = Vue.extend(messageBox)
function generateInstance(options, type = 'alert') {
let instance = new Constructor({
propsData: Object.assign(options, {
type
}),
}).$mount(document.createElement('div'))
...
return instance
}
複製代碼
首先import
模態框組件,而後利用Vue.extend
建立一個Message-Box組件的構造器。
當調用this.$alert
以及this.$confirm
時利用new Constructor
建立一個Message-Box組件,這時顯式調用vm.$mount()
手動開啓編譯,此時會觸發組件的mounted
生命週期
mounted() {
document.body.appendChild(this.$el)
}
複製代碼
完成在頁面中的掛載。
最後一步,利用Vue.use
完成封裝,在Vue.prototype
上添加$alert
以及$confirm
方法。
export default {
install(Vue) {
Vue.prototype.$alert = (options = {}) => generateInstance(options)
Vue.prototype.$confirm = (options = {}) => generateInstance(options, 'confirm')
}
}
複製代碼
闡述下 Vue.extend
的源碼,在 src/core/global-api/extend.js
中,比較關鍵的點在於:
object
生成對應構造器 constructor
;constructor
,是否存在什麼優化。Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
...
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
...
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
複製代碼
從源碼中能夠看出,Vue.extend
是一個變式的原型式繼承
function object(o) {
function F() {}
F.prototype = o
return new F()
}
複製代碼
臨時的構造函數 function F
爲 function VueComponent
,函數中 this._init
指向的是初始化Vue時候的 _init function
在將 F.prototype
指向 Object.create(Super.prototype)
,這樣能夠繼承 Vue 自己原型上的一些方法,最後 return constructor
。
以後 Vue 還作了緩存處理,因此屢次利用 Vue.extend
建立Message-Box、Toast、Message時,並不會影響效率/
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// cache constructor
cachedCtors[SuperId] = Sub
複製代碼
深究了一下 Vue.extend
背後的原理,以及如何用它來是實現組件。
參考文章:
原創聲明: 該文章爲原創文章,轉載請註明出處。