优化输入延迟

了解什么是输入延迟,并学习减少这类延迟以提高互动速度的技巧。

网络上的互动很复杂,因为浏览器中的各类活动都会产生这种影响。不过,它们的共同点是,它们会导致事件回调开始运行之前出现一些输入延迟。在本指南中,您将了解什么是输入延迟,以及如何最大限度地缩短输入延迟,以便加快网站互动的运行速度。

什么是输入延迟?

输入延迟是指从用户首次与页面互动(例如点按屏幕、使用鼠标点击或按下按键)开始,直到互动的事件回调开始运行为止的一段时间。每次互动都始于一定程度的输入延迟。

简化的输入延迟可视化效果。左侧是一张鼠标光标的线条图,后面有一个星形图案,表示互动已开始。右侧是齿轮的线条图,表示互动的事件处理脚本何时开始运行。中间的空格被标注为带有大括号的输入延迟。
输入延迟背后的机制。操作系统收到输入内容后,必须先将其传递给浏览器,然后才能开始互动。这需要一定的时间,并且可能会因现有的主线程工作而增加。

部分输入延迟是不可避免的:操作系统始终需要一些时间来识别输入事件并将其传递给浏览器。但是,这部分输入延迟通常并不明显,并且网页上本身发生了其他一些情况,可能会导致输入延迟足够长,进而导致问题。

如何看待输入延迟

一般来说,您应尽可能缩短互动的每个部分,这样,无论用户使用的是何种设备,您的网站都有可能达到互动次数进行下一次绘制 (INP) 指标的“良好”阈值。检查输入延迟只是满足该阈值的一个环节。

因此,您应力求尽可能缩短输入延迟,以达到 INP 的“良好”阈值。但请注意,您不能期望完全消除输入延迟。只要您避免在用户尝试与您的页面交互时执行过多的主线程工作,您的输入延迟就应该足够短,以免出现问题。

如何最大限度地缩短输入延迟

如前所述,有些输入延迟是不可避免的,但另一方面,某些输入延迟是可以避免的。如果您遇到了较长的输入延迟问题,请考虑以下事项。

避免计时器会启动过多的主线程工作

JavaScript 中有两种会导致输入延迟的常用计时器函数:setTimeoutsetInterval。两者之间的区别在于,setTimeout 会调度回调在指定时间之后运行。另一方面,setInterval 会永久地或每 n 毫秒运行一次回调,或直到通过 clearInterval 停止计时器为止。n

setTimeout 本身没有问题 - 事实上,它可以帮助您避免耗时较长的任务。不过,这取决于超时的发生时间,以及超时回调运行时用户是否尝试与网页互动。

此外,setTimeout 可以循环运行或以递归方式运行,此时它的作用更类似于 setInterval,但最好在前一次迭代完成之前不安排下一次迭代。虽然这意味着每次调用 setTimeout 时,循环都会让出到主线程,但您应谨慎处理,以确保其回调最终不会执行过多的工作。

setInterval 按一定时间间隔运行回调,因此更有可能妨碍互动。这是因为,与 setTimeout 调用的单个实例(可能会阻碍用户互动的一次性回调)不同,setInterval 的重复性更有可能会干扰互动,从而增加互动的输入延迟。

Chrome 开发者工具中性能分析器的屏幕截图,展示了输入延迟。由计时器函数触发的任务会在用户开始点击互动之前发生。不过,计时器会延长输入延迟,导致交互的事件回调的运行时间晚于其他时间。
由之前的 setInterval 调用记录的导致输入延迟的计时器,如 Chrome 开发者工具的性能面板所示。增加的输入延迟会导致互动的事件回调的运行时间晚于其他时间。

如果计时器发生在第一方代码中,那么您就可以控制它们。请评估您是否需要它们,或者尽最大努力减少其中的工作。不过,在第三方脚本中,计时器则另当别论。您通常无法控制第三方脚本的用途,而修复第三方代码的性能问题通常涉及与利益相关方合作,以确定是否有必要使用给定的第三方脚本。如果有必要,请与第三方脚本供应商联系,以确定采取什么措施来修复它们可能对您网站上的性能问题造成的问题。

避免耗时较长的任务

减少长时间输入延迟的一种方法是避免使用冗长的任务。如果在互动期间有过多的主线程工作,导致主线程阻塞,那么在较长的任务有机会完成之前,这些线程的输入延迟时间就会增加。

直观呈现任务延长输入延迟的时长。顶部,交互会在单个长时间运行的任务运行后不久发生,导致严重的输入延迟,导致事件回调的运行时间远远晚于预期。在底部,交互会大致同时发生,但是较长的任务会通过让让被分解为几个较小的任务,从而使交互的事件回调能够更快地运行。
直观呈现当任务过长且浏览器无法足够快速地响应互动时,互动会发生的情况,而不是将较长的任务分解为更小的任务时。

除了最大限度减少任务中的工作量(您应始终尽可能减少在主线程上执行的工作)之外,您还可以通过拆分冗长的任务来提高对用户输入的响应速度。

注意互动重叠

如果您的互动存在重叠,那么在优化 INP 时尤为困难。互动重叠是指,在您与一个元素互动后,您在首次互动还没来得及呈现下一帧之前,与网页进行了另一次互动。

描绘何时任务可能会重叠以产生较长的输入延迟。在此描绘中,点击互动与 keydown 互动重叠,增加了 keydown 互动的输入延迟时间。
Chrome 开发者工具性能分析器中两个并发交互的可视化效果。初始点击互动中的渲染工作会导致后续键盘互动出现输入延迟。

互动重叠的原因可能只是用户在短时间内进行多次互动。当用户在表单字段中输入内容时,可能就会发生上述情况,而在这些字段中,可能会在很短的时间内发生很多键盘互动。如果处理关键事件的工作成本特别高(例如,向后端发出网络请求的自动补全字段这种常见情况),您有以下两种选择:

  • 考虑对输入进行去抖动处理,以限制事件回调在给定时间段内的执行次数。
  • 使用 AbortController 取消传出的 fetch 请求,以免主线程在处理 fetch 回调时出现拥塞。注意:AbortController 实例的 signal 属性还可用于中止事件

因相互重叠而造成输入延迟增加的另一个原因是动画开销高昂。特别是,JavaScript 中的动画可能会触发许多 requestAnimationFrame 调用,这可能会妨碍用户互动。为了解决此问题,请尽可能使用 CSS 动画来避免将可能昂贵的动画帧排入队列。但是,如果您这样做,请确保避免未合成的动画,使动画主要在 GPU 和合成器线程上运行,而不是在主线程上运行。

总结

虽然输入延迟时间可能并不代表互动的大部分时间,但请务必注意,互动的每个环节都会占用您较少的时间。如果您观察到输入延迟很长,则有机会减少延迟。避免反复计时器回调、拆分耗时的任务以及注意潜在的互动重叠都可以帮助您减少输入延迟,从而加快网站用户的互动速度。

主打图片来自 Unsplash 用户,由 Erik Mclean 制作。