分类
javascript vue 性能 算法 解决方案

做虚拟列表时所遇到的坑

页面跳动

出现这种情况的原因是虚拟列表所渲染出的Dom是有限的,向下滚动时需要将上方的Dom移除,向上滚动时需要添加上方的Dom

解决方案

降低Dom渲染的频率

  1. 我有一个当前所处位置的检测值命名为topIndex,以3的倍数进行检测,当我所处位置为1值为0,所处位置为4值为3
  2. 我有一个判断当前滚动方向的检测值,命名为slideArrow,当向下滑动屏幕是置为down,向上滑动屏幕时值为up
  3. 我有一个由topIndex和slideArrow组合而成的计算值,命名为topAndArrowChange
  4. 监听topAndArrowChange,当发生改变时判断滑动方向,改变虚拟列表的值
  5. 当快要到底部时会触发查找列表,数据会放进源列表recommendList中
  6. 当滑动方向为up向上滑动,看下面的内容。或者滑动方向为down向下滑动,看上面的内容。那么虚拟列表为源数据的截取值。
topAndArrowChange(newVal) {
      const circle = 30
      const circleNum = parseInt(this.topIndex / circle, 10)
      console.log(newVal)
      if (this.slideArrow === 'up') {
        this.virtualRecommendList = this.recommendList.slice(circleNum * circle, circleNum * circle + 60)
      } else {
        const start = Math.max((Math.round(this.topIndex / circle) - 1) * circle, 0)
        const end = Math.round(this.topIndex / circle) * circle + circle
        this.virtualRecommendList = this.recommendList.slice(start, end)
      }
    },

看下面的内容,截取的部分

如果当前的topIndex为40,则截取30至90,最大截取60条

看上面的内容,截取的部分

如果当前的topIndex是0,则截取0-30

如果当前的topIndex是36,则截取0-60

如果当前的topIndex是119,则截取90-150

如果当前的topIndex是129,则截取90-150

无限触发查找数据

出现原因是初始时虚拟列表没有赋值,滚动过快触底,虚拟列表来不急赋值

解决方案

  1. 添加源数据列表监听,发现源数据列表更新的同时修改虚拟列表的值
  2. 只有在查找数据时才会触发源列表数据更新,因此虚拟列表的截取值为
 // 防止无限触发查找列表
    recommendList() {
      const circle = 30
      const circleNum = parseInt(this.topIndex / circle, 10)
      this.virtualRecommendList = this.recommendList.slice(circleNum * circle)
    },

当还是以30为一个循环,最多截取不超过60个Dom

总结

综上,此方案虚拟列表最多渲染60条数据

分类
性能

页面性能如何判断

Light House

无法量化,但可以提高用户体验的行为

  • 使用下一代的图片格式,webp,AVIF,使用http2协议下载图片
  • 去除无效css
  • 屏幕外的图片推迟加载
  • 减少工作主线程
  • 避免巨大的网络负载

可以量化提高用户体验的行为

CLS页面视觉稳定分析

aHR0cHM6Ly9wNi1qdWVqaW4uYnl0ZWltZy5jb20vdG9zLWNuLWktazN1MWZicGZjcC82MGFjOWU5M2ZhOWI0OGZmYjNjNmMzMzI2NGI4OGFlNX50cGx2LWszdTFmYnBmY3Atem9vbS0xLmltYWdl 1024x290 - 页面性能如何判断

计算方式

CLS=距离分数*影响分数

影响分数 = 发生抖动的元素在可视区域中占据的百分比

距离分数 = 发生抖动的元素抖动距离占据可视区域的百分比

分数越低越稳定

FCP首次内容绘制时间

标记浏览器渲染来自DOM第一位内容的时间点

image - 页面性能如何判断

LCP视窗最大内容渲染时间

代表页面可视区域接近完整渲染

image 1 - 页面性能如何判断

加载进度条时间

onload事件启动时间

分类
性能

代码性能

此处收集一些代码性能提升方面的细节,持续更新

for循环

  • for(let i=0,len=arr.length;i<len;i++)比for(let i=0;i<=arr.length;i++)更快
  • 当复杂度等于O(n)着重减少每次迭代的工作量,当复杂度大于O(n)着重减少迭代次数
  • forEach比for慢

条件语句

  • if适合判断范围
  • 当判断离散值多余两个是选择switch而不是if
  • 当有大量的离散值更适合查找表,const arr = [result1,result2,…,result100]; return arr[index];

字符串连接

str+= ‘one’ + ‘two’浏览器运行逻辑

  1. 在内存创建一个临时字符串
  2. 连接后的字符onetwo赋值给临时字符串
  3. 临时字符串与str当前值连接
  4. 结果赋值给str

优化: str = str + ‘one’ + ‘two’;

  1. 以str为基础加上one
  2. 再加上two
  3. 当前值赋给str

结果少了一步创建临时字符串的过程,所以速度加快,若str = ‘one’ + str + ‘two’则优化无效

vue-使用函数式组件

递归

尾调用优化

// 优化后和优化前对比 
function factorial(n) {
        function fact(n, res) {
            if(n<2) return res;
            return fact(n-1,n*res);
        }
        return fact(n,1);
    }
    function _factorial(n) {
        if (n===1) return 1;
        return n*_factorial(n-1);
    }

原理:优化前的递归要为每次调用开辟一个帧栈,优化后可以重用fact(n,1)这个帧栈,减小空间复杂度。