背景
- 需求是这样:详情页面展示大量html渲染出来的dom元素,每个子元素的高度是动态的,需同时具备目录功能;
- 出现的问题:页面加载卡顿、数据渲染耗时长,在低配置时 even bad
- 如何解决呢:需要性能优化哦
方法
- 解决的方法:
- lazy load 懒加载
- 虚拟列表
对比
- 懒加载可行么?
大多数会想到懒加载,懒加载是随着页面滚动触发加载,但随之而来的页面DOM元素的不断增加,页面仍旧会卡哦,还卡哦遂放弃!
- 虚拟列表是什么?
虚拟列表,字面含义当然是虚拟的列表数据,即你看到并非列表完整数据;
- 可视区域好比舞台,完整列表数据好比一行排队上台表演的表演者,当演出开始,表演者群体(完整列表数据)根据时间安排依次入场,在舞台上表演,而表演完成的表演者按顺序离台,就这样,随着时间推移,表演完成,而且非常有序高效。
- 打比方如此,前端所说的虚拟列表道理类似,注意需要给定一个高度的容器,页面的DOM元素不会随着页面滚动会越来大,占用过多的浏览器资源,非常好用哦
- 虚拟列表如何实现?
感谢前端大佬,感谢开源,在此使用了两款虚拟列表插件
- vue-virtual-scroller 放链接:github.com/Akryum/vue-…
引入
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller‘
export default {
components: {
DynamicScroller,
DynamicScrollerItem
}
}
使用
<template>
<DynamicScroller
:items="list"
:min-item-size="50"
ref="dynamicScroller
>
<template v-slot="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
>
//业务组件数据
...
</DynamicScrollerItem>
</template>
</DynamicScroller>
</template>
如此可实现虚拟动态列表加载
- vue-virtual-scroll-list 放链接:github.com/tangbc/vue-…
引入
import VirtualList from 'vue-virtual-scroll-list‘
export default {
components: {
VirtualList
}
}
使用
- List.vue 父级组件
<template>
<VirtualList
:data-sources="list"
:data-key="'id'"
:data-component="item"
:estimate-size="50"
ref="virtualList
/>
</template>
<script>
import Item from './Item
export default {
data() {
item: Item,
list: [{id: 1,...}, ...]
},
mounted() {
// 监听子组件数据返回,父子通信
this.$on('change', (index) => {
console.log(index)
// 让目录的index = index 从而达到监听正文滚动 目录滚动效果
})
},
methods: {
// 点击目录定位到对应位置
clickToIndex (index) {
// 调用提供方法
this.$refs.virtualList.scrollToIndex(index)
},
}
}
</script>
- Item.vue 子级组件
<template>
<div v-observe-visibility="handleVisibilityChange" :data-index="index">
// 业务数据渲染
{{ source.id }}
</div>
</template>
<script>
// vue监听元素显示在可视区域的插件
import { ObserveVisibility } from 'vue-observe-visibility'
export default {
directives: {
ObserveVisibility
},
props: {
// 父级list中的每一项数据
source: {
type: Object,
},
index: {
type: Number, // 也可以在list注入,data中定义
}
},
methods: {
// 注册通信方案
dispatch(componentName, eventName, ...rest) {
let parent = this.$parent || this.$root
let name = parent.options.name
while (parent && (!name || name !== componentName)) {
parent = parent.$parent
if (parent) {
name = parent.$option.name
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(rest))
}
},
hanldeVisibilityChange(isVisible, entry) {
if (isVisible) {
// 显示在可视区内,index为绑定的index
this.dispatch('List', 'change', index)
}
}
}
}
</script>
如此可实现虚拟动态列表加载。
结合实际业务需求,可选用以上两个虚拟列表组件,当然你也可以造轮子,优秀哦
源码和原理
当完成业务需求,仍需静下心来思考虚拟动态列表的原理,分析源码,温故而知新!
注意哪些点:
-
要给定固定高度的容器,好比你要给一个舞台,才可以表演
-
分情况,每一项高度固定,如table列表数据,选用对应的组件;每一项动态高度,如本文所讲,每一块由v-html渲染,要选对哦;
-
滚动监听,怎么做,性能优化到极致;两种思路,监听scroll事件,无疑开销会大,性能不是最佳。 new IntersectionObserver是性能优化最优选,当然不考虑那么多的兼容问题;
先说 vue-virtual-scroller
常用两种类型
-
table 每行高度固定哦, 选用RecyleScroller
-
聊天室页面 每行高度虽内容大小而不一, 选用 DynamicScroller
分析源码发现:DynamicScroller 根据 RecyleScroller 进化而来哦
在谈 vue-virtual-scroll-list
目前维护较好, 22days 前发布了最新的release
提供了较多的 props 和 event 哦! 棒!
提供examples:tangbc.github.io/vue-virtual…
贴心!
就此,总结,记录,留用,分享,少走坑,时不我待!