优化输入延迟

了解输入延迟是什么,并学习缩短输入延迟时间的技巧,以提高互动速度。

网络互动非常复杂,浏览器中会发生各种活动来推动互动。不过,它们都有一个共同点,即在事件回调开始运行之前,都会产生一些输入延迟。在本指南中,您将了解什么是输入延迟,以及可以采取哪些措施来尽可能缩短输入延迟时间,以加快网站互动速度。

什么是输入延迟?

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

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

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

如何看待输入延迟

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

因此,您应力求尽可能缩短输入延迟时间,以满足 INP 的“良好”阈值。不过,请注意,您无法完全消除输入延迟。只要您在用户尝试与您的网页互动时避免过多的主线程工作,输入延迟应该足够低,以避免出现问题。

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

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

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

JavaScript 中有两个常用的计时器函数可能会导致输入延迟:setTimeoutsetInterval。两者的区别在于,setTimeout 会安排在指定时间后运行回调。另一方面,setInterval 会安排一个回调,以便在永久或在使用 clearInterval 停止计时器之前,每 n 毫秒运行一次。

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

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

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

Chrome 开发者工具中性能分析器的屏幕截图,演示了输入延迟。计时器函数触发的任务会在用户发起点击互动之前发生。不过,计时器会延长输入延迟时间,导致互动事件回调的运行时间比正常情况下晚。
上一次 setInterval 调用注册的一个计时器,会导致输入延迟,如 Chrome 开发者工具的“性能”面板中所示。添加的输入延迟会导致互动事件回调的运行时间比原来晚。

如果计时器是在第一方代码中运行的,那么您可以控制这些计时器。评估您是否需要这些步骤,或者尽可能减少这些步骤中的工作量。不过,第三方脚本中的计时器则另当别论。您通常无法控制第三方脚本的行为,因此要解决第三方代码中的性能问题,通常需要与利益相关方合作,确定给定第三方脚本是否必不可少;如果必不可少,则与第三方脚本供应商联系,确定可以采取哪些措施来解决它们可能会在您的网站上造成的性能问题。

避免长任务

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

直观呈现任务延长输入延迟的时长。在顶部,在单个长任务运行后不久就会发生互动,导致明显的输入延迟,从而导致事件回调比预期晚得多。从底层来看,交互大致在同一时间发生,但系统会通过让出时间来将长任务拆分为多个较小的任务,从而让交互的事件回调能够更早运行。
直观呈现以下情况:当任务过长且浏览器无法快速响应互动时,与将较长的任务拆分为较小的任务时,互动情况有何不同。

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

注意互动重叠

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

一张图片,显示了任务何时可能会重叠,从而导致较长的输入延迟。在此图示中,点击互动与按键互动重叠,以增加按键互动的输入延迟时间。
Chrome 开发者工具中性能分析器中两个并发互动的可视化图表。初始点击互动中的渲染工作会导致后续键盘互动出现输入延迟。

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

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

由于重叠互动而导致输入延迟增加的另一个原因可能是耗费资源的动画。特别是,JavaScript 中的动画可能会触发许多 requestAnimationFrame 调用,这可能会妨碍用户互动。为解决此问题,请尽可能使用 CSS 动画,以避免将可能耗费大量资源的动画帧加入队列。不过,如果您这样做,请务必避免使用未合成的动画,以便动画主要在 GPU 和合成程序线程上运行,而不是在主线程上运行。

总结

虽然输入延迟可能不是互动运行时间的大部分,但请务必了解,互动中的每个部分都会占用一定的时间,而您可以缩短这些时间。如果您发现输入延迟时间过长,则可以采取措施缩短延迟时间。避免重复的计时器回调、拆分长任务以及注意可能的互动重叠,都可以帮助您减少输入延迟,从而让网站用户更快地进行互动。

主打图片来自 Unsplash,作者:Erik Mclean