跳至正文
Shopify · Google SEO

Shopify INP 与主线程:脚本、事件处理与交互延迟

// / / 光算科技

INP 衡量的是用户一次点击、轻触或按键之后到画面给出反馈之间的延迟,跟页面加载快不快是两件事。所以会出现这种情况:加载分数漂亮的店,用户点「加入购物车」半天没反应。

一次交互里其实有三段延迟

INP 观察用户在一次访问里的所有点击、轻触和键盘交互,最终值取最慢的那些交互——交互数量多的页面按每 50 次忽略一个最高值的方式剔除异常,然后按 75 分位报告(web.dev 的 INP 说明)。官方给的参考是 200 毫秒或更低属于好,大于 500 毫秒属于差(Web Vitals 概览)。

INP 排查三步:它衡量的是交互后的反馈速度

它衡量的是交互后的反馈速度(光算 · 示意图)

一段交互延迟由三部分组成:输入延迟是事件处理函数开始执行之前的等待,通常来自主线程上的长任务;处理时长是所有回调跑完的时间;呈现延迟是回调结束到下一帧真正画出来之间的时间。三段里任何一段被阻塞,用户感受到的都是「点了没反应」。

指标只观察点击、轻触、按键。悬停、缩放、滚动不计入,所以「滚动很顺」和「INP 好」没有必然联系。INP 也是 FID 的替代者:FID 只看首次交互的输入延迟,INP 看全部交互的三段延迟。

还有一种情况要认识:用户从头到尾没有交互、只用滚动浏览,或者访问者是爬虫这类不会交互的客户端,页面就没有 INP 值可报。报告里看到「无数据」,不能读成「响应很快」。

主线程上最常堵路的几件事

浏览器的主线程同时负责解析、样式计算、布局、绘制和 JavaScript 执行。任何一段长任务占着它,交互就只能在队列里等。官方文档说得很直接:驱动交互性的主要是 JavaScript。在 Shopify 店里,下面几类最常见。

  • 第三方脚本:统计、热图、客服、评论、推荐、AB 测试、同意管理,全部跑在同一条主线程上。
  • 滚动监听与滚动触发的动画:滚动事件触发频率很高,处理函数里读写 DOM 就很容易超出一帧。
  • 集合页的筛选与排序:一次点击要重新过滤并重排几十上百个卡片。这类交互的状态很复杂,容易误判成别的原因,排序与筛选交互的排查讲的是状态与 URL 层面,性能层面要另做测量。
  • 未节流的输入处理:搜索框每敲一个键就发一次请求或者重排一次结果。
  • 加购、弹窗开关这类高频操作被排在一长串回调后面。

还有一个容易漏的来源在主题自己身上:推荐位、评论区块如果一次渲染几百个卡片,或者在渲染后又跑一遍计算,同样会占住主线程。它的归属是主题而不是应用,改起来反而更自由,先看渲染能不能分批、能不能只渲染当前可见的那一段。

排查顺序:先定交互,再定脚本,最后定处置

顺序不能换。先决定要改哪个脚本再去找证据,最后往往改了不痛的地方。

脚本的三类处置:不要一刀切删除

不要一刀切删除(光算 · 示意图)

第一步是先确定哪一类交互慢。 实验室数据在这件事上只是近似:实验室测出来的 INP 取决于你测试时做了什么操作,用户行为不可预测,实验室不一定能复现出真实的问题交互;有些工具甚至不报 INP,因为它只观察加载、根本不产生交互。没有真实用户数据时,Total Blocking Time 可以作为参考指标,但它替代不了 INP。可行的实验室做法有两条:按常见用户流程走一遍,从首页到集合页到产品页再到加购;以及特意在页面还在加载的时候去交互,那正是主线程最忙的时候。

第二步是定位到具体脚本。 用 Performance 面板录一段带交互的 trace,看长任务落在哪个文件上;Network 面板看哪个第三方域名在交互前后发请求。这里要注意工具之间的固有差异:iframe 里发生的交互会计入页面的 INP 指标,但前端 JavaScript API 拿不到跨域 iframe 内的计时数据,所以后台数据和自己测出来的值对不上,有时是正常的,不必因此怀疑改动本身。

第三步是决定处置方式。 从影响面小、可回退的做法开始,逐步升级到结构变更,每改一步复测一次。

排优先级时也有个顺序:先做影响面大、回退容易的,比如改主题自己的事件处理、把非首屏脚本延迟加载;卸载应用和合并脚本会牵动业务功能,往后放,而且一次只动一个。

处置方式的取舍

处置适合什么代价
延迟到空闲或首次交互后加载客服组件、非首屏统计、弹窗首次交互会触发它自己的加载,那一次反而更慢
按页面条件按需加载只在特定模板用的功能条件写错就漏加载,要逐个模板确认
合并同一来源的脚本同一供应商的多个片段合并后一个失败全失败,排查更麻烦
换成平台原生功能简单评论展示、相关产品、基础促销功能深度有限,复杂需求做不了
直接移除装了没用、功能重叠的应用要先确认没有内容依赖它

到底该合并、延迟还是移除,取决于它是不是必需。第三方脚本审计给的是清单与归属方法,先有清单再谈处置;功能能不能换掉、换掉会丢什么,见哪些插件能用原生功能替代

实验室里怎么把慢交互逼出来

真实用户数据是判断依据,但改动的时候总得在本地复现。官方给的两条策略是:跟着常见用户流程走,在流程的每个节点上都交互一次;以及刻意在页面还在加载的过程中去交互,因为那段时间主线程最忙。除此之外还要把条件调到接近用户的水平,录制时开启 CPU 节流模拟中低端机型,用未缓存状态跑,而不是在一台性能过剩的机器上开着缓存测。

要自己收集真实用户数据,不必从零实现测量逻辑。INP 需要在页面卸载或者切到后台时上报,还要处理标签页长期不关、移动端不触发卸载回调这些情况,官方库已经把这些都覆盖了(跨域 iframe 的情况除外):

import { onINP } from 'web-vitals';
onINP(console.log);

把它接到你已有的分析系统上,先积累一段时间的数据再定目标。没有数据的时候最容易做的事是照别人的清单删脚本,而那份清单不适用于你的店。

事件处理本身的写法也要看

第三方脚本之外,主题自己的事件处理经常就是三段延迟的来源。三条原则足够实用。

  • 不要在事件回调里同步读写布局。一次处理里反复读取位置信息再写样式,会触发多次强制重排,处理时长直接被拉长。
  • 把长任务拆小。一次点击里遍历大数组、重排几百个卡片,都是典型的长任务。
  • 高频事件要有节制。搜索框输入、滚动位置这类触发密集的事件,处理函数要么只做轻量更新,要么等用户停下来再做重活。

这些属于主题层可以直接改的部分,风险低,建议排在删脚本之前做,因为删脚本需要先有归属清单,而改自己的代码只需要看懂一段回调。

不要一刀切删脚本

先把脚本分成两类。结账相关、客服、必要的统计、同意管理这些通常动不了,删了会直接影响成交或者数据口径;热图、AB 测试、重复的评论工具、已经卸载应用留下的残留脚本,才是优先级高的处理对象。

删之前先确认这段代码从哪来。主题文件里搜得到的是主题层的,可以直接改;搜不到的多半是应用注入,需要从应用侧关掉或卸载,残留清理见卸载插件后的残留清理。搞错归属的代价是把应用的问题当成主题的,改半天不见效。

删掉之后也别急着下结论。INP 受用户设备、网络和页面使用方式影响,一次实验室测量只能说明那次快了,真实用户数据要看一段时间,口径见Core Web Vitals 基线怎么建后台报告与实验室数据的差别

边界

实验室跑一次好分数不代表真实用户的体感好,反过来也一样。工具结果受测试设备与网络影响,要看多次、多条件的结果,别用一次记录下结论。

后台报告是真实用户数据的聚合,与单次模拟本来就不是同一口径,对不上不代表谁错了。跨域 iframe 里的交互前端有时测不到,工具之间的差值是正常现象,这点前面说过,这里再强调一次,因为它最容易被当成「改动没生效」。

应用注入的脚本主题层改不了,这类问题要么与应用方沟通,要么评估卸载,别在主题里绕圈。平台侧的 CDN 与图片处理不是主线程压力的大头,力气别花错地方。

还要记得 INP 是页面级指标。同一套脚本在首页可能没问题,在产品页因为交互多、DOM 更大就出问题;报告里看着「整站还行」,有时是首页在拉平均分。按页面类型分开看,把力气放在交互最密集的那些模板上。

这类整站速度问题通常要和抓取、收录一起看:脚本量、渲染方式、页面被看到的程度互相牵扯。我们在谷歌 SEO 技术整改里按同一套基线口径推进,改前改后同口径对比,不承诺具体分数。