重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
react-performance by s-hiraoku/synapse-a2a
npx skills add https://github.com/s-hiraoku/synapse-a2a --skill react-performance针对 React 和 Next.js 应用程序的系统化性能优化,按影响程度排序。
顺序异步操作是最大的性能杀手。首先解决这些问题。
将 await 移到使用点,而不是声明点。
// 错误做法:顺序执行 — 总时间 = fetch1 + fetch2
async function Page() {
const user = await getUser();
const posts = await getPosts(user.id);
return <Feed user={user} posts={posts} />;
}
// 正确做法:尽可能并行
async function Page() {
const userPromise = getUser();
const postsPromise = getPosts(); // 如果独立
const [user, posts] = await Promise.all([userPromise, postsPromise]);
return <Feed user={user} posts={posts} />;
}
将慢速数据包裹在 <Suspense> 中,以便外壳立即渲染。
export default function Page() {
return (
<main>
<Header /> {/* 立即渲染 */}
<Suspense fallback={<Skeleton />}>
<SlowDataSection /> {/* 流式加载 */}
</Suspense>
</main>
);
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
当 Promise 存在部分依赖时,立即开始独立的工作。
async function Dashboard() {
const userPromise = getUser();
const settingsPromise = getSettings(); // 独立
const user = await userPromise;
const postsPromise = getPosts(user.id); // 依赖于 user
const [settings, posts] = await Promise.all([settingsPromise, postsPromise]);
return <View user={user} settings={settings} posts={posts} />;
}
在生产代码中切勿从桶文件导入。
// 错误做法:引入整个库
import { Button } from '@/components';
import { format } from 'date-fns';
// 正确做法:可进行 Tree-shaking
import { Button } from '@/components/ui/button';
import { format } from 'date-fns/format';
对初始渲染不需要的繁重组件进行代码分割。
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('@/components/chart'), {
loading: () => <ChartSkeleton />,
ssr: false, // 对于仅客户端的组件跳过 SSR
});
const Editor = dynamic(() => import('@/components/editor'), {
loading: () => <EditorSkeleton />,
});
在 hydration 后加载分析、日志记录和非关键脚本。
// 错误做法:阻塞 hydration
import { analytics } from 'heavy-analytics';
analytics.init();
// 正确做法:hydration 后加载
useEffect(() => {
import('heavy-analytics').then(({ analytics }) => analytics.init());
}, []);
当用户表现出意图(悬停、聚焦)时预加载资源。
function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
const router = useRouter();
return (
<Link
href={href}
onMouseEnter={() => router.prefetch(href)}
onFocus={() => router.prefetch(href)}
>
{children}
</Link>
);
}
使用 React.cache() 在单个请求内对数据获取进行去重。
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
// 在 layout.tsx 和 page.tsx 中调用 — 仅进行一次数据库查询
仅传递客户端组件实际需要的数据。
// 错误做法:序列化整个 user 对象
<ClientAvatar user={user} />
// 正确做法:仅传递所需内容
<ClientAvatar name={user.name} avatarUrl={user.avatarUrl} />
使用 after() 处理不应阻塞响应的任务。
import { after } from 'next/server';
export async function POST(request: Request) {
const data = await processRequest(request);
after(async () => {
await logAnalytics(data);
await sendNotification(data);
});
return Response.json(data); // 立即返回
}
在渲染期间计算派生值,切勿在副作用中计算。
// 错误做法:额外的渲染周期
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
useEffect(() => setCount(items.length), [items]);
// 正确做法:在渲染期间派生
const [items, setItems] = useState([]);
const count = items.length;
使用基于回调的 setState,并将回调提取到渲染之外。
// 错误做法:每次渲染都创建新函数
<Button onClick={() => setCount(count + 1)} />
// 正确做法:稳定引用
<Button onClick={() => setCount(c => c + 1)} />
在依赖数组中使用原始值以避免误报。
// 错误做法:对象引用每次渲染都会改变
useEffect(() => { ... }, [config]);
// 正确做法:原始值是稳定的
useEffect(() => { ... }, [config.apiUrl, config.timeout]);
为昂贵的初始状态传递初始化函数。
// 错误做法:每次渲染都运行
const [data, setData] = useState(expensiveParse(raw));
// 正确做法:仅在挂载时运行
const [data, setData] = useState(() => expensiveParse(raw));
将繁重的计算提取到记忆化的子组件中。
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
return items.map(item => <ComplexItem key={item.id} item={item} />);
});
使用 startTransition 处理非紧急的状态更新。
const [isPending, startTransition] = useTransition();
function handleSearch(query: string) {
setInputValue(query); // 紧急:更新输入
startTransition(() => setResults(search(query))); // 可延迟:更新结果
}
对长滚动列表应用 CSS content-visibility。
.list-item {
content-visibility: auto;
contain-intrinsic-size: 0 80px;
}
将不依赖于 props/state 的 JSX 提取到组件外部。
// 错误做法:每次渲染都重新创建
function Layout({ children }) {
return (
<div>
<footer><p>Copyright 2026</p></footer>
{children}
</div>
);
}
// 正确做法:只创建一次
const footer = <footer><p>Copyright 2026</p></footer>;
function Layout({ children }) {
return <div>{footer}{children}</div>;
}
优先使用三元运算符而非 &&,以避免渲染 0 或 ""。
// 错误做法:当 count 为 0 时会渲染 "0"
{count && <Badge count={count} />}
// 正确做法:显式的布尔检查
{count > 0 ? <Badge count={count} /> : null}
| 问题 | 解决方案 | 优先级 |
|---|---|---|
| 顺序获取 | Promise.all() / Suspense | 关键 |
| 桶文件导入 | 直接路径导入 | 关键 |
| 初始包体积过大 | next/dynamic | 关键 |
| 冗余的数据库调用 | React.cache() | 高 |
| 过度序列化的 props | 仅传递原始值 | 高 |
| 在 useEffect 中派生状态 | 在渲染期间计算 | 中 |
| 不稳定的回调 | useCallback / 回调式 setState | 中 |
| 长列表 | 虚拟化 + content-visibility | 中 |
| 非紧急更新 | useTransition | 中 |
每周安装数
57
仓库
首次出现
2026年2月11日
安全审计
安装于
gemini-cli56
codex56
kimi-cli56
opencode56
amp55
github-copilot55
Systematic performance optimization for React and Next.js applications, organized by impact.
Sequential async operations are the single biggest performance killer. Fix these first.
Move await to the point of use, not the point of declaration.
// BAD: Sequential — total time = fetch1 + fetch2
async function Page() {
const user = await getUser();
const posts = await getPosts(user.id);
return <Feed user={user} posts={posts} />;
}
// GOOD: Parallel where possible
async function Page() {
const userPromise = getUser();
const postsPromise = getPosts(); // if independent
const [user, posts] = await Promise.all([userPromise, postsPromise]);
return <Feed user={user} posts={posts} />;
}
Wrap slow data behind <Suspense> so the shell renders instantly.
export default function Page() {
return (
<main>
<Header /> {/* instant */}
<Suspense fallback={<Skeleton />}>
<SlowDataSection /> {/* streams in */}
</Suspense>
</main>
);
}
When promises have partial dependencies, start independent work immediately.
async function Dashboard() {
const userPromise = getUser();
const settingsPromise = getSettings(); // independent
const user = await userPromise;
const postsPromise = getPosts(user.id); // depends on user
const [settings, posts] = await Promise.all([settingsPromise, postsPromise]);
return <View user={user} settings={settings} posts={posts} />;
}
Never import from barrel files in production code.
// BAD: Pulls entire library
import { Button } from '@/components';
import { format } from 'date-fns';
// GOOD: Tree-shakeable
import { Button } from '@/components/ui/button';
import { format } from 'date-fns/format';
Code-split heavy components that aren't needed on initial render.
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('@/components/chart'), {
loading: () => <ChartSkeleton />,
ssr: false, // skip SSR for client-only components
});
const Editor = dynamic(() => import('@/components/editor'), {
loading: () => <EditorSkeleton />,
});
Load analytics, logging, and non-critical scripts after hydration.
// BAD: Blocks hydration
import { analytics } from 'heavy-analytics';
analytics.init();
// GOOD: Load after hydration
useEffect(() => {
import('heavy-analytics').then(({ analytics }) => analytics.init());
}, []);
Preload resources when user shows intent (hover, focus).
function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
const router = useRouter();
return (
<Link
href={href}
onMouseEnter={() => router.prefetch(href)}
onFocus={() => router.prefetch(href)}
>
{children}
</Link>
);
}
Use React.cache() to deduplicate data fetches within a single request.
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
// Called in layout.tsx AND page.tsx — only one DB query
Only pass the data client components actually need.
// BAD: Serializes entire user object
<ClientAvatar user={user} />
// GOOD: Pass only what's needed
<ClientAvatar name={user.name} avatarUrl={user.avatarUrl} />
Use after() for work that shouldn't block the response.
import { after } from 'next/server';
export async function POST(request: Request) {
const data = await processRequest(request);
after(async () => {
await logAnalytics(data);
await sendNotification(data);
});
return Response.json(data); // returns immediately
}
Compute derived values during render, never in effects.
// BAD: Extra render cycle
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
useEffect(() => setCount(items.length), [items]);
// GOOD: Derive during render
const [items, setItems] = useState([]);
const count = items.length;
Use callback-based setState and extract callbacks outside render.
// BAD: New function every render
<Button onClick={() => setCount(count + 1)} />
// GOOD: Stable reference
<Button onClick={() => setCount(c => c + 1)} />
Use primitives in dependency arrays to avoid false positives.
// BAD: Object reference changes every render
useEffect(() => { ... }, [config]);
// GOOD: Primitive values are stable
useEffect(() => { ... }, [config.apiUrl, config.timeout]);
Pass initializer functions for expensive initial state.
// BAD: Runs every render
const [data, setData] = useState(expensiveParse(raw));
// GOOD: Runs only on mount
const [data, setData] = useState(() => expensiveParse(raw));
Extract heavy computation into memoized child components.
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
return items.map(item => <ComplexItem key={item.id} item={item} />);
});
Use startTransition for non-urgent state updates.
const [isPending, startTransition] = useTransition();
function handleSearch(query: string) {
setInputValue(query); // urgent: update input
startTransition(() => setResults(search(query))); // deferrable: update results
}
Apply CSS content-visibility for long scrollable lists.
.list-item {
content-visibility: auto;
contain-intrinsic-size: 0 80px;
}
Extract JSX that doesn't depend on props/state outside the component.
// BAD: Recreated every render
function Layout({ children }) {
return (
<div>
<footer><p>Copyright 2026</p></footer>
{children}
</div>
);
}
// GOOD: Created once
const footer = <footer><p>Copyright 2026</p></footer>;
function Layout({ children }) {
return <div>{footer}{children}</div>;
}
Prefer ternary over && to avoid rendering 0 or "".
// BAD: Renders "0" when count is 0
{count && <Badge count={count} />}
// GOOD: Explicit boolean check
{count > 0 ? <Badge count={count} /> : null}
| Issue | Fix | Priority |
|---|---|---|
| Sequential fetches | Promise.all() / Suspense | CRITICAL |
| Barrel imports | Direct path imports | CRITICAL |
| Large initial bundle | next/dynamic | CRITICAL |
| Redundant DB calls | React.cache() | HIGH |
| Over-serialized props | Pass primitives only | HIGH |
| Derived state in useEffect | Compute during render | MEDIUM |
| Unstable callbacks | / callback setState |
Weekly Installs
57
Repository
First Seen
Feb 11, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli56
codex56
kimi-cli56
opencode56
amp55
github-copilot55
GSAP React 动画库使用指南:useGSAP Hook 与最佳实践
3,900 周安装
面包板(breadboarding)技术详解:工作流可视化与系统映射工具
6 周安装
Agentic Browser:AI代理浏览器自动化工具 | Playwright驱动 | 元素交互
6 周安装
Nansen钱包迁移指南:从旧密码存储安全迁移至操作系统密钥链
7 周安装
Nansen 钱包归属分析工具 - 区块链地址身份识别与关联追踪
7 周安装
YouTube自动化教程:使用Rube MCP和Composio工具包实现视频上传、管理与分析
7 周安装
WhatsApp Business API 自动化指南:通过 Rube MCP 和 Composio 实现消息发送与管理
7 周安装
useCallback| MEDIUM |
| Long lists | Virtualization + content-visibility | MEDIUM |
| Non-urgent updates | useTransition | MEDIUM |