注册一个插件
我们知道在vue中,如果需要在全局增加一个自己使用的对象,在vue的原型中:vue.prototype.xxx 后面接自己的对象通常是没有问题的,但是在项目中vue的原型是不能乱去修改的,因为一旦原型中有两个同名的对象,极有可能会出现灾难性的后果。因此我们需要使用vue中提到的一种方法 Vue.use( plugin ),注册一个插件,集中性地管理这些将被添加到Vue.prototyp中的插件。
使用插件:
通过全局方法
Vue.use()
使用插件。它需要在你调用
new Vue()
启动应用之前完成:
// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)
new Vue({
// ...组件选项
})
也可以传入一个可选的选项对象:
Vue.use(MyPlugin, { someOption: true })
Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。
然后在插件中写入逻辑:
Vue.js 的插件应该暴露一个
install
方法。这个方法的第一个参数是
Vue
构造器,第二个参数是一个可选的选项对象。
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
实现toast组件逻辑
从应用页面组件触发toast事件——>plugin插件中收到传递的参数处理后再传给toast组件——>toast组件收到plugin传递的参数开始执行相关的方法。
应用页面组件触发toast事件:
created() {
this.$toast('这是内容',{
closeButton:{
text: '知道了',
callback() { // 可以接受组件回传来的某些重要的功能
console.log('用户知道了');
}
},
})
},
plugin插件中收到传递的参数处理后再传给toast组件:
import tToast from './toast'
let currentToast = null
export default {
install(Vue, options){
Vue.prototype.$toast = function (message, toastOptions) {
if (currentToast) {
currentToast.close()
}
currentToast = createToast({
Vue,
message,
propsData: toastOptions,
onClose: () => {
currentToast = null
}
})
}
}
}
function createToast({Vue, message,propsData, onClose}) {
let Construtor = Vue.extend(tToast)
let toast = new Construtor({propsData})
toast.$slots.default = [message]
toast.$mount()
toast.$on('close', onClose)
document.body.appendChild(toast.$el)
return toast
}
toast组件收到plugin传递的参数开始执行相关的方法:
props接受4个参数,1、是否自动关闭弹框。2、关闭按钮文案及关闭回调。3、是否可开启传html。4、toast弹出位置选择。
export default {
name: "tToast",
props: {
autoClose: { // 是否自动关闭弹框
type: [Boolean, Number],
default: 2,
validator(value) {
return value === false || typeof value === 'number'
}
},
closeButton: {
type: Object,
default() {
return {
text: '关闭',
callback: undefined
}
}
},
enableHtml: { // 默认不开启传html
type: Boolean,
default: false
},
position: {
type: String,
default: 'top',
validator(value) {
return ['top', 'middle', 'bottom'].indexOf(value) >= 0
}
}
},
mounted() {
this.updateStyles()
this.execAutoClose()
},
methods: {
updateStyles() {
this.$nextTick(() => {
this.$refs.line.style.height = `${this.$refs.wrapper.getBoundingClientRect().height}px`
})
},
execAutoClose() {
if (this.autoClose) {
setTimeout(() => {
this.close()
}, this.autoClose * 1000)
}
},
close() {
this.$el.remove() //删掉当前组件DOM节点
this.$emit('close')
this.$destroy()
},
onClickClose() {
this.close()
if (this.closeButton && typeof this.closeButton.callback === 'function') {
this.closeButton.callback(this) // this === toast实例
}
},
}
}
需要注意的点
如何动态获取DOM在页面中的坐标位置?
通过Element.getBoundingClientRect()方法可以返回元素的大小及其相对于视口的位置
这样就能拿到元素相对与页面的坐标值——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。这样我们就能实现toast组件在页面中的定位显示,可以相对于body也可以相对于任何一个元素的位置,具有很大的自由度。
获取dom元素位置的问题
当我们在mounted生命周期钩子中调用Element.getBoundingClientRect()方法,会拿不到元素的坐标值,因为是先mount挂载,再在plugin插件中appendChild动态添加到body元素中的,因此需要在下一次DOM队列执行的时候才能拿到,这里我们就需要使用vue中的$nextTick() (在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM)。
this.$nextTick(() => {
this.$refs.line.style.height = `${this.$refs.wrapper.getBoundingClientRect().height}px`
})