前言

2026 年,Andrej Karpathy 公开了 autoresearch。对我最有启发的地方,不是训练小模型这件事本身,而是它把研究组织成了一套有边界、有基线、有保留和放弃规则的持续实验机制。

这套思路很适合迁移到前端性能优化领域。相比一次性的性能专项,它更强调先固定实验环境、限制改动范围、统一记录结果,再让 agent 在明确规则下持续提出假设、验证假设、保留有效改动。

在这套方法的指引下,我把前端性能优化组织成一套“固定实验室环境、限制改动范围、小步实验、统一记录结果、只保留被证明有效的改动”的持续优化系统。

从小处着手

第一次落地这套前端性能实验方法时,只选:

  • 一个关键页面或关键交互路径
  • 一个北极星指标
  • 一类允许改动的优化面

推荐的上手方式:

  • 落地页首屏加载
  • 商品详情页首屏与交互
  • 工作台首页首屏
  • 编辑器内某条关键交互链路

不推荐第一次就做:

  • “整个站点性能治理”
  • “一口气把 LCP、INP、CLS、SEO、错误率都一起优化”
  • “允许 agent 改所有前端和 Node 代码”

第一步:定义北极星指标

每轮实验只设一个主目标,推荐从下面选一个:

  • p75 LCP
  • p75 INP
  • p75 CLS
  • 首屏 JS 传输体积
  • 某关键交互的 long task 总时长

推荐口径:

  • 选一个最接近用户真实痛点的指标
  • 对同一个实验周期保持口径不变
  • 把其他指标降级为底线指标或辅助指标

一个简单模板:

  • 主目标:商品详情页移动端 p75 INP
  • 底线指标:功能正确、错误率不上升、CLS 不变差
  • 辅助指标:首屏 JS、长任务数、第三方脚本耗时

什么样的北极星指标是好的?

可以用下面这张表快速判断一个指标适不适合做每轮实验的主目标:

判断维度好的指标坏的指标
和用户体验的关系直接反映用户体感,例如页面是否出来得快、点击后是否有响应、页面是否会跳动只反映局部技术状态,和用户实际感受距离较远
稳定性在相同环境下重复测试,结果相对稳定,可比较噪声很大,多跑几次就明显波动,分不清是改动生效还是环境干扰
可操作性改动之后能较快看到变化,适合指导下一轮实验反馈很慢,或者变化太间接,难以指导单轮决策
是否容易“刷分”指标变好通常意味着体验真的变好很容易通过技巧把数字做漂亮,但用户体验并没有改善
团队共识成本团队容易理解,能围绕它达成统一判断每次都要解释它代表什么,为什么它重要

常见的坏指标有几类:

  • 噪声太大,例如没有固定数据状态的整页总耗时,或者没有统一交互脚本时的人手点击耗时
  • 离用户体感太远,例如单纯的 bundle 体积、某个函数执行时间、某个接口返回时间
  • 太容易被“刷分”,例如通过延后关键内容渲染,让某个数字变好看,但用户依然觉得页面不可用
  • 反馈周期太长,无法支撑高频实验

一个简单判断方法是:

  • 如果你的核心问题是“页面出来太慢”,优先考虑 LCP
  • 如果你的核心问题是“点了没反应、交互卡顿”,优先考虑 INP
  • 如果你的核心问题是“页面跳动、误触”,优先考虑 CLS

第二步:固定实验室环境

在实验室中,必须先把这些条件固定住:

  • 浏览器版本
  • 设备画像
  • 网络条件
  • CPU 降速
  • 页面入口
  • 登录态与数据态
  • 测试脚本与采样次数

输出至少包含:

  • 主目标值
  • 关键瀑布图或长任务信息
  • 关键资源体积
  • 失败日志

这里的作用不是替代线上数据,而是把实验反馈周期压缩到团队可持续迭代的程度。

第三步:用线上真实用户数据做最后确认

如果只靠实验室环境,你会遇到两个问题:

  • 实验室很漂亮,线上收益一般
  • 某些优化只对测试路径有效,对真实用户分布无效

所以每个准备晋级的实验都要经过线上真实用户数据确认,例如:

  • 真实用户监测系统中相同页面、相同设备类型、相同国家或网络等级下的 p75 指标
  • 关键交互链路的真实用户延迟变化
  • 错误率与埋点完整性是否受影响

简单理解:

  • 实验室结果决定“值不值得继续看”
  • 线上真实用户数据决定“值不值得保留到主线”

第四步:限定 agent 允许修改的范围

推荐按风险分层开放。关键不是“列一个大清单”,而是把每层能碰什么、不能碰什么写清楚。

建议团队给 agent 一份明确边界:

  • 允许修改哪些目录
  • 允许修改哪些类型的代码
  • 明确禁止改哪些模块
  • 每轮只允许选一个假设

一个简单模板:

允许修改:
- src/routes/product/**
- src/components/hero/**
- src/utils/loaders/**

禁止修改:
- src/domain/order/**
- src/tracking/**
- src/seo/**
- server/**

允许的改动类型:
- 资源加载策略
- 代码切分
- 图片与字体加载策略

禁止的改动类型:
- 改业务逻辑
- 改埋点协议
- 改 SEO 结构
- 改后端接口

下面按三层说明。

第一层:低风险、容易比较

适合开放给 agent 的内容:

  • 资源预加载与 preload 调整
  • 路由与组件级代码切分
  • 图片格式、尺寸与懒加载策略
  • 字体加载策略
  • 第三方脚本 defer / async / 空闲时调度

适合放开的原因:

  • 改动边界相对清楚
  • 回滚容易
  • 对业务逻辑影响较小
  • 收益常常能在实验室里较快看到

代码示例 1:把第三方组件改成按需加载

import { lazy, Suspense } from "react";

const ReviewWidget = lazy(() => import("./ReviewWidget"));

export function ProductSidebar() {
  return (
    <aside>
      <ProductSummary />
      <Suspense fallback={null}>
        <ReviewWidget />
      </Suspense>
    </aside>
  );
}

代码示例 2:把首屏外图片改成原生懒加载

export function ProductGalleryImage({ src, alt }: { src: string; alt: string }) {
  return (
    <img
      src={src}
      alt={alt}
      loading="lazy"
      decoding="async"
      width={640}
      height={640}
    />
  );
}

代码示例 3:把非关键第三方脚本延后到空闲阶段加载

function loadScript(src: string) {
  const script = document.createElement("script");
  script.src = src;
  script.async = true;
  document.head.appendChild(script);
}

if ("requestIdleCallback" in window) {
  window.requestIdleCallback(() => loadScript("https://example.com/chat-widget.js"));
} else {
  window.setTimeout(() => loadScript("https://example.com/chat-widget.js"), 2000);
}

第二层:中风险、收益可能更大

适合开放给 agent 的内容:

  • hydration 时机调整
  • 关键渲染路径精简
  • 列表与大块内容的延迟渲染
  • 长任务拆分
  • 事件处理中的调度与节流策略

这层收益更可能明显,但也更容易带来体验副作用,所以需要更严格的人工审查。

代码示例 1:把非关键区域延后挂载

import { useEffect, useState } from "react";

export function ProductPage() {
  const [showRecommendations, setShowRecommendations] = useState(false);

  useEffect(() => {
    const timer = window.setTimeout(() => setShowRecommendations(true), 1200);
    return () => window.clearTimeout(timer);
  }, []);

  return (
    <>
      <Hero />
      <PriceBlock />
      {showRecommendations ? <Recommendations /> : null}
    </>
  );
}

代码示例 2:把重计算拆到下一帧,减少一次长任务

function onFilterChange(nextValue: string) {
  setKeyword(nextValue);

  requestAnimationFrame(() => {
    runHeavyFilter(nextValue);
  });
}

代码示例 3:对频繁触发的逻辑做节流

function throttle<T extends (...args: any[]) => void>(fn: T, wait: number) {
  let pending = false;

  return (...args: Parameters<T>) => {
    if (pending) return;
    pending = true;

    window.setTimeout(() => {
      fn(...args);
      pending = false;
    }, wait);
  };
}

const onScroll = throttle(() => {
  updateVisibleCards();
}, 100);

window.addEventListener("scroll", onScroll, { passive: true });

第三层:高风险、需要人工强审

适合谨慎开放的内容:

  • 页面结构大改
  • 跨页面共享框架调整
  • 核心状态管理改造
  • SSR / CSR / islands 等架构切换

这层不建议一开始就开放给 agent,除非团队已经有稳定的验证链路和回滚机制。

代码示例 1:把整页从直接客户端渲染改成服务端下发关键内容

export async function ProductPageServer({ productId }: { productId: string }) {
  const product = await getProduct(productId);

  return (
    <>
      <Hero product={product} />
      <PriceBlock product={product} />
      <ClientRecommendations productId={productId} />
    </>
  );
}

代码示例 2:把全量客户端状态改成按页面拆分的局部状态

import { create } from "zustand";

type ProductPageState = {
  selectedSkuId: string | null;
  setSelectedSkuId: (skuId: string) => void;
};

export const useProductPageStore = create<ProductPageState>((set) => ({
  selectedSkuId: null,
  setSelectedSkuId: (skuId) => set({ selectedSkuId: skuId }),
}));

代码示例 3:把页面的一部分改成 islands 式延后激活

export default function ProductPage() {
  return (
    <>
      <StaticHero />
      <StaticSpecs />
      <InteractiveReviewsIsland client:visible />
    </>
  );
}

第五步:建立统一实验记录表

建议每轮实验都记到统一表里,例如 results.tsv 或团队共享表格,至少包含:

  • id:实验编号
  • commit
  • page_or_flow
  • north_star_metric
  • north_star_before
  • north_star_after
  • bottom_line_status
  • lab_result
  • field_result
  • status
  • description
  • risk_note

status 建议只有三种:

  • keep
  • discard
  • crash

一个样例:

idcommitpage_or_flownorth_star_metricbeforeafterstatusdescription
exp-01a1b2c3dproduct-detailp75 INP280ms280mskeepbaseline
exp-02b2c3d4eproduct-detailp75 INP280ms235mskeepdefer third-party review widget
exp-03c3d4e5fproduct-detailp75 INP235ms238msdiscardaggressive image preload changed waterfall
exp-04d4e5f6gproduct-detailp75 INP235msinvalidcrashhydration split caused event binding failure

第六步:定义保留 / 放弃 / 失败规则

推荐的默认规则:

keep

  • 主目标明确改善
  • 功能与体验底线未退化
  • 代码维护成本在可接受范围内
  • 线上真实用户数据没有明显反证

discard

  • 主目标未改善
  • 改善不稳定,复测后消失
  • 虽有改善,但引入过高维护成本
  • 伤害了底线指标

crash

  • 页面功能失效
  • 核心埋点失效
  • 性能数据无法采集
  • 改动导致明显运行时错误

重点不是把所有实验都做成 keep,而是快速而诚实地淘汰无效方案。

第七步:显式评估方案维护成本

建议团队在评审实验时,额外问四个问题:

  • 这次收益是否足够大,值得团队长期维护?
  • 如果 3 个月后回归,团队能快速定位吗?
  • 这个方案是否严重依赖脆弱时序、魔法常量或灰色技巧?
  • 有没有更简单但收益接近的替代方案?

如果答案偏负面,就算指标更好,也不一定要 keep。

第八步:安排实验节奏

推荐一个现实可行的节奏:

  • 每次只跑 1 个优化假设
  • 每个假设先经过实验室环境快速筛选
  • 通过后进入小流量或线上观察
  • 观察完成再决定 keep / discard

对多数团队来说,更适合的不是“让 agent 无限循环一整晚”,而是:

  • 每天固定 1 到 3 轮高质量实验
  • 周度复盘实验记录表
  • 每月沉淀高频有效模式

第九步:明确人和 agent 的分工

适合 agent 做的

  • 读取既有性能报告
  • 归纳瓶颈候选
  • 生成单点实验假设
  • 在限定范围内改代码
  • 跑实验室测试并记录结果
  • 对 keep / discard 给出建议

更适合人做的

  • 选择北极星指标
  • 定义底线指标
  • 决定允许改动边界
  • 判断维护成本是否过高
  • 审核高风险改动是否值得晋级

这套方法的正确打开方式,不是让人退出系统,而是让人把判断力集中在真正需要判断的地方。

第十步:准备 agent prompt 模板

下面这份模板适合给 agent 作为单轮实验指令:

你现在负责一轮前端性能实验,只能在允许的改动范围内行动。

目标页面:
- 商品详情页移动端

北极星指标:
- p75 INP

底线指标:
- 关键功能不能回归
- 错误率不能上升
- CLS 不能变差

允许改动范围:
- 第三方脚本加载策略
- 代码切分
- 图片与字体加载策略

不允许改动范围:
- 核心业务逻辑
- 埋点协议
- SEO 结构

工作流程:
1. 先读取 baseline 和最近几轮实验记录
2. 只提出一个实验假设
3. 修改代码并记录改动说明
4. 跑实验室测试并记录主指标与底线指标
5. 给出 keep / discard / crash 建议
6. 如果收益不明确或风险偏高,默认 discard

维护原则:
- 同等收益下优先更简单的方案
- 小收益但明显增加维护成本,不保留

最后

这套方法真正的价值,不在于“用 AI 自动优化前端”,而在于让性能优化从“靠高手临场发挥”变成“团队可重复运行的实验机制”。

先把实验环境、改动边界和结果记录搭好,再让 agent 开始优化。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com