如果我想要在网页中放大量的列表项,纯渲染的话,对于浏览器性能将会是个极大的挑战,会造成滚动卡顿,整体体验非常不好,主要有以下问题:
页面等待时间极长,用户体验差CPU计算能力不够,滑动会卡顿GPU渲染能力不够,页面会跳屏RAM内存容量不够,浏览器崩溃1. 传统做法对于长列表渲染,传统的方法是使用懒加载的方式,下拉到底部获取新的内容加载进来,其实就相当于是在垂直方向上的分页叠加功能,但随着加载数据越来越多,浏览器的回流和重绘的开销将会越来越大,整个滑动也会造成卡顿,这个时候我们就可以考虑使用虚拟列表来解决问题
2. 虚拟列表其核心思想就是在处理用户滚动时,只改变列表在可视区域的渲染部分,具体步骤为:
先计算可见区域起始数据的索引值startIndex和当前可见区域结束数据的索引值endIndex,假如元素的高度是固定的,那么startIndex的算法很简单,即startIndex = Math.floor(scrollTop/itemHeight),endIndex = startIndex (clientHeight/itemHeight) - 1,再根据startIndex 和endIndex取相应范围的数据,渲染到可视区域,然后再计算startOffset(上滚动空白区域)和endOffset(下滚动空白区域),这两个偏移量的作用就是来撑开容器元素的内容,从而起到缓冲的作用,使得滚动条保持平滑滚动,并使滚动条处于一个正确的位置
上述的操作可以总结成五步:
不把长列表数据一次性全部直接渲染在页面上截取长列表一部分数据用来填充可视区域长列表数据不可视部分使用空白占位填充(下图中的startOffset和endOffset区域)监听滚动事件根据滚动位置动态改变可视列表监听滚动事件根据滚动位置动态改变空白填充基础示例下面就是使用pinia的一个例子(一定要看后面的文档,从头学习)。这样你就创建了一个状态存储。
// stores/counter.jsimport { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', { state: () => { return { count: 0 } }, // 也可以这样定义状态 // state: () => ({ count: 0 }) actions: { increment() { this.count }, },})在组件中使用:
import { useCounterStore } from '@/stores/counter'export default { setup() { const counter = useCounterStore() counter.count // 编辑器会有代码提示 ✨ counter.$patch({ count: counter.count 1 }) // 也可以使用action来代替 counter.increment() },}你甚至可以用一个函数(像setup函数一样)来定义你的store:
export const useCounterStore = defineStore('counter', () => { const count = ref(0) function increment() { count.value } return { count, increment }})Pinia 优势符合直觉,易于学习极轻, 仅有 1 KB模块化设计,便于拆分状态