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

2026年1月18日
·

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

11

接着我们来具体看看第一条:消除瀑布流请求这个类别,它一共分为了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 会自动分析依赖关系,在最早可能的时刻启动每个任务。

参考:https://github.com/shuding/better-all

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

/vercel-react-best-practices