普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月18日首页

加上这个React最佳实践Skill,再也不怕AI写代码不考虑性能优化了

作者 墨舟
2026年1月18日 16:12

Vercel 团队维护的 React 和 Next.js 性能优化指南的Skill: React Best Practices,包含 45 条规则,8大类的优化的方向:

类别 说明
消除瀑布流请求 瀑布流是性能杀手,每个顺序 await 都会增加完整的网络延迟
Bundle 体积优化 减少初始 bundle 大小,改善首次可交互时间(TTI)和最大内容绘制(LCP)
服务端性能 优化服务端渲染和数据获取,消除服务端瀑布流,减少响应时间
客户端数据获取 自动去重和高效的数据获取模式,减少冗余网络请求
重渲染优化 减少不必要的重渲染,最小化浪费的计算,提升 UI 响应性
渲染性能 优化渲染过程,减少浏览器的工作量
JavaScript 性能 热路径的微优化,积少成多也能带来明显改善
高级模式 针对特定场景的高级模式,需要谨慎实现

接着我们来具体看看第一条:消除瀑布流请求这个类别,它一共分为了5条rules:

1、async-defer-await:在真正需要的时候再使用await

核心思想:在真正需要使用到 await 时使用,避免阻塞前置不需要 await 的地方。

错误示例(两个分支都被阻塞):

async function handleRequest(userId: string, skipProcessing: boolean) {
  const userData = await fetchUserData(userId)  // 总是等待

  if (skipProcessing) {
    // 虽然立即返回,但已经等待了 userData
    return { skipped: true }
  }

  // 只有这个分支使用 userData
  return processUserData(userData)
}

正确示例(只在需要时阻塞):

async function handleRequest(userId: string, skipProcessing: boolean) {
  if (skipProcessing) {
    // 立即返回,无需等待
    return { skipped: true }
  }

  // 只在需要时获取
  const userData = await fetchUserData(userId)
  return processUserData(userData)
}

另一个实际场景:

// 错误:每次都获取权限
async function updateResource(resourceId: string, userId: string) {
  const permissions = await fetchPermissions(userId)
  const resource = await getResource(resourceId)

  if (!resource) {
    return { error: 'Not found' }
  }

  if (!permissions.canEdit) {
    return { error: 'Forbidden' }
  }

  return await updateResourceData(resource, permissions)
}

// 正确:只在需要时获取
async function updateResource(resourceId: string, userId: string) {
  const resource = await getResource(resourceId)

  if (!resource) {
    return { error: 'Not found' }  // 提前返回,省掉权限请求
  }

  const permissions = await fetchPermissions(userId)

  if (!permissions.canEdit) {
    return { error: 'Forbidden' }
  }

  return await updateResourceData(resource, permissions)
}

2、async-parallel:使用 Promise.all() 并发执行

核心思想:当每个异步操作之间没有依赖关系时,可以使用 Promise.all() 来进行并发执行

错误示例(串行执行,3 次网络往返):

const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()

正确示例(并行执行,1 次网络往返):

const [user, posts, comments] = await Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments()
])

这可能是最简单但也是最有效的优化之一。

3、async-dependencies:使用 better-all 处理部分依赖

核心思想:当异步操作之间存在依赖时,可以使用 better-all 库来自动最大化并行度。

问题场景:profile 依赖 user,但 config 不依赖其他操作。

错误示例(profile 不必要地等待 config):

const [user, config] = await Promise.all([
  fetchUser(),
  fetchConfig()
])
const profile = await fetchProfile(user.id)  // config 已完成,但 profile 还要等

正确示例(config 和 profile 并行运行):

import { all } from 'better-all'

const { user, config, profile } = await all({
  async user() { return fetchUser() },
  async config() { return fetchConfig() },
  async profile() {
    // 只等待 user,不等待 config
    return fetchProfile((await this.$.user).id)
  }
})

better-all 会自动分析依赖关系,在最早可能的时刻启动每个任务。

参考:github.com/shuding/bet…

4、async-api-routes:API 路由中尽早开始 Promise

核心思想:在 API 路由和 Server Actions 中,立即启动独立操作,即使你还不需要 await 它们。

错误示例(config 等待 auth,data 等待两者):

export async function GET(request: Request) {
  const session = await auth()
  const config = await fetchConfig()  // 等 auth 完成才开始
  const data = await fetchData(session.user.id)
  return Response.json({ data, config })
}

正确示例(auth 和 config 立即并行启动):

export async function GET(request: Request) {
  // 立即启动,不等待
  const sessionPromise = auth()
  const configPromise = fetchConfig()

  // 需要 session 时才 await
  const session = await sessionPromise

  // config 和 data 并行获取
  const [config, data] = await Promise.all([
    configPromise,
    fetchData(session.user.id)
  ])

  return Response.json({ data, config })
}

关键技巧:先启动 Promise,后 await。

5、async-suspense-boundaries:使用 Suspense

核心思想:不要在 async 组件中 await 数据后再返回 JSX,而是使用 Suspense 边界让外层 UI 先显示。

错误示例(整个页面被数据获取阻塞):

async function Page() {
  const data = await fetchData()  // 阻塞整个页面

  return (
    <div>
      <div>Sidebar</div>
      <div>Header</div>
      <div>
        <DataDisplay data={data} />
      </div>
      <div>Footer</div>
    </div>
  )
}

整个布局都要等数据,尽管只有中间部分需要它。

正确示例(外层立即显示,数据流式加载):

function Page() {
  return (
    <div>
      <div>Sidebar</div>
      <div>Header</div>
      <div>
        <Suspense fallback={<Skeleton />}>
          <DataDisplay />
        </Suspense>
      </div>
      <div>Footer</div>
    </div>
  )
}

async function DataDisplay() {
  const data = await fetchData()  // 只阻塞这个组件
  return <div>{data.content}</div>
}

Sidebar、Header、Footer 立即渲染,只有 DataDisplay 等待数据。

进阶:多个组件共享 Promise

function Page() {
  // 立即开始获取,但不 await
  const dataPromise = fetchData()

  return (
    <div>
      <div>Sidebar</div>
      <div>Header</div>
      <Suspense fallback={<Skeleton />}>
        <DataDisplay dataPromise={dataPromise} />
        <DataSummary dataPromise={dataPromise} />
      </Suspense>
      <div>Footer</div>
    </div>
  )
}

function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
  const data = use(dataPromise)  // 使用 React 19 的 use() hook
  return <div>{data.content}</div>
}

function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
  const data = use(dataPromise)  // 复用同一个 Promise
  return <div>{data.summary}</div>
}

两个组件共享同一个 Promise,只发起一次请求。

注意:这种方式需要权衡。更快的首次绘制 vs 重绘重排

如何安装这个 Skill

直接将github的这整个文件夹:github.com/vercel-labs… 放到 ~/.claude/skills(mac电脑)下即可 。安装完成后,Claude Code会自动判断是否在需要的时候使用这个skill,当然也可以通过以下方式直接调用这个 Skill:

/vercel-react-best-practices
❌
❌