likes
comments
collection
share

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

作者站长头像
站长
· 阅读数 16

前置基本知识

vuedevtool功能简介

安装

安装过程就不再赘述,可以参考官方的文档 ,官网上也描述了devtools不生效的场景。

概览

左侧4个tab

  1. components(组件的详细信息)
  2. Timeline(记录了事件的触发时间)
  3. Routes(查看页面的路由,当你接手一个老的项目的话,这个是非常有用的,尤其是在路由是由后端拼接而成的场景)
  4. Vuex(查看Vuex的各种状态)

右侧点击点点点的设置

  1. Component names 设置组件名称的显示格式
  2. Editable props 是否允许编辑props
  3. Hightlight updates 当更新的时候是否高亮

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

全局配置(通过点击上方的GLobal settings)

从左到右依次是

  1. Theme(主题色)
  2. Menu Step Scrolling(没用过)
  3. Performance monitoring (性能监控)推荐开启,我们在下面的案例中会用到
  4. Update tracking(更新追踪)
  5. Debugging info(调试信息)

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

下面我们依次介绍下每个模块的功能

具体的功能和使用使用场景

  • components

方便查询当前组件的props、data、computed和route的一些信息

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化 当你调试的时候可以直接修改props,在开发阶段很有用

  • Timeline

记录了每个时间点的鼠标事件(Mouse)、键盘事件(keyboard)

鼠标事件截图 让页面变得无比流畅——使用vueDevtools和Performance进行性能优化 键盘事件截图

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化 组件加载的时间

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

  • Routes

记录了你的页面的所有的路由,方便你在接手一个新的项目的时候快速了解项目

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

  • Vuex或者Pinia

在这个tab页面,你可以不用通过console看到当前Vuex或者Pinia的信息,提升你的开发效率 让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

chrome开发者工具 performance

打开F12,将我们的tab切换到Performance,我们接下来会用到这个页面。

performance官方的解释:使用性能洞察面板获取有关您网站性能的可操作和用例驱动的洞察。说简单点就是来帮助我们进行优化的。

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

基本用法

我只介绍下基本的怎么用。如果你想系统学习,请直接去观看官方的学习视频。点击这里直接查看官方文档

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

案例分享

背景

一天领导安排做一个页面的优化。有一个功能是自定义表单有一个流程配置了181个组件,在电脑上候渲染了7秒多,需要优化一下。

页面的构成是这样的

  1. 一个自定义表单,里面有各种各样的组件,如datetimepicker、input、select、table等
  2. 当页面进入的时候,调用后端的接口拿到配置,然后for循环渲染页面

思考

拿到这个需求,先理清思路,我们可以按照下方的步骤来做

  1. 先统计接口耗时,观察是否受到接口的影响。如果需要后端协助,及时找领导安排后端人员的排期,免得到时候乱了阵脚
  2. 用vueDevtool记录组件耗时,找出耗时的组件,逐个攻破
  3. 用performance分析,找出问题点

接口排除

通过network查看接口耗时大概在50ms,没必要优化

统计组件渲染时长

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

注意看:两个时间的间隔就是当前组件的渲染时间

重点重点重点 从页面上可以看到关键的4188.8直接到5078.ms

说明有个组件耗时过长了,耗时大概在890ms。展开后发现是dateTimePicker

验证

  1. 将当前的表单配置复制一份出来,方便对比。
  2. 新的表单配置去除了3个dateTimePicker
  3. 用同一个手机进行验证

结果如下

原流程耗时9.7s 新流程耗时5.6s

说明我们的排除思路是有效的

从VueDevtool得出的解决方案

  1. 可以写一个和dateTimerPciker同样样式的组件,当点击的时候,渲染真正的dateTimePicker组件
  2. 如果时间充裕的话可以看下dateTimePicker的源码。clone一份,自己研究下组件为什么耗时这么久
  3. 同理我们可以继续找出耗时的组件并且统计出来,然后解决

根据performance排查

接口耗时50ms,那么时间就耗费在渲染dom上,测试后发现假如是181个组件的话,需要等待for循环结束后页面才渲染出来。

下方为performance截图的截图。在 performance 面板中,通过看火焰图分析 call stack 和执行耗时。火焰图中每一个方块的宽度代表执行耗时,方块叠加的高度代表调用栈的深度。

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化 通过上方的截图可以发现两个问题

  1. 获取配置的接口在netWork中耗时50ms,在performance中看到xhrload中时间线比较长,耗费了1.76s,大胆的猜测应该是有更高优先级的任务在执行
  2. Run Microtasks(微任务)耗费了1.76s

针对上面的第一个问题先寻找原因:

查阅文档后发现XHR是宏任务,Fetch请求是微任务,我们用的是XHR(微任务比宏任务执行的时间要早,微任务总是在宏任务之前去执行)。

既然第一个问题解释的通了,我们优化的方向就变成如何缩短Microtasks的时间

首次尝试:时间分片

这个概念还是在react filber的时候学会的。

目前这个流程dom正常的渲染需要3000多ms,共计181个组件。

181个组件是否可以分成5份,40个组件展示的内容足够填充满用户的首屏了。

先渲染一部分组件,然后再慢慢渲染第二组、第三组、第四组直到渲染结束,缓解用户的等待焦虑。

下方是编码的思路

  1. 对接口返回值进行group分组,
  2. 先渲染第一部分,等第一部分渲染完毕再渲染第二部分再重复进行(在nextTick中使用setTimeout进行对变量的赋值,利用js的异步事件机制来控制渲染)

下面是改造后的performance截图

  1. 任务已经分成了多个,在一次循环后再渲染另外的一组数据
  2. 同时Xhr执行时间也降低了

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

别的思路尝试

我们注意到我红色圈出的这里特别长,代表我们的调用堆栈过长。

让页面变得无比流畅——使用vueDevtools和Performance进行性能优化

在新增的页面要使用vue的特性双向绑定,但是在详情页面只是展示,我们是否可以将绑定配置的对象变成Non-reactive data(避免vue的递归响应式)。

转为 Non-reactive data,主要有三种方法

  1. 数据没有预先定义在 data 选项中,而是在组件实例 created 之后再动态定义 this.configList (没有事先进行依赖收集,不会递归响应式);
  2. 数据预先定义在 data 选项中,但是后续修改状态的时候,对象经过 Object.freeze 处理(让 Vue 忽略该对象的响应式处理);
  3. 数据定义在组件实例之外,以模块私有变量形式定义(这种方式要注意内存泄漏问题,Vue 不会在组件卸载的时候销毁状态);

我测试下来影响不大,可能我这个数据不是那种很深或者数据量比较大。 但是也为大家提供一个思路

扩展阅读

我们知道setTimeout是有4ms的延迟,如果做的好的话,会去除这个4ms的延迟

我们可以使用下方的技术来实现(React Fiber的实现方法)

  • 使用 requestAnimationFrame 获取渲染某一帧的开始时间,进而计算出当前帧到期时间点;
  • 使用 performance.now() 实现微秒级高精度时间戳,用于计算当前帧剩余时间;
  • 使用 MessageChannel 零延迟宏任务实现任务调度,如使用 setTimeout() 则有一个最小的时间阈值,一般是 4ms;
// 当前帧到期时间点
let deadlineTime;
// 回调任务
let callback;
// 使用宏任务进行任务调度
const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;
// 接收并执行宏任务
port2.onmessage = () => {
  // 判断当前帧是否还有空闲,即返回的是剩下的时间
  const timeRemaining = () => deadlineTime - performance.now();
  const _timeRemain = timeRemaining();
  // 有空闲时间 且 有回调任务
  if (_timeRemain > 0 && callback) {
    const deadline = {
      timeRemaining,
      didTimeout: _timeRemain < 0,
    };
    // 执行回调
    callback(deadline);
  }
};
window.requestIdleCallback = function (cb) {
  requestAnimationFrame((rafTime) => {
    // 结束时间点 = 开始时间点 + 一帧用时16.667ms
    deadlineTime = rafTime + 16.667;
    // 保存任务
    callback = cb;
    // 发送个宏任务
    port1.postMessage(null);
  });
};

总结

  1. 当拿到一个页面后,我们先查看netWork的瀑布图进行分析,如果你不太懂可以查看我的这个文章进行简单的学习juejin.cn/post/722555…
  2. 用可视化工具vueDevtool或者ReactDevTool进行分析,分析出耗性能的组件,然后带着目的去优化
  3. 用Performance分析执行耗时,分析哪个阶段我们可以进行优化
  4. 多学习学习vue和react的设计思路,在我们的开发过程中如果遇到了同样的问题,可以参考这些开源组件的思想解决

参考

有道技术博客 techblog.youdao.com/?p=3055