一. 假造滚动是什么?
前端假造滚动是一种用于优化长列表或大量数据展示的技术。它的重要原理是只渲染用户当前可见区域的数据,而不是一次性渲染整个列表或数据集。
在传统的列表渲染中,如果数据量很大,渲染所有数据可能会导致性能下降,特别是在滚动时。假造滚动通过动态盘算可见区域的位置和需要渲染的数据范围,只渲染可见区域内的部分数据,并在滚动时及时更新渲染的内容。
总结下就是:
- 接口返回了大量数据,前端将大量数据保存起来;
- 假造滚动的策略是:由于无论你返回了多大的数据量,用户可以看到的永远只是可视区域展示的数据,只是大量数据的很少一部分,所以保证可视区域的数据正常渲染就行。
二. 假造滚动怎么实现?
先上代码直观体验下
index.vue
- <template>
- <div class="container">
- <my-virtual-scroller :items="list" />
- </div>
- </template>
- <script>
- import myVirtualScroller from './myVirtualScroller.vue'
- // 模拟一个长列表
- const list = []
- for(let i=0; i<10000; i++) {
- list.push({
- id: i,
- label: `virtual-list ${i}`
- })
- }
- export default {
- components: {
- myVirtualScroller
- },
- data() {
- return {
- list: list
- }
- }
- }
- </script>
- <style scoped>
- .container {
- height: 300px;
- border: 1px solid #ccc;
- }
- </style>
复制代码 myVirtualScroller.vue
- // 传入数据可以实现虚拟滚动事件
- // 这是一个封装好的子组件 传入数组进行渲染
- <template>
- <!-- 最底层的可视区容器 -->
- <div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
- <!-- 中间的可滚动区域,z-index=-1,高度和真实列表相同,目的是出现相同的滚动条 -->
- <div
- class="infinite-list-phantom"
- :style="{ height: listHeight + 'px' }"
- ></div>
- <!-- 最上层的可视区列表,数据和偏移距离随着滚动距离的变化而变化 -->
- <div class="infinite-list" :style="{ transform: getTransform }">
- <div
- class="infinite-list-item"
- v-for="item in visibleData"
- :key="item.id"
- :style="{ height: itemSize + 'px' }"
- >
- {{ item.label }}
- </div>
- </div>
- </div>
- </template>
-
- <script>
- export default {
- name: "MyVirtualList",
- props: {
- //父组件传过来的数据
- items: {
- type: Array,
- default: () => [],
- },
- //可视区域每一项的高度 没有传参的话默认高度是100px
- itemSize: {
- type: Number,
- //没有高度的话 高度会默认是100px
- default: 100,
- },
- },
- computed: {
- //列表总高度
- listHeight() {
- // 计算当前传值数据的总长度 * 每个盒子的高度 计算出所有数据的整体高度
- return this.items.length * this.itemSize;
- },
- //可视区列表的项数
- visibleCount() {
- return Math.ceil(this.screenHeight / this.itemSize);
- },
- //可视区列表偏移距离对应的样式
- getTransform() {
- return `translate3d(0,${this.startOffset}px,0)`;
- },
- //获取可视区列表数据
- visibleData() {
- // 截取出可视区域显示的盒子 进行渲染
- return this.items.slice(
- this.start,
- Math.min(this.end, this.items.length)
- );
- },
- },
- mounted() {
- // 获取可是区域的高度
- this.screenHeight = this.$refs.list.clientHeight;
- // 开始
- this.start = 0;
- // 结束
- this.end = this.start + this.visibleCount;
- },
- data() {
- return {
- screenHeight: 0, //可视区域高度
- startOffset: 0, //偏移距离
- start: 0, //起始索引
- end: 0, //结束索引
- };
- },
- methods: {
- scrollEvent() {
- //当前滚动条滚动位置
- let scrollTop = this.$refs.list.scrollTop;
- //此时的开始索引 使用当前滚动条高度 / 盒子高度 拿到的是当前是哪一个盒子为第一个
- this.start = Math.floor(scrollTop / this.itemSize);
- //此时的结束索引
- //获取到当前第一个盒子的数值 加 计算好可视区域可容纳多少盒子的值
- this.end = this.start + this.visibleCount;
- //此时的偏移距离
- this.startOffset = scrollTop - (scrollTop % this.itemSize);
-
- },
- },
- };
- </script>
-
- <style scoped>
- .infinite-list-container {
- height: 100%;
- overflow: auto;
- position: relative;
- }
-
- .infinite-list-phantom {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- }
-
- .infinite-list {
- left: 0;
- right: 0;
- top: 0;
- position: absolute;
- }
-
- .infinite-list-item {
- line-height: 50px;
- text-align: center;
- color: #555;
- border: 1px solid #ccc;
- box-sizing: border-box;
- }
- </style>
复制代码 假造滚动的思路总结如下:
1. 确定视窗位置:通过获取列表滚动区域的scrollTop属性,确定当前视窗的位置;
2. 盘算需要渲染的节点:根据视窗位置和子节点的高度,盘算出需要真实渲染的节点范围。可以使用Math.floor(scrollTop/itemHeight)和Math.ceil(viewHeight/itemHeight)来确定起始索引和渲染的节点数量;
3. 移动渲染的节点到视窗内:将需要渲染的节点移动到视窗内,可以通过修改节点的位置或使用其他方式实现;
4. 按需加载数据:根据滚动位置,动态加载相应的数据,只加载当前视窗可见范围内的数据,减少数据量和渲染负担;
5. 处理滚动变乱:监听滚动变乱,根据滚动位置及时更新需要渲染的节点和数据。
三. 假造滚动示例代码难点解析
1. 可视化容器为什么要包裹两个盒子,并且设置绝对定位?
第一个盒子(类名为"infinite-list-phantom")的作用:
其总高度为内容的总高度,重要要为了撑起父盒子,使父盒子有滚动条。
第二个盒子(类名为‘infinite-list’)的作用:
这个要可视区域,是用户所能看到的界面,也是数据渲染的界面。
至于为什么要设置绝对定位,是由于需要第二个盒子覆盖第一个盒子,以满意第二个盒子显如今可视界面。
2. 为什么要设置:style=“{ transform: getTransform }”?
原因如下:
- 由于可视界面区域有限,在滚动后,会展示总的内容区域和可视区域不相交的部分,在可视区域就会出现部分或全部空缺。
- 滚动的过程本质要内容区域的位置厘革,其厘革的数值可以通过scrollTop来查看;
- 通过transform的作用,抵消scrollTop的位置变动,使得我们看到的还是原先的可视区域位置的dom节点,只不外在滚动的过程中,渲染数据会改变。
3.为什么要设置偏移距离?
相关代码如下:
- //此时的偏移距离
- this.startOffset = scrollTop - (scrollTop % this.itemSize);
复制代码 原因如下:
- 为了使得滚动的时间有动态效果,体验会更好;
- 可以实行将scrollTop - 改成 scrollTop + 测试,就可以更好的去理解2、3点疑问。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |