可以看到close方法在这里被实现了,那么为什么要在原组件上面加上那些方法的定义呢?因为需要在模板上绑定,而模板是无法extend的,只能覆盖,如果要覆盖重新实现,那扩展的意义就不是很大了。其实这里只是一个消息弹窗组件,是可以在模板中就被实现,还有插件怎么注入,大家都可以自己抉择。
同时在使用extend的时候要注意:
方法和属性的定义是直接覆盖的
生命周期方法类似余mixin,会合并,也就是原组件和继承之后的组件都会被调用,原组件先调用
首先通过 let UiNotifier = Vue.extend(Notifier),我们得到了一个类似于Vue的子类,接着就可以通过new UiNotifier({...options})的方式去创建Vue的实例了,同时通过该方式创建的实例,会有组件定义里面的所有属性。
在创建实例之后,vm.$mount()手动将组件挂载到DOM上面,这样我们可以不依赖Vue组件树来输出DOM片段,达到自由显示通知的效果。
扩展:
说一下$mount,我们也许很多项目的主文件是这样的
new Vue({ router, store, el: '#app', render: h => h(App) })
其实el与$mount在使用效果上没有任何区别,都是为了将实例化后的vue挂载到指定的dom元素中。如果在实例化vue的时候指定el,则该vue将会渲染在此el对应的dom中,反之,若没有指定el,则vue实例会处于一种“未挂载”的状态,此时可以通过$mount来手动执行挂载。值得注意的是如果$mount没有提供参数,模板将被渲染为文档之外的的元素,并且你必须使用原生DOM API把它插入文档中,所以我上面写的你应该明白了吧!
这是$mount的一个源码片段,其实$mount的方法支持传入2个参数的,第一个是 el,它表示挂载的元素,可以是字符串,也可以是 DOM 对象,如果是字符串在浏览器环境下会调用 query 方法转换成 DOM 对象的。第二个参数是和服务端渲染相关,在浏览器环境下不需要传第二个参数。
好了,我们现在其实就可以在组件中:
this.notifier[state](msg)来调用了,是不是很方便?
进阶
我们刚才实现了在Vue中通过方法来进行用户反馈的提醒,再增加一个难度:
我们vue项目中应该也遇到过这种情况,弹出一个对话框或是选择框?不但要求用方法弹出,并且能接收到对话框交互所返回的结果。
这里就不详细的分析了直接上代码说(之前的代码,用render来写的组件,懒得改了,直接拿来用...),先创建一个对话框组件---Confirm.vue
<script> let __this = null export default { name: 'Confirm', data() { return { config: { msg: '', ifBtn: '', top: null } } }, created() { __this = this }, methods: { createBox(h) { let config = {} config.attrs = { id: '__confirm' } let children = [] children.push(this.createContainer(h)) children.push(this.createBg(h)) return h('div', config, children) }, createBg(h) { return h('div', { class: 'bg', on: { click: __this.$cancel } }) }, createContainer(h) { let config = {} config.class = { 'box-container': true } if (__this.config.top) { config.style = { 'top': __this.config.top + 'px', 'transform': 'translate(-50%, 0)' } } let children = [] children.push(this.createContentBox(h)) children.push(this.createClose(h)) if (__this.config.ifBtn) { children.push(__this.createBtnBox(h)) } return h('div', config, children) }, createContentBox(h) { let config = {} config.class = { 'content-box': true } return h('div', config, [__this.createContent(h)]) }, createContent(h) { let config = {} config.domProps = { innerHTML: __this.config.msg } return h('p', config) }, createClose(h) { return h('i', { class: 'eqf-no pointer close-btn', on: { click: __this.$cancel } }) }, createBtnBox(h) { return h( 'div', { class: { 'btn-box': true } }, [ __this.createBtn(h, 'btn-cancel middle mr10', '取消', __this.$cancel), __this.createBtn(h, 'btn-primary middle mr10', '确定', __this.$confirm) ]) }, createBtn(h, styles, content, callBack) { return h('button', { class: styles, on: { click: callBack } }, content) } }, render(h) { return this.createBox(h) } } </script> <style scoped> #__confirm { position: fixed; top: 0; left: 0; z-index: 10; width: 100%; height: 100%; } #__confirm .bg { position: fixed; top: 0; left: 0; z-index: 0; width: 100%; height: 100%; } #__confirm .box-container { position: absolute; width: 500px; padding: 20px; padding-top: 30px; border-radius: 3px; background: #fff; z-index: 1; box-shadow: 2px 2px 10px rgba(0,0,0,0.4); top: 50%; left: 50%; transform: translate(-50%, -50%); } #__confirm .content-box { font-size: 14px; line-height: 20px; margin-bottom: 10px; } #__confirm .btn-box { margin-top: 20px; text-align: right; } #__confirm .close-btn { position: absolute; top: 15px; right: 20px; font-size: 16px; color: #666666; } #__confirm .close-btn:hover { color: #1593FF; } #__confirm .bg { position: fixed; } </style>
然后创建confirm.js