[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering":773,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering":797},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":769,"stem":770,"id":771,"extension":772},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering","渲染与协调","React 将组件渲染为虚拟 DOM 树，再协调（reconciliation）到浏览器 DOM。理解该过程有助于解释 key、memo 与性能优化。涵盖渲染流程、Fiber、startTransition、Suspense、最佳实践与常见坑。","技能小册",[12,13],"react","rendering","2026-07-10",false,{"type":17,"value":18,"toc":754},"minimark",[19,22,46,50,65,69,105,115,119,126,142,147,253,259,263,270,285,288,291,298,370,381,457,461,469,489,496,500,518,521,554,557,646,649,670,673,676,750],[20,21,8],"h1",{"id":8},[23,24,25,26,30,31,35,36,39,40,45],"p",{},"React 将组件渲染为",[27,28,29],"strong",{},"虚拟 DOM 树","，再**协调（reconciliation）**到浏览器 DOM。理解该过程有助于解释 ",[32,33,34],"code",{},"key","、",[32,37,38],{},"memo"," 与性能优化。见 ",[41,42,44],"a",{"href":43},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fbasics","React 基础","。",[47,48,49],"h2",{"id":49},"核心概念",[23,51,52,53,56,57,60,61,64],{},"React 应用的每次更新都会经历「重新计算应该长什么样」和「把差异应用到真实 DOM」两件事。React 把这两件事拆成明确的",[27,54,55],{},"两阶段","：先在内存里算出新的虚拟 DOM 树并与旧树 ",[27,58,59],{},"diff（协调）","，再把差异一次性 ",[27,62,63],{},"commit"," 到真实 DOM。这个设计让 React 可以在 Render 阶段暂停、重新开始、甚至丢弃计算结果而不影响用户看到的界面，是并发特性的基础。",[47,66,68],{"id":67},"渲染流程简化","渲染流程（简化）",[70,71,72,86,92],"ol",{},[73,74,75,78,79,35,82,85],"li",{},[27,76,77],{},"触发更新","：",[32,80,81],{},"setState",[32,83,84],{},"dispatch","、父组件重渲染、Context value 变化等。",[73,87,88,91],{},[27,89,90],{},"Render 阶段","：调用组件函数，得到新的 React 元素树（可中断，React 18 并发特性在此阶段调度）。",[73,93,94,97,98,101,102,45],{},[27,95,96],{},"Commit 阶段","：将变更应用到 DOM，执行 ",[32,99,100],{},"useLayoutEffect","、浏览器绘制，再执行 ",[32,103,104],{},"useEffect",[106,107,113],"pre",{"className":108,"code":110,"language":111,"meta":112},[109],"language-text","setState() → 调度更新 → Render（可中断\u002F可丢弃） → Diff → Commit（不可中断）→ useLayoutEffect → 浏览器绘制 → useEffect\n","text","",[32,114,110],{"__ignoreMap":112},[47,116,118],{"id":117},"协调与-key","协调与 key",[23,120,121,122,125],{},"当同一层级的子节点类型或 key 变化时，React 会卸载旧节点、挂载新节点；",[27,123,124],{},"key 相同","则尝试复用实例并更新 props。",[127,128,129,139],"ul",{},[73,130,131,132,135,136,138],{},"列表使用",[27,133,134],{},"稳定唯一"," ",[32,137,34],{},"（如数据库 id）。",[73,140,141],{},"用下标作 key 在插入\u002F重排时会导致错误的状态保留。",[143,144,146],"h3",{"id":145},"示例下标-key-导致的状态错位","示例：下标 key 导致的状态错位",[106,148,152],{"className":149,"code":150,"language":151,"meta":112,"style":112},"language-tsx shiki shiki-themes material-theme-lighter github-light github-dark","function TodoList({ todos }: { todos: Todo[] }) {\n  return (\n    \u003Cul>\n      {todos.map((todo, index) => (\n        \u002F\u002F 反例：用 index 作 key，在列表开头插入新项后，\n        \u002F\u002F 原来第 0 项的 \u003Cinput> 内部状态（如已聚焦、已输入的临时值）会被错误地复用到新的第 0 项上\n        \u003Cli key={index}>\n          \u003CTodoItem todo={todo} \u002F>\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n}\n\n\u002F\u002F 正确：用稳定的业务 id\n{todos.map((todo) => \u003Cli key={todo.id}>\u003CTodoItem todo={todo} \u002F>\u003C\u002Fli>)}\n","tsx",[32,153,154,162,168,174,180,186,192,198,204,210,216,222,228,234,241,247],{"__ignoreMap":112},[155,156,159],"span",{"class":157,"line":158},"line",1,[155,160,161],{},"function TodoList({ todos }: { todos: Todo[] }) {\n",[155,163,165],{"class":157,"line":164},2,[155,166,167],{},"  return (\n",[155,169,171],{"class":157,"line":170},3,[155,172,173],{},"    \u003Cul>\n",[155,175,177],{"class":157,"line":176},4,[155,178,179],{},"      {todos.map((todo, index) => (\n",[155,181,183],{"class":157,"line":182},5,[155,184,185],{},"        \u002F\u002F 反例：用 index 作 key，在列表开头插入新项后，\n",[155,187,189],{"class":157,"line":188},6,[155,190,191],{},"        \u002F\u002F 原来第 0 项的 \u003Cinput> 内部状态（如已聚焦、已输入的临时值）会被错误地复用到新的第 0 项上\n",[155,193,195],{"class":157,"line":194},7,[155,196,197],{},"        \u003Cli key={index}>\n",[155,199,201],{"class":157,"line":200},8,[155,202,203],{},"          \u003CTodoItem todo={todo} \u002F>\n",[155,205,207],{"class":157,"line":206},9,[155,208,209],{},"        \u003C\u002Fli>\n",[155,211,213],{"class":157,"line":212},10,[155,214,215],{},"      ))}\n",[155,217,219],{"class":157,"line":218},11,[155,220,221],{},"    \u003C\u002Ful>\n",[155,223,225],{"class":157,"line":224},12,[155,226,227],{},"  );\n",[155,229,231],{"class":157,"line":230},13,[155,232,233],{},"}\n",[155,235,237],{"class":157,"line":236},14,[155,238,240],{"emptyLinePlaceholder":239},true,"\n",[155,242,244],{"class":157,"line":243},15,[155,245,246],{},"\u002F\u002F 正确：用稳定的业务 id\n",[155,248,250],{"class":157,"line":249},16,[155,251,252],{},"{todos.map((todo) => \u003Cli key={todo.id}>\u003CTodoItem todo={todo} \u002F>\u003C\u002Fli>)}\n",[23,254,255,256,258],{},"React 通过 ",[32,257,34],{}," 判断「这是不是同一个逻辑实体」，而不是「这是不是同一个位置」——理解这一点就能明白为什么下标 key 在增删排序场景下容易出问题。",[47,260,262],{"id":261},"fiber-架构概念","Fiber 架构（概念）",[23,264,265,266,269],{},"React 16+ 使用 ",[27,267,268],{},"Fiber"," 将渲染工作拆为可调度的小单元，支持：",[127,271,272,275],{},[73,273,274],{},"可中断的渲染（为高优先级更新让路）。",[73,276,277,280,281,284],{},[32,278,279],{},"Suspense","、并发渲染、",[32,282,283],{},"startTransition"," 等（React 18）。",[23,286,287],{},"应用开发通常只需知道「渲染可被打断、分优先级」，无需实现 Fiber 细节。可以把 Fiber 类比成「渲染任务的调度单元」：每个组件对应一个 Fiber 节点，React 可以在处理完一个节点后检查「是否有更紧急的任务插队」，而不必等整棵树处理完。",[47,289,283],{"id":290},"starttransition",[23,292,293,294,297],{},"将",[27,295,296],{},"非紧急","更新标记为 transition，保持输入等交互流畅：",[106,299,301],{"className":149,"code":300,"language":151,"meta":112,"style":112},"import { startTransition, useState } from \"react\";\n\nfunction Search() {\n  const [query, setQuery] = useState(\"\");\n  const [results, setResults] = useState\u003CItem[]>([]);\n\n  function handleChange(value: string) {\n    setQuery(value); \u002F\u002F 紧急更新：输入框要立刻响应\n    startTransition(() => {\n      setResults(filterHugeList(value)); \u002F\u002F 非紧急：可以稍后计算，不阻塞输入\n    });\n  }\n  \u002F\u002F ...\n}\n",[32,302,303,308,312,317,322,327,331,336,341,346,351,356,361,366],{"__ignoreMap":112},[155,304,305],{"class":157,"line":158},[155,306,307],{},"import { startTransition, useState } from \"react\";\n",[155,309,310],{"class":157,"line":164},[155,311,240],{"emptyLinePlaceholder":239},[155,313,314],{"class":157,"line":170},[155,315,316],{},"function Search() {\n",[155,318,319],{"class":157,"line":176},[155,320,321],{},"  const [query, setQuery] = useState(\"\");\n",[155,323,324],{"class":157,"line":182},[155,325,326],{},"  const [results, setResults] = useState\u003CItem[]>([]);\n",[155,328,329],{"class":157,"line":188},[155,330,240],{"emptyLinePlaceholder":239},[155,332,333],{"class":157,"line":194},[155,334,335],{},"  function handleChange(value: string) {\n",[155,337,338],{"class":157,"line":200},[155,339,340],{},"    setQuery(value); \u002F\u002F 紧急更新：输入框要立刻响应\n",[155,342,343],{"class":157,"line":206},[155,344,345],{},"    startTransition(() => {\n",[155,347,348],{"class":157,"line":212},[155,349,350],{},"      setResults(filterHugeList(value)); \u002F\u002F 非紧急：可以稍后计算，不阻塞输入\n",[155,352,353],{"class":157,"line":218},[155,354,355],{},"    });\n",[155,357,358],{"class":157,"line":224},[155,359,360],{},"  }\n",[155,362,363],{"class":157,"line":230},[155,364,365],{},"  \u002F\u002F ...\n",[155,367,368],{"class":157,"line":236},[155,369,233],{},[23,371,372,373,376,377,380],{},"配合 ",[32,374,375],{},"useTransition"," 可以获得 ",[32,378,379],{},"isPending"," 状态，用于展示加载指示：",[106,382,384],{"className":149,"code":383,"language":151,"meta":112,"style":112},"function Search() {\n  const [isPending, startTransition] = useTransition();\n  const [results, setResults] = useState\u003CItem[]>([]);\n\n  function handleChange(value: string) {\n    startTransition(() => setResults(filterHugeList(value)));\n  }\n\n  return (\n    \u003Cdiv>\n      \u003Cinput onChange={(e) => handleChange(e.target.value)} \u002F>\n      {isPending && \u003CSpinner \u002F>}\n      \u003CResultList items={results} \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n",[32,385,386,390,395,399,403,407,412,416,420,424,429,434,439,444,449,453],{"__ignoreMap":112},[155,387,388],{"class":157,"line":158},[155,389,316],{},[155,391,392],{"class":157,"line":164},[155,393,394],{},"  const [isPending, startTransition] = useTransition();\n",[155,396,397],{"class":157,"line":170},[155,398,326],{},[155,400,401],{"class":157,"line":176},[155,402,240],{"emptyLinePlaceholder":239},[155,404,405],{"class":157,"line":182},[155,406,335],{},[155,408,409],{"class":157,"line":188},[155,410,411],{},"    startTransition(() => setResults(filterHugeList(value)));\n",[155,413,414],{"class":157,"line":194},[155,415,360],{},[155,417,418],{"class":157,"line":200},[155,419,240],{"emptyLinePlaceholder":239},[155,421,422],{"class":157,"line":206},[155,423,167],{},[155,425,426],{"class":157,"line":212},[155,427,428],{},"    \u003Cdiv>\n",[155,430,431],{"class":157,"line":218},[155,432,433],{},"      \u003Cinput onChange={(e) => handleChange(e.target.value)} \u002F>\n",[155,435,436],{"class":157,"line":224},[155,437,438],{},"      {isPending && \u003CSpinner \u002F>}\n",[155,440,441],{"class":157,"line":230},[155,442,443],{},"      \u003CResultList items={results} \u002F>\n",[155,445,446],{"class":157,"line":236},[155,447,448],{},"    \u003C\u002Fdiv>\n",[155,450,451],{"class":157,"line":243},[155,452,227],{},[155,454,455],{"class":157,"line":249},[155,456,233],{},[47,458,460],{"id":459},"suspense-与数据获取","Suspense 与数据获取",[23,462,463,465,466,78],{},[32,464,279],{}," 让组件在等待异步资源（懒加载组件、支持 Suspense 的数据获取库）时展示 fallback，而不必手写 ",[32,467,468],{},"if (loading) return \u003CSpinner \u002F>",[106,470,472],{"className":149,"code":471,"language":151,"meta":112,"style":112},"\u003CSuspense fallback={\u003CSpinner \u002F>}>\n  \u003CUserProfile userId={id} \u002F>\n\u003C\u002FSuspense>\n",[32,473,474,479,484],{"__ignoreMap":112},[155,475,476],{"class":157,"line":158},[155,477,478],{},"\u003CSuspense fallback={\u003CSpinner \u002F>}>\n",[155,480,481],{"class":157,"line":164},[155,482,483],{},"  \u003CUserProfile userId={id} \u002F>\n",[155,485,486],{"class":157,"line":170},[155,487,488],{},"\u003C\u002FSuspense>\n",[23,490,491,492,495],{},"结合支持 Suspense 的数据请求方案（如部分 Relay、",[32,493,494],{},"use()"," API 或框架内置能力），可以让加载状态的处理更统一，具体接入方式因所用数据层而异。",[47,497,499],{"id":498},"strictmode-与双重渲染","StrictMode 与双重渲染",[23,501,502,503,506,507,510,511,514,515,517],{},"开发模式下 ",[32,504,505],{},"StrictMode"," 会",[27,508,509],{},"额外执行一次","部分逻辑以帮助发现副作用问题，",[27,512,513],{},"不代表生产环境会渲染两次","。见 ",[41,516,44],{"href":43},"。这个「双重调用」专门用于暴露不纯的组件函数或未正确清理的 Effect——如果一个 Effect 执行两次就出现异常行为（如重复发起请求且没有取消机制），往往说明清理逻辑本身有问题，而不是 StrictMode 的锅。",[47,519,520],{"id":520},"最佳实践",[127,522,523,529,538,545,548],{},[73,524,525,526,528],{},"列表 ",[32,527,34],{}," 一律使用稳定的业务标识（id），不要用数组下标，尤其是列表可能重排、插入、删除的场景。",[73,530,531,532,534,535,537],{},"筛选、搜索等「输入即触发大量计算」的交互，用 ",[32,533,283],{},"\u002F",[32,536,375],{}," 保持输入响应性。",[73,539,540,541,544],{},"理解「Render 阶段可能被多次调用\u002F丢弃」，因此组件函数体必须是",[27,542,543],{},"纯函数","，不要在渲染过程中直接产生副作用（如修改外部变量、发起请求）。",[73,546,547],{},"Suspense 边界的粒度要适当：太粗会导致整块 UI 一起等待，太细会导致 fallback 闪烁过多。",[73,549,550,551,553],{},"排查「明明数据变了但界面没更新」问题时，先检查是否是 ",[32,552,34],{}," 导致组件被错误复用\u002F重建。",[47,555,556],{"id":556},"常见坑",[558,559,560,576],"table",{},[561,562,563],"thead",{},[564,565,566,570,573],"tr",{},[567,568,569],"th",{},"现象",[567,571,572],{},"常见原因",[567,574,575],{},"处理",[577,578,579,593,611,624,635],"tbody",{},[564,580,581,585,590],{},[582,583,584],"td",{},"列表重排后输入框内容跑到别的行",[582,586,587,588],{},"用了下标作 ",[32,589,34],{},[582,591,592],{},"改用稳定业务 id",[564,594,595,602,605],{},[582,596,597,598,601],{},"组件函数体内直接调用了 ",[32,599,600],{},"console.log","\u002F发请求造成重复执行",[582,603,604],{},"不了解 Render 阶段可能被多次调用（尤其 StrictMode 开发下）",[582,606,607,608,610],{},"副作用移入 ",[32,609,104],{},"，保持渲染函数纯净",[564,612,613,618,621],{},[582,614,615,617],{},[32,616,283],{}," 里的更新看起来「没生效」",[582,619,620],{},"把同步的紧急更新也包进了 transition，导致响应变慢",[582,622,623],{},"只把「非紧急、可以稍后计算」的更新放进 transition",[564,625,626,629,632],{},[582,627,628],{},"Suspense fallback 一直不消失",[582,630,631],{},"内部资源的 Promise 未正确 resolve，或数据层未支持 Suspense 协议",[582,633,634],{},"确认所用数据获取方案是否真正支持 Suspense",[564,636,637,640,643],{},[582,638,639],{},"生产环境「感觉」也渲染了两次",[582,641,642],{},"与 StrictMode 无关，可能是父组件本身多次触发更新",[582,644,645],{},"用 Profiler 确认渲染次数与触发源",[47,647,648],{"id":648},"延伸阅读",[127,650,651,656,663],{},[73,652,653,655],{},[41,654,44],{"href":43},"：StrictMode 与组件模型",[73,657,658,662],{},[41,659,661],{"href":660},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fruntime","运行时优化","：减少重渲染",[73,664,665,669],{},[41,666,668],{"href":667},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Feffect","Effect 与副作用","：Commit 阶段与 Effect 执行时机",[47,671,672],{"id":672},"参考文献",[23,674,675],{},"以下链接在编写时均可正常访问：",[558,677,678,688],{},[561,679,680],{},[564,681,682,685],{},[567,683,684],{},"资料",[567,686,687],{},"说明",[577,689,690,702,714,726,738],{},[564,691,692,700],{},[582,693,694],{},[41,695,699],{"href":696,"rel":697},"https:\u002F\u002Fzh-hans.react.dev\u002Flearn\u002Frender-and-commit",[698],"nofollow","React：渲染与提交",[582,701,55],{},[564,703,704,711],{},[582,705,706],{},[41,707,710],{"href":708,"rel":709},"https:\u002F\u002Fzh-hans.react.dev\u002Flearn\u002Fpreserving-and-resetting-state",[698],"React：preserving state",[582,712,713],{},"key 与状态",[564,715,716,723],{},[582,717,718],{},[41,719,722],{"href":720,"rel":721},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002FuseTransition",[698],"React：useTransition",[582,724,725],{},"过渡更新",[564,727,728,735],{},[582,729,730],{},[41,731,734],{"href":732,"rel":733},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002FSuspense",[698],"React：Suspense",[582,736,737],{},"异步边界",[564,739,740,747],{},[582,741,742],{},[41,743,746],{"href":744,"rel":745},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002FStrictMode",[698],"React：StrictMode",[582,748,749],{},"开发模式检查",[751,752,753],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":112,"searchDepth":164,"depth":164,"links":755},[756,757,758,761,762,763,764,765,766,767,768],{"id":49,"depth":164,"text":49},{"id":67,"depth":164,"text":68},{"id":117,"depth":164,"text":118,"children":759},[760],{"id":145,"depth":170,"text":146},{"id":261,"depth":164,"text":262},{"id":290,"depth":164,"text":283},{"id":459,"depth":164,"text":460},{"id":498,"depth":164,"text":499},{"id":520,"depth":164,"text":520},{"id":556,"depth":164,"text":556},{"id":648,"depth":164,"text":648},{"id":672,"depth":164,"text":672},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FReact\u002Frendering","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FReact\u002Frendering.md","md",{"items":774},[775,779,783,787,791,793],{"path":776,"title":777,"description":778,"category":10,"score":188},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject","工程化","新建 React 项目时，官方与社区主流工具为 Vite；全栈场景常用 Next.js。涵盖 Vite + React、Create React App 现状、Next.js、目录结构、环境变量、部署、最佳实践与常见坑。",{"path":780,"title":781,"description":782,"category":10,"score":188},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftypescript","TypeScript","React 官方推荐 TypeScript。类型主要落在 Props、事件、Ref 与 Hooks 返回值上。见 组件。",{"path":784,"title":785,"description":786,"category":10,"score":188},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftest","测试","React 组件测试推荐 React Testing Library（RTL）：从用户可见行为断言，而非实现细节。运行器常用 Vitest 或 Jest。涵盖安装配置、查询优先级、异步测试、Mock 网络请求、最佳实践与常见坑。",{"path":788,"title":789,"description":790,"category":10,"score":188},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild","构建与分包","通过代码分割与构建配置减小首屏 JavaScript 体积。涵盖 React.lazy\u002FSuspense、路由级分割、Tree Shaking、Vite 生产构建、体积分析、最佳实践与常见坑。运行时优化见 runtime。",{"path":667,"title":668,"description":792,"category":10,"score":188},"useEffect 用于在组件渲染后执行与外部系统同步的逻辑（请求、订阅、手动改 DOM 等）。涵盖基本用法、依赖数组、适用\u002F不适用场景、useLayoutEffect、最佳实践与常见坑。",{"path":794,"title":795,"description":796,"category":10,"score":188},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fstate","状态管理","React 组件内 state 用 Hooks；跨组件共享状态可选 Context、Redux Toolkit、Zustand 等。见 Context Hook。",{"series":798,"title":12,"items":799,"index":206,"prev":845,"next":846},"booklet\u002Fframe\u002Freact",[800,802,804,808,812,816,818,822,826,828,829,833,835,839,841,843],{"path":43,"title":44,"date":801,"pinned":15},"2020-02-12",{"path":788,"title":789,"date":803,"pinned":15},"2025-07-23",{"path":805,"title":806,"date":807,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents","组件与 Props","2020-06-11",{"path":809,"title":810,"date":811,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fcontext","Context","2021-10-16",{"path":813,"title":814,"date":815,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fecology","生态","2023-02-04",{"path":667,"title":668,"date":817,"pinned":15},"2024-10-11",{"path":819,"title":820,"date":821,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fjsx","JSX 与语法","2020-02-29",{"path":823,"title":824,"date":825,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fmemo","Memo、Callback 与 Ref","2021-02-28",{"path":776,"title":777,"date":827,"pinned":15},"2026-05-08",{"path":7,"title":8,"date":14,"pinned":15},{"path":830,"title":831,"date":832,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter","路由","2023-09-06",{"path":660,"title":661,"date":834,"pinned":15},"2020-02-05",{"path":836,"title":837,"date":838,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate","State 与 Reducer","2021-02-21",{"path":794,"title":795,"date":840,"pinned":15},"2024-02-07",{"path":784,"title":785,"date":842,"pinned":15},"2025-09-26",{"path":780,"title":781,"date":844,"pinned":15},"2026-03-13",{"path":776,"title":777,"date":827,"pinned":15},{"path":830,"title":831,"date":832,"pinned":15}]