Q5 · 性能指标
如何用代码获取 TTI?指标分数段?怎么提升?
⚡ 速记答案(30 秒)
- 概念:TTI = 页面已经可见且主线程"足够空闲"并能快速响应输入的时间点
- 获取:可基于
longtask、FCP、网络静默等信号自己实现,或用 Lighthouse 算法 - 分数段(参考 Lighthouse):好 ≈ < 3.8s,一般 3.8–7.3s,差 > 7.3s
- 拆分长任务,避免一次执行大量 JS
- 代码分割、按需加载、减少同步计算
- 利用
requestIdleCallback、Web Worker 把非关键计算移出主线程
📖 详细讲解
TTI 定义
Time to Interactive:页面完全可交互的时间点,满足:
1. 页面已显示可见内容(FCP 之后)
2. 页面对大多数可见元素绑定了事件
3. 主线程足够空闲(5 秒内没有长任务)
长任务(Long Task)
超过 50ms 的任务会阻塞主线程,影响用户交互。
优化策略
1. 拆分长任务
• 使用 requestIdleCallback 分批处理
• 使用 setTimeout(fn, 0) 让出主线程
2. 减少 JS 执行时间
• 代码分割
• 延迟非关键脚本
• Tree-shaking
3. 移出主线程
• Web Worker 处理计算密集型任务
💻 代码示例
监听长任务
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('长任务:', entry.duration, 'ms');
// duration - 50 = 阻塞时间
console.log('阻塞时间:', entry.duration - 50, 'ms');
}
});
observer.observe({ type: 'longtask', buffered: true });拆分长任务
// 使用 scheduler.yield() (实验性)
async function processLargeArray(items: any[]) {
for (let i = 0; i < items.length; i++) {
processItem(items[i]);
// 每处理 100 个项目让出主线程
if (i % 100 === 0) {
await scheduler.yield?.() ?? new Promise(r => setTimeout(r, 0));
}
}
}
// 使用 requestIdleCallback
function processInIdle(tasks: (() => void)[]) {
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
const task = tasks.shift();
task?.();
}
if (tasks.length > 0) {
processInIdle(tasks);
}
});
}面试技巧:回答性能问题时,先说指标和标准,再讲优化手段,最后结合实际项目经验。