vue-lazyload的一个坑:事件失效

前言

使用vue-lazyloadlazy-component组件对某个组件进行包裹,然后对组件整体设置懒加载效果时,存在一个坑:该组件内部的自定义组件(即非原生html标签)所有的自定义事件都会失效,且可能视图渲染不完全(多层组件嵌套时);

可能的原因

初步猜测是lazy-component渲染函数的问题,因为渲染函数可以控制内部子元素的事件和属性等配置的挂载;然后一看该组件源码,其内部渲染函数为:

img

不知道是不是跟渲染函数的第二个参数为null有关,渲染函数的第二个参数是用来设置attribute相关的参数(即包括指令,自定义事件v-onprops等相关的属性);

img

解决办法

首先本能地使用Vue实例自带的$foreUpdate()对组件内部进行强制重新渲染,但是重新渲染后还是存在上述问题;毕竟$foreUpdate()方法本质上就是再次调用组件的渲染函数进行渲染,如果渲染函数本身就存在问题,重新渲染多少次都没用。

直接修改源码也是一种办法,不过发现直接改源码并没有用,因为vue-lazyload包主文件是一个编译后压缩的js文件(大多数包都是编译后的),也就是说如果要通过改源码这一途径来解决就需要对这个包进行编译……这个还是有点麻烦的,毕竟看了一下里面的依赖挺多的;

img

不过好在发现这个lazy-component有个自定义事件show,看源码应该是在懒加载组件从不可见区域进入可见区域时触发的,有了这个时间点就好办了;只需要利用这个时间点,用lazy-component进行占位,然后列表项进入可见区域后马上隐藏lazy-component且显示真正的列表项组件,因为此时lazy-component组件和要进行懒加载的列表项组件是平级的(即兄弟关系),所以就避免了lazy-component组件渲染的坑,还可以直接利用其懒加载功能;

img

可以写成一个新的懒加载组件:

1
2
3
4
5
6
7
8
9
<!-- lazy-item组件 -->
<template>
<div class="item">
<!-- 通过lazy-component组件的show事件来切换真正的内容 -->
<lazy-component v-if="showLazy" class="item-lazy" @show="load" />
<!-- 直接透传prop和自定义事件 -->
<list-item v-else v-on="$listeners" v-bind="$attrs"/>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
export default class LayzItem extends Vue {
name = 'LayzItem'

private showLazy = true

private load () {
console.log('懒加载load')
this.$nextTick(() => {
this.showLazy = false
})
}
}

一个小技巧:透传

所谓的透传就是将父组件的属性直接传给子组件,而不是通过在父组件定义相关的props和事件,然后再传入到子组件上;如:

1
<list-item v-else v-on="$listeners" v-bind="$attrs"/>

通过v-on直接绑定父组件的$listeners属性,可以将父组件上绑定的所有事件直接绑定到子组件上;同理,$attrs包含组件的所有attribute属性,通过v-bind则可以直接绑定到子组件上;

透传的好处就是不需要在父组件中重新定义相关属性再中转给子组件,适用于希望将父组件上的相关属性直接传递给子组件的情况;上面这种包装列表项为懒加载组件的情况就很适用,因为直接在懒加载组件(父组件)上绑定的属性和事件就可以原封不动地传给真正的列表项了。

相关文档