模拟系统的消息提示框而实现的一套模态对话框组件,用于消息提示、确认消息和提交内容。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
背后的原理,以及如何用它来是实现组件。
参考文章:
原创声明: 该文章为原创文章,转载请注明出处。