优化输入延迟

了解什么是输入延迟,并学习如何减少输入延迟以提高互动速度。

网络上的互动非常复杂,浏览器中会发生各种活动来驱动这些互动。不过,它们的共同点是,在事件回调开始运行之前,它们都会产生一些输入延迟。在本指南中,您将了解什么是输入延迟,以及如何最大限度地减少输入延迟,从而加快网站的互动速度。

什么是输入延迟?

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

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

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

如何看待输入延迟

一般来说,您需要尽可能缩短互动的每个部分,以便您的网站有最大可能达到 Interaction to Next Paint (INP) 指标的“良好”阈值,无论用户使用何种设备。控制输入延迟只是达到该阈值的一个方面。

因此,您需要尽可能缩短输入延迟时间,以达到 INP 的“良好”阈值。不过,您应该知道,您无法完全消除输入延迟。只要您在用户尝试与网页互动时避免执行过多的主线程工作,输入延迟时间应该就会足够低,从而避免出现问题。

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

如前所述,有些输入延迟是不可避免的,但有些输入延迟是可以避免的。如果您遇到输入延迟时间过长的问题,请考虑以下事项。

避免使用会启动过多主线程工作的周期性计时器

JavaScript 中有两个常用的计时器函数可能会导致输入延迟:setTimeout 和 setInterval。两者的区别在于,setTimeout 会安排回调在指定时间后运行。setInterval 则会安排一个回调,使其每 n 毫秒运行一次,直到永远,或者直到使用 clearInterval 停止计时器。

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

此外,setTimeout 可以在循环中运行或以递归方式运行,其行为更像 setInterval,但最好在上一迭代完成之前不要安排下一迭代。虽然这意味着每次调用 setTimeout 时,循环都会让位于主线程,但您应注意确保其回调最终不会执行过多工作。

setInterval 会按一定间隔运行回调,因此更有可能妨碍互动。这是因为,与单次 setTimeout 调用(这是一种可能会妨碍用户互动的一次性回调)不同,setInterval 的周期性特征使其很可能会妨碍互动,从而增加互动的输入延迟时间。

Chrome 开发者工具中性能分析器的屏幕截图,展示了输入延迟。由计时器函数触发的任务发生在用户发起点击互动之前。不过,计时器会延长输入延迟时间,导致互动的事件回调运行时间晚于正常情况。
Chrome 开发者工具的“性能”面板中显示的由之前的 setInterval 调用注册的计时器,该计时器会导致输入延迟。增加的输入延迟会导致互动的事件回调运行时间晚于原本可能的时间。

如果计时器出现在第一方代码中,则您可以控制它们。评估您是否需要这些方法,或者尽最大努力尽可能减少这些方法中的工作。不过,第三方脚本中的计时器则另当别论。您通常无法控制第三方脚本的行为,而修复第三方代码中的性能问题通常需要与利益相关方合作,确定给定的第三方脚本是否必要,如果必要,则与第三方脚本供应商建立联系,确定可以采取哪些措施来修复他们可能在您的网站上造成的性能问题。

避免长任务

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

长任务如何延长输入延迟时间的可视化图表。在顶部,互动在单个长任务运行后不久发生,导致严重的输入延迟,从而导致事件回调的运行时间比预期晚得多。在底部,交互发生的时间大致相同,但长任务通过让出机制被拆分为多个较小的任务,从而使交互的事件回调能够更快运行。
可视化展示了当任务过长且浏览器无法足够快地响应互动时,互动会发生什么情况,以及当较长任务被拆分为较小任务时,互动会发生什么情况。

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

注意互动重叠

如果存在重叠的互动,优化 INP 可能会特别具有挑战性。互动重叠是指,在您与某个元素互动后,您在初始互动有机会渲染下一帧之前,又与页面进行了另一次互动。

描绘了任务何时可能重叠,从而导致输入延迟时间过长。在此描述中,点击互动与 keydown 互动重叠,从而增加了 keydown 互动的输入延迟。
Chrome 开发者工具的性能分析器中两个并发互动的可视化图表。初始点击互动中的渲染工作会导致后续键盘互动的输入延迟。

互动重叠的原因可能很简单,例如用户在短时间内进行了多次互动。当用户在表单字段中输入内容时,可能会发生这种情况,因为在很短的时间内可能会发生许多键盘互动。如果按键事件的处理工作特别耗费资源(例如在常见情况下,自动补全字段会向后端发出网络请求),您有以下几种选择:

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

重叠互动导致输入延迟时间增加的另一个原因是动画开销过大。具体而言,JavaScript 中的动画可能会触发许多 requestAnimationFrame 调用,这可能会妨碍用户互动。为解决此问题,请尽可能使用 CSS 动画,以避免将可能开销高昂的动画帧排入队列。不过,如果您这样做,请务必避免使用未合成的动画,以便动画主要在 GPU 和合成器线程上运行,而不是在主线程上运行。

总结

虽然输入延迟可能不占互动运行时间的大部分,但请务必了解,互动的每个部分都会占用一定的时间,而您可以减少这些时间。如果您观察到输入延迟时间较长,则有机会缩短延迟时间。避免重复的计时器回调、拆分长任务以及注意潜在的互动重叠,这些都有助于减少输入延迟,从而提高网站用户的互动速度。