解决vue引入BetterScroll导致的图片列表拉不上去问题

    技术2022-07-11  74

    写在开头

      学习之余,发现了一个比较好的滚动组件 BetterScroll,在移动端、PC端都有很好的体验效果。就拿出来分享一下:BetterScroll官网。源码托管自 GitHub:BetterScroll。中文文档:BetterScroll文档

      该组件在 GitHub 有这样依据介绍:inspired by iscroll, and it supports more features and has a better scroll perfermance.(灵感来源于iscroll...)。iscroll 也是一个比较好的滚动组件,但是在 3 Jan 2017停更了。所以在这之后,国人就开发了 BetterScroll 这么一个优秀的组件,截止2020.7.1还在继续更新,而且该组件在 iscroll之上也添加了更多的一些功能,效果还是不错的。

    图片列表拉不上去的原因

      在开发中,总会遇到对图片列表的上拉加载需求。但是在引入 BetterScroll 组件时,却 导致图片类型列表上拉到一部分时,出现拉不上去的Bug。

      经过分析,是Vue在加载数据的过程中,由于网络的原因,图片的加载都会有个先后顺序(或因为网络延迟,导致的图片迟了几百毫秒显示)。然而 BetterScroll 组件是在图片还未加载完成时,就已经完成了对滚动区域的高度计算,当图片真正加载完成时,导致高度计算有问题,从而出现拉不上去的Bug。 (实际高度>已计算的高度,导致拉不上去)

    解决办法

      BetterScroll 组件中,提供了refresh()这个方法,即:重新计算 better-scroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常。

      最终解决方法就是:在每次加载图片完成时,重新计算一下 DOM 结构的高度。

      原生JS在<img onload="xxx">标签中,onload 这个属性就是用来监听图片是否加载完成的。Vue 中我们可以通过 @load="xxx" 的方式来监听。步骤如下:

    在包含<img> 的 Vue 子组件中,通过 @load="isLoadOK" 的方式来监听图片是否加载完成;在 methods 中定义方法 isLoadOK ,然后通过 $emit 的方式,将事件传到父组件(兄弟组件请使用bus总线),父组件通过 this.$on() 的方式调用 this.refs.scroll.refresh() 就可以完美解决。

    伪代码如下 (思想是这般,不好原样提供代码,就上面两个步骤就能解决的)

    <!-- 子组件 --> <template> <div class="goods-item" @click="itemClick"> <img :src="goodsItem.show.img" alt="" @load="isLoadOK"> <p>其他文字介绍</p> </div> </template> <script> export default { name: 'scroll ', //省略部分代码 methods:{ isLoadOK() { this.$emit('imageLoad'); } } } </script> <!-- 父组件 --> <template> <div id="home" class="wrapper"> <!-- 引入子组件(此处是伪代码) --> <scroll ref="scroll"></scroll> </div> </template> <script> export default { //省略部分代码 //在加载完成时, mounted(){ this.$on('imageLoad', this.$refs.scroll.refresh()) } } </script>

    上面方式,会引出性能问题

      如果一张图片加载完成就调用一次 refresh()方法,显然影响性能。此处我们可以使用 JS防抖函数来处理。

      所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

    /** * 定义防抖动函数,防止函数调用频繁,图片加载完成后,再进行重新计算高度的刷新 */ export function debounce(func, delay) { let timer; return function(...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args) }, delay) } }

      然后,将父组件中,this.$on('imageLoad', this.$refs.scroll.refresh()) 这种方式稍微修改一下即可,如下所示:

    <script> export default { //省略部分代码 //在加载完成时, mounted(){ // 1.图片加载完成的事件监听 const refresh = debounce(this.$refs.scroll.refresh, 50) this.$on('imageLoad', () => { refresh() }) } }

    参考文章:

      JS 防抖函数和节流函数介绍


    博主写作不易,加个关注呗

    求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙

    博主不能保证写的所有知识点都正确,但是能保证纯手敲,错误也请指出,望轻喷 Thanks♪(・ω・)ノ

    Processed: 0.008, SQL: 9