今天在开发的时候,遇到一个问题:
场景:
有一个数组,我需要遍历这个数组,把这个数组里的每一项使用element-ui的Notification组件渲染在页面右下角。
这还不简单,arr.forEach()搞定啊!!!
tipsArr.forEach((item: string) => {
this.$notify({
title: item,
message: '',
position: 'bottom-right',
type: 'warning',
duration: 0,
offset: 10
});
})
但是似乎没我想得那么顺利,因为代码写出来之后,页面展示是这样的,每一条信息都重叠到一起了,和官网展示的效果不太一样啊???
我的提示信息都重叠到了一块,关掉一条之后,才能看到下一条的内容,这不是我想要的效果哇!!!我希望它可以像官网展示的那样,平铺下来。
原因分析:
之后,通过查看了notify的源码,找到了原因:
let verticalOffset = options.offset || 0;
instances.filter(item => item.position === position).forEach(item => {
verticalOffset += item.$el.offsetHeight + 16;
});
verticalOffset += 16;
instance.verticalOffset = verticalOffset;
每一个Notification通知组件在显示之前需要计算它应该显示的位置,他需要知道它前面的 Notification 通知实例有多少个,然后进一步计算它自己应该显示的位置,每次计算间距时,会取当前元素的高度:item.$el.offsetHeight ,但是因为vue的异步更新队列有缓冲机制,第一次个通知渲染时,并没有更新dom,导致取到的高度为0,所有第二个通知只是上移了默认的offset 16px,并没有加上第一个通知的高度,之后的每一个通知都是只增加了16px的间距,最终渲染出来的通知组件,就是重叠在一起的。
解决方式:
方法一:使用nextTick方法
但这个方法有一定的局限性,就是你已知要渲染的通知信息只有少数几条,或者已知数组的长度,数组的length值不是很长。
//已知 tipsArr的length值为2
this.$notify({
title: tipsArr[0],
message: '',
position: 'bottom-right',
type: 'warning',
duration: 0,
offset: 10
});
this.$nextTick(() => {
this.$notify({
title: tipsArr[1],
message: '',
position: 'bottom-right',
type: 'warning',
duration: 0,
offset: 10
});
})
方法二:使用setTimeout
data() {
return {
timer: null
}
},
tipsArr.forEach((item: string) => {
this.timer = setTimeout(() => {
this.$notify({
title: item,
message: '',
position: 'bottom-right',
type: 'warning',
duration: 0,
offset: 10
});
}, 0)
this.$once('hook:beforeDestroy', () => {
this.timer && clearTimeout(this.timer)
this.timer = null
})
})
方式三:使用Promise
data() {
return {
notifyPromise: Promise.resolve()
}
}
tipsArr.forEach((item: string) => {
this.notifyPromise = this.notifyPromise.then(() => {
this.$notify({
title: item,
message: '',
position: 'bottom-right',
type: 'warning',
duration: 0,
offset: 10
});
})
})
解决后的效果:
|