Skip to main content

react-best-practices

React宝典——React 与 Next.js 全栈最佳实践指南。 当用户编写、审查或重构 React/Next.js 代码时自动激活。 触发场景:React 最佳实践、React 怎么写、组件设计、hooks 用法、hooks 怎么用、 状态管理、性能优化、React 性能、Server Components、RSC、Next.js 集成、 数据获取、bundle 优化、重渲染优化、React 19 新特性、use hook、 React best practices、component design patterns、React performance、 how to write React、Next.js best practices、React Server Components。 覆盖:组件设计模式、Hooks 最佳实践、状态管理策略、性能优化、 Server Components、Next.js 15+ 集成、React 19+ 新 API。

インストールへ移動

ソース情報

リポジトリ
sahit-sai/saviaa
ソースの最終更新活動
2026年7月18日 11:03
検出された SKILL.md の言語
中国語
スター
0
フォーク
0

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
react-best-practices
description
|
## 适用时机 在以下场景中使用本指南: - 编写新的 React 组件或页面 - 实现数据获取逻辑 - 审查代码性能问题 - 重构已有代码 - 优化 bundle 大小和加载时间 - 迁移到 React 19+ / Next.js 15+ / Server Components --- ## 一、消除请求瀑布(CRITICAL) 请求瀑布是性能的头号杀手。多个异步操作串行执行时,总延迟 = 所有操作之和。 ### 1.1 延迟 await 到真正需要的位置 ```typescript // ❌ 阻塞整个函数 async function Page() { const user = await getUser(); const posts = await getPosts(); // 必须等 getUser 完成 return <Feed user={user} posts={posts} />; } // ✅ 并行发起,延迟 await async function Page() { const userPromise = getUser(); const postsPromise = getPosts(); const [user, posts] = await Promise.all([userPromise, postsPromise]); return <Feed user={user} posts={posts} />; } ``` ### 1.2 用 Promise.all() 并行化独立操作 当多个异步操作互不依赖时,始终使用 `Promise.all()` 并行执行。 ```typescript // ❌ 串行:总时间 = T1 + T2 + T3 const user = await fetchUser(id); const orders = await fetchOrders(id); const recommendations = await fetchRecommendations(id); // ✅ 并行:总时间 = max(T1, T2, T3) const [user, orders, recommendations] = await Promise.all([ fetchUser(id), fetchOrders(id), fetchRecommendations(id), ]); ``` ### 1.3 策略性放置 Suspense 边界 用 Suspense 拆分页面,让快的部分先展示,慢的部分异步加载。 ```tsx // ✅ 外壳立即可见,数据部分逐步流式加载 export default function DashboardPage() { return ( <DashboardShell> <Suspense fallback={<StatsSkeleton />}> <StatsPanel /> </Suspense> <Suspense fallback={<FeedSkeleton />}> <ActivityFeed /> </Suspense> </DashboardShell> ); } ``` ### 1.4 API Route 中避免瀑布链 在 API Route / Server Action 中同样适用——先发起所有请求,再 await。 ### 1.5 有依赖关系时最大化并行度 当操作之间存在部分依赖时,用依赖图分析哪些可以并行。 ```typescript // ✅ user 和 config 并行;posts 依赖 user,但 config 不阻塞它 const [user, config] = await Promise.all([getUser(), getConfig()]); const posts = await getPosts(user.id); ``` --- ## 二、Bundle 体积优化(CRITICAL) ### 2.1 避免 Barrel File 导入 Barrel file(`index.ts` 聚合导出)会导致 tree-shaking 失效,一个图标可能拉入整个图标库。 ```typescript // ❌ 拉入全部图标(200KB+) import { Check } from "lucide-react"; // ✅ 直接从源文件导入 import Check from "lucide-react/dist/esm/icons/check"; ``` 适用于所有 icon 库、UI 组件库、工具库。 ### 2.2 动态导入重型组件 对大型组件(编辑器、图表、地图等)使用 `next/dynamic` 或 `React.lazy`。 ```typescript // ✅ Monaco Editor (~300KB) 仅在需要时加载 import dynamic from "next/dynamic"; const CodeEditor = dynamic(() => import("@/components/CodeEditor"), { loading: () => <EditorSkeleton />, ssr: false, }); ``` ### 2.3 延迟加载非关键第三方库 分析脚本、错误追踪等不应阻塞首屏。 ```typescript // ✅ hydration 完成后再加载分析脚本 useEffect(() => { import("@analytics/core").then((mod) => mod.init()); }, []); ``` ### 2.4 条件加载模块 仅在功能激活时才加载对应的模块/数据。 ### 2.5 基于用户意图预加载 在 hover/focus 时预加载即将使用的组件。 ```tsx // ✅ hover 时预加载,点击时立即可用 <button onMouseEnter={() => import("@/components/SettingsPanel")} onClick={() => setShowSettings(true)} > 设置 </button> ``` --- ## 三、服务端性能(HIGH) ### 3.1 Server Action 必须验证身份 Server Action 是公开端点,必须像 API Route 一样验证认证和授权。 ```typescript "use server"; export async function deletePost(postId: string) { const session = await auth(); // ✅ 始终验证 if (!session) throw new Error("Unauthorized"); if (!await canDelete(session.user.id, postId)) throw new Error("Forbidden"); await db.post.delete({ where: { id: postId } }); } ``` ### 3.2 最小化 RSC 边界的序列化 Server Component 传给 Client Component 的每个 prop 都会被序列化进 HTML。只传客户端需要的字段。 ```typescript // ❌ 传了整个 user 对象(包含敏感字段) <UserCard user={user} /> // ✅ 只传需要的字段 <UserCard name={user.name} avatar={user.avatar} /> ``` ### 3.3 用 React.cache() 做请求内去重 同一次请求中多个组件调用同一个数据函数时,用 `React.cache()` 避免重复执行。 ```typescript import { cache } from "react"; export const getUser = cache(async (id: string) => { return db.user.findUnique({ where: { id } }); }); // 同一请求中多次调用 getUser(id) 只执行一次 ``` ### 3.4 跨请求使用 LRU 缓存 `React.cache()` 仅在单次请求内生效。对跨请求共享的数据,使用 LRU 缓存。 ```typescript import { LRUCache } from "lru-cache"; const cache = new LRUCache<string, any>({ max: 500, ttl: 1000 * 60 * 5 }); export async function getConfig() { const cached = cache.get("config"); if (cached) return cached; const config = await db.config.findFirst(); cache.set("config", config); return config; } ``` ### 3.5 静态 I/O 提升到模块级别 字体文件、Logo、静态配置等不要每次请求都读取。 ```typescript // ✅ 模块加载时读取一次 const logoBuffer = await fs.readFile("./public/logo.png"); export function Logo() { return <img src={`data:image/png;base64,${logoBuffer.toString("base64")}`} />; } ``` ### 3.6 用 after() 做非阻塞操作 日志、埋点等不影响响应的操作用 `after()` 推迟到响应发送之后。 ```typescript import { after } from "next/server"; export async function POST(request: Request) { const result = await processOrder(request); after(async () => { await logAnalytics({ event: "order_created", orderId: result.id }); }); return Response.json(result); } ``` ### 3.7 并行数据获取 + 组件组合 利用 Server Component 的组合特性,让独立的数据获取自动并行。 ### 3.8 避免重复序列化 不要同时传原始数组和衍生状态,让客户端自行计算。 --- ## 四、客户端数据获取(MEDIUM-HIGH) ### 4.1 用 SWR/TanStack Query 自动去重 多个组件实例订阅同一数据时自动共享请求。 ```typescript // ✅ 多个组件调用同一 key,只发一次请求 function useUser(id: string) { return useSWR(`/api/users/${id}`, fetcher); } ``` ### 4.2 全局事件监听器去重 用 `useSyncExternalStore` 或 `useSWRSubscription` 共享 WebSocket/事件源。 ### 4.3 使用 Passive Event Listener 滚动、触摸事件加 `{ passive: true }`,消除浏览器滚动延迟。 ```typescript // ✅ 不阻塞滚动 element.addEventListener("touchstart", handler, { passive: true }); ``` ### 4.4 localStorage 加版本号并最小化存储 ```typescript // ✅ 带版本前缀 + 只存必要字段 + try-catch 包裹 const STORAGE_KEY = "app_prefs_v2"; function savePrefs(prefs: UserPrefs) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify({ theme: prefs.theme, lang: prefs.lang, })); } catch (e) { // 存储已满或隐私模式 } } ``` --- ## 五、重渲染优化(MEDIUM) ### 5.1 渲染期间计算派生状态 不要把可以计算的值存进 state——直接在渲染中计算。 ```typescript // ❌ 多余的 state + effect const [items, setItems] = useState(data); const [filteredItems, setFilteredItems] = useState([]); useEffect(() => { setFilteredItems(items.filter((i) => i.active)); }, [items]); // ✅ 渲染期间直接计算 const [items, setItems] = useState(data); const filteredItems = items.filter((i) => i.active); ``` ### 5.2 不要在组件内定义组件 内联组件每次父组件渲染都会导致完全卸载+重新挂载。 ```typescript // ❌ 每次渲染都创建新组件 function Parent() { function Child() { return <div>child</div>; } // 别这样做 return <Child />; } // ✅ 提取到外部,用 props 传递数据 function Child({ data }: { data: string }) { return <div>{data}</div>; } function Parent() { return <Child data="hello" />; } ``` ### 5.3 用 useRef 存储不触发渲染的频繁变化值 鼠标位置、滚动进度、计时器 ID 等不需要触发 UI 更新的值,用 ref 而非 state。 ### 5.4 用 useMemo/React.memo 控制子组件渲染 但注意:**简单表达式不要包 useMemo**。`useMemo` 本身有开销,布尔值/数字/字符串的简单计算不值得 memo。 ```typescript // ❌ 过度 memo const isActive = useMemo(() => status === "active", [status]); // ✅ 直接计算 const isActive = status === "active"; ``` ### 5.5 用函数式 setState 避免闭包陷阱 ```typescript // ❌ 可能使用过期的 count setCount(count + 1); // ✅ 始终基于最新值 setCount((prev) => prev + 1); ``` ### 5.6 useState 惰性初始化 传函数给 `useState`,仅在首次渲染时执行。 ```typescript // ❌ 每次渲染都执行 expensive() const [data, setData] = useState(expensiveComputation()); // ✅ 只在挂载时执行一次 const [data, setData] = useState(() => expensiveComputation()); ``` ### 5.7 提取非原始值默认值为常量 ```typescript // ❌ 每次渲染新建对象,memo 失效 function List({ config = { pageSize: 10 } }) { ... } // ✅ 稳定引用 const DEFAULT_CONFIG = { pageSize: 10 }; function List({ config = DEFAULT_CONFIG }) { ... } ``` ### 5.8 缩窄 Effect 依赖 订阅派生的布尔值而非连续变化的值。 ```typescript // ❌ value 每次变化都触发 effect useEffect(() => { ... }, [value]); // ✅ 只在 isAboveThreshold 变化时触发 const isAboveThreshold = value > 100; useEffect(() => { ... }, [isAboveThreshold]); ``` ### 5.9 把交互逻辑放进事件处理器 用户行为 -> 事件处理器,不是 state + effect 间接表达。 ### 5.10 延迟读取动态状态 如果只在回调中读取 searchParams/localStorage,不要在渲染层订阅它们。 ### 5.11 用 startTransition 标记非紧急更新 ```typescript // ✅ 搜索输入立即响应,结果列表延迟更新 function Search() { const [query, setQuery] = useState(""); const [results, setResults] = useState([]); function handleChange(e: React.ChangeEvent<HTMLInputElement>) { setQuery(e.target.value); startTransition(() => { setResults(searchItems(e.target.value)); }); } return ( <> <input value={query} onChange={handleChange} /> <ResultList results={results} /> </> ); } ``` ### 5.12 提取为 Memo 组件实现提前返回 ### 5.13 Effect 内不该做的事 -> 移到事件处理器 --- ## 六、渲染性能(MEDIUM) ### 6.1 用 CSS content-visibility 优化长列表 ```css /* ✅ 浏览器跳过屏幕外内容的渲染 */ .list-item { content-visibility: auto; contain-intrinsic-size: 0 80px; } ``` ### 6.2 静态 JSX 提升到组件外部 不会变化的 JSX 片段提取为模块级常量,避免每次渲染重新创建。 ```typescript // ✅ 只创建一次 const EMPTY_STATE = <div className="empty">暂无数据</div>; function List({ items }: { items: Item[] }) { if (items.length === 0) return EMPTY_STATE; return <ul>{items.map(...)}</ul>; } ``` ### 6.3 防止 Hydration 闪烁 依赖 localStorage/cookie 的 UI(如暗色模式)用同步脚本在 React hydrate 之前设置。 ```html <!-- ✅ 在 <head> 中同步执行,hydration 前完成 --> <script> const theme = localStorage.getItem("theme") || "light"; document.documentElement.dataset.theme = theme; </script> ``` ### 6.4 使用 Activity 组件(React 19+) 频繁切换显示/隐藏的重型组件,用 `<Activity>` 保留 state 和 DOM。 ```tsx // ✅ 切换时保留状态,不重新挂载 import { Activity } from "react"; <Activity mode={activeTab === "editor" ? "visible" : "hidden"}> <HeavyEditor /> </Activity> ``` ### 6.5 SVG 动画包裹 div 直接在 SVG 元素上做 CSS 动画会跳过 GPU 加速。包一层 div。 ### 6.6 优化 SVG 精度 用 SVGO 将坐标精度降到 1 位小数,减少 SVG 体积 20-40%。
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る