INP 是 PWA 安装信任中最重要的 Core Web Vitals 指标
用户不会安装感觉慢的应用。INP 衡量影响安装决策的真实响应能力。
为什么在已安装的 PWA 中 INP 比 LCP 更重要
Largest Contentful Paint (LCP) 衡量加载速度。Interaction to Next Paint (INP) 衡量交互速度。对于已安装的 PWA,用户已经"支付"了加载成本——他们期望从那里获得快速响应。高 INP 分数会让应用感觉"坏了"或"低质量",直接影响安装保留率和口碑推荐。
根据 web.dev 的 Core Web Vitals 指南,INP 取代了 First Input Delay (FID) 作为响应性指标,因为 FID 仅衡量第一次交互——现在是一致的响应性。
INP 实际测量什么
INP 捕获最长的交互延迟,从:
- 用户点击/按钮按下
- 主线程开始处理
- 视觉反馈在屏幕上渲染
这包括所有三个阶段:
- 输入延迟:用户操作时主线程被阻塞
- 处理时间:事件处理程序、JavaScript 执行
- 呈现延迟:浏览器渲染视觉变化
良好 INP:< 100ms 需改进:100ms - 200ms 差:> 200ms
为什么高 INP 破坏 PWA 信任
想想用户期望:
- 原生应用:按钮点击 → 视觉反馈约 16ms(1 帧)
- 慢 Web:按钮点击 → 加载转圈、延迟、渲染(~800ms)
- 好 PWA:按钮点击 → 100ms 内反馈(接近原生感觉)
当用户安装"类原生"Web 应用时,他们潜意识中会以实际原生应用为基准。高 INP 让 PWA 感觉"廉价"或"坏了",导致:
- 立即卸载:"这真的不是原生应用"
- 差评:"慢"、"无响应"、"故障频发"
- 拒绝重装:"这个品牌的 Web 应用质量很差"
PWA 特定的 INP 陷阱
PWA 比普通 Web 页面有更高的 INP 风险:
| 风险因素 | 为什么 PWA 脆弱 | 修复 | |----------|-----------------|------| | 离线优先架构 | IndexedDB 读取可阻塞主线程 | 使用 IDB Keyval 模式或后台同步 | | Service Worker 生命周期 | 激活竞争条件延迟事件 | 预热 SW,回退到主线程 | | Manifest 截图生成 | 安装前渲染耗时 | 预生成,使用更简单格式 | | 大型 JavaScript 包 | 已安装应用缓存不完美 | 代码分割,延迟加载路由 | | 平台检测代码 | 每次交互都运行特性检查 | 在模块作用域缓存检测结果 |
PWA 的实用 INP 优化
1. 提前加载 JavaScript 执行
// 在安装按钮显示前
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
// 缓存用户能力
const supportsFileAPI = 'showOpenFilePicker' in window;
const supportsClipboard = 'clipboard' in navigator;
}2. 将昂贵工作安排在交互后
// 安装按钮处理程序(快!)
button.onclick = () => {
// 立即反馈:显示加载转圈
showLoadingSpinner();
// 昂贵工作:帧后安排
requestAnimationFrame(() => {
setTimeout(async () => {
await prewarmServiceWorker();
await cacheOfflineAssets();
await verifyInstallCriteria();
}, 0);
});
};3. 避免布局抖动
// 坏:读-写-读-写模式
const height = el.clientHeight; // 强制布局
el.style.height = height + 10 + 'px';
const width = el.clientWidth; // 再次强制布局
// 好:批量读取,然后批量写入
const heights = elements.map(el => el.clientHeight);
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px';
});4. 使用 Web Workers 处理繁重任务
// 不要阻塞主线程
const worker = new Worker('/offline-validator.js');
worker.postMessage({ action: 'validate-manifest' });
worker.onmessage = (event) => {
if (event.data.valid) showInstallButton();
};平台特定的 INP 行为
iOS Safari PWA
- WebView 调度延迟更高(~20-30ms 基线)
- IndexedDB 读取串行化,可阻塞主线程
- 修复:小型读取使用 IndexedDB Keyval 模式
Android Chrome PWA
- 较低基线延迟(~10-15ms)
- V8 更好地优化热路径
- 修复:将交互代码保留在主包中,避免热路径的动态导入
桌面 PWA
- 鼠标 vs 触摸:鼠标约 8ms 延迟 vs 触摸约 30ms
- 窗口调整大小事件可使 INP 尖峰
- 修复:节流调整大小,使用 ResizeObserver 代替
测量工作流
Chrome DevTools Performance 面板:
- 加载你的 PWA
- 打开 Performance 面板
- 开始录制
- 点击按钮/交互 5-10 次
- 停止录制
- 查找 "INP" 指标或长任务(>50ms)
自动化测试:
// Lighthouse CI + INP 检查
await lighthouse('https://pwa.example.com', {
onlyCategories: ['performance'],
thresholds: {
'interaction-to-next-paint': 100 // ms
}
});INP vs 其他指标:报告什么
| 指标 | 对于 PWA,如果... 专注于此 | |------|---------------------------| | INP | 用户点击、点按、表单提交 | | LCP | 首次加载、深度链接 | | CLS | 动态内容、动画、轮播 | | FID | 较旧浏览器(Chrome 95 及以下)|
什么时候"够好"就真的够好了
INP 目标取决于 PWA 类别:
- 电商 PWA:目标 <100ms(用户放弃慢购物车)
- 媒体 PWA:150ms 可接受(每次交互需求较少)
- 工具 PWA:<200ms 可容忍(交互较少,更基于任务)
- 社交 PWA:<80ms 理想(连续交互循环)
下一步
使用 DevTools 测量你 PWA 的当前 INP,然后使用 requestAnimationFrame + setTimeout 批处理将昂贵工作(service worker 预热、缓存预热)安排在用户交互之外。