Vue渲染性能优化实践

2,481 阅读2分钟

在项目规模增大、工程迭代、需求变更、数据量增长的背景下,项目的性能问题会渐渐浮现。

实际开发中,我做了一个类表格组件,但出现了极其严重的性能问题,导致整个单页渲染在数据量大的情况下需要接近10秒才渲染完成,因此开启了性能分析与优化之路。

借助Chrome控制台的performance,点开重载页面并进行分析来看当前页面情况:

选中时间轴片段,在时间轴中,紫色块表示计算样式,进行重绘或者回流,黄色块为JS脚本执行,浅绿色代表帧率。

由此看出在我的页面中js执行花费了非常长的时间, 在其中一个接口的回调执行栈中总共执行了9.12s。点开Main查看整个函数调用栈:

一目了然,在上千条的数据量下,Select组件的渲染有着极大开销,因此我针对Vue框架的特点,做深度优化:

  1. 列表渲染加key值。万人皆知,但加key值并不会改善首次渲染速度。

  2. 部分组件改为functional组件。functional函数式组件,与react中相同,特点是stateless。vue官网介绍: 函数式组件

  3. 尽量不要在mounted中设置data新值。会触发重渲染。

  4. 数据缓存

  5. 阻止部分数据被vue转为响应式数据。Vue会把data中(深度遍历)的所有key转为响应式,即访问器属性,但有些数据仅仅只是做展示,我们并不希望vue去这么做,因此有两种做法:

    • Object.freeze()。冻结对象,防止对象被修改。
    • 不在data中设置初始值(不推荐)。以下是Vue 的setter源码,当有新值时,新值也会被observe转为访问器属性,因此只要我们的初始值并没有设置,新值并不会是响应式的。
set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal) // observe新值
      dep.notify()
    }

实现该几点后,效果明显提升,再来一遍performance分析,渲染速度有了些提升,但这是远远不够的。

由于分页、懒加载等不满足业务需求,最后我重写该Select组件,原因:select包括它内部的子组件的初始化代码在我的业务下太多余,上千条数据下这样的性能浪费是非常奢侈的,重写之后的performance: