← 中文资源

简体中文版本

长动画帧(LoAF)

LoAF 是浏览器时序 API,上报超过 50ms 的帧并归因到脚本、样式、布局与渲染各阶段,把交互冻结的原因暴露出来。

阅读英文原文

概念定义

用户对交互延迟的感知以帧为粒度:点击在下一个呈现帧出现前毫无意义。LoAF 上报每个超过 50 毫秒的帧,拆解时间去向并点名涉事脚本,把卡顿从体感变成可归因数据。

工作机制

类型为 long-animation-frame 的 PerformanceObserver 收到条目,携带 startTime、duration,以及含调用者信息与源码字符位置的脚本归因记录;渲染各阶段时长分别列出样式、布局、绘制与提交。配合元素计时可回溯到具体函数。

使用场景

面向 INP 优化的生产 RUM、发版后的回归分诊、第三方影响审计。帧事件量极大且归因载荷不小,接入遥测管线时应做采样。

常见误区

长任务只看任务,长帧覆盖整条管线:布局与绘制撑爆的帧里任务可能很小,长任务口径会漏掉。归因指向 bundle 而非意图,压缩后的代码需要 source map 才可执行。跨交互边界的帧可能重复计数,应与事件时序条目关联;旧浏览器只有长任务,管线要兼容两种条目。

代码示例

public class StringBuilderManipulation {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder(64);

        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        System.out.println("Built: " + sb);

        sb.insert(5, " Java");
        System.out.println("After insert: " + sb);

        sb.delete(5, 10);
        System.out.println("After delete: " + sb);

        sb.replace(0, 5, "Hi");
        System.out.println("After replace: " + sb);

        sb.reverse();
        System.out.println("Reversed: " + sb);

        String csv = String.join(", ",
            "Apple", "Banana", "Cherry", "Date");
        System.out.println("CSV: " + csv);

        String formatted = String.format(
            "Name: %s, Age: %d, Score: %.2f",
            "Alice", 30, 95.5
        );
        System.out.println(formatted);

        String stripped = "  padded  ".strip();
        System.out.println("Stripped: '" + stripped + "'");
    }
}