[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents":548,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents":574},{"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":544,"stem":545,"id":546,"extension":547},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents","组件与 Props","React 应用由组件树组成。Props 是父组件传入子组件的只读参数；子组件通过回调 props 向父组件上报事件。见 JSX、Hooks。","技能小册",[12,13],"react","components","2020-06-11",false,{"type":17,"value":18,"toc":532},"minimark",[19,23,47,52,143,164,167,217,227,230,236,279,283,374,378,385,389,396,400,473,476,479,528],[20,21,8],"h1",{"id":22},"组件与-props",[24,25,26,27,31,32,35,36,41,42,46],"p",{},"React 应用由组件树组成。Props 是父组件传入子组件的",[28,29,30],"strong",{},"只读","参数；子组件通过",[28,33,34],{},"回调 props"," 向父组件上报事件。见 ",[37,38,40],"a",{"href":39},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fjsx","JSX","、",[37,43,45],{"href":44},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate","Hooks","。",[48,49,51],"h2",{"id":50},"props-与单向数据流","Props 与单向数据流",[53,54,59],"pre",{"className":55,"code":56,"language":57,"meta":58,"style":58},"language-tsx shiki shiki-themes material-theme-lighter github-light github-dark","type ButtonProps = {\n  label: string;\n  disabled?: boolean;\n  onPress: () => void;\n};\n\nfunction Button({ label, disabled = false, onPress }: ButtonProps) {\n  return (\n    \u003Cbutton type=\"button\" disabled={disabled} onClick={onPress}>\n      {label}\n    \u003C\u002Fbutton>\n  );\n}\n","tsx","",[60,61,62,70,76,82,88,94,101,107,113,119,125,131,137],"code",{"__ignoreMap":58},[63,64,67],"span",{"class":65,"line":66},"line",1,[63,68,69],{},"type ButtonProps = {\n",[63,71,73],{"class":65,"line":72},2,[63,74,75],{},"  label: string;\n",[63,77,79],{"class":65,"line":78},3,[63,80,81],{},"  disabled?: boolean;\n",[63,83,85],{"class":65,"line":84},4,[63,86,87],{},"  onPress: () => void;\n",[63,89,91],{"class":65,"line":90},5,[63,92,93],{},"};\n",[63,95,97],{"class":65,"line":96},6,[63,98,100],{"emptyLinePlaceholder":99},true,"\n",[63,102,104],{"class":65,"line":103},7,[63,105,106],{},"function Button({ label, disabled = false, onPress }: ButtonProps) {\n",[63,108,110],{"class":65,"line":109},8,[63,111,112],{},"  return (\n",[63,114,116],{"class":65,"line":115},9,[63,117,118],{},"    \u003Cbutton type=\"button\" disabled={disabled} onClick={onPress}>\n",[63,120,122],{"class":65,"line":121},10,[63,123,124],{},"      {label}\n",[63,126,128],{"class":65,"line":127},11,[63,129,130],{},"    \u003C\u002Fbutton>\n",[63,132,134],{"class":65,"line":133},12,[63,135,136],{},"  );\n",[63,138,140],{"class":65,"line":139},13,[63,141,142],{},"}\n",[144,145,146,157],"ul",{},[147,148,149,152,153,156],"li",{},[28,150,151],{},"不要修改 props","；需要“子改父数据”时由父组件持有 state，通过 ",[60,154,155],{},"onPress"," 等回调更新。",[147,158,159,160,163],{},"展开传递：",[60,161,162],{},"\u003CProfile {...user} \u002F>"," 等价于逐个属性绑定。",[48,165,166],{"id":166},"受控与非受控组件",[168,169,170,183],"table",{},[171,172,173],"thead",{},[174,175,176,180],"tr",{},[177,178,179],"th",{},"类型",[177,181,182],{},"说明",[184,185,186,200],"tbody",{},[174,187,188,194],{},[189,190,191],"td",{},[28,192,193],{},"受控",[189,195,196,197],{},"表单值由 React state 驱动，如 ",[60,198,199],{},"value={text} onChange={e => setText(e.target.value)}",[174,201,202,207],{},[189,203,204],{},[28,205,206],{},"非受控",[189,208,209,210,213,214],{},"值存在 DOM 中，用 ",[60,211,212],{},"ref"," 读取，如 ",[60,215,216],{},"\u003Cinput ref={inputRef} defaultValue=\"...\" \u002F>",[24,218,219,220,226],{},"复杂表单可配合 ",[37,221,225],{"href":222,"rel":223},"https:\u002F\u002Freact-hook-form.com\u002F",[224],"nofollow","React Hook Form"," 等库。",[48,228,229],{"id":229},"组合模式",[231,232,234],"h3",{"id":233},"children",[60,235,233],{},[53,237,239],{"className":55,"code":238,"language":57,"meta":58,"style":58},"function Card({ children }: { children: React.ReactNode }) {\n  return \u003Csection className=\"card\">{children}\u003C\u002Fsection>;\n}\n\n\u003CCard>\n  \u003Ch2>标题\u003C\u002Fh2>\n  \u003Cp>内容\u003C\u002Fp>\n\u003C\u002FCard>;\n",[60,240,241,246,251,255,259,264,269,274],{"__ignoreMap":58},[63,242,243],{"class":65,"line":66},[63,244,245],{},"function Card({ children }: { children: React.ReactNode }) {\n",[63,247,248],{"class":65,"line":72},[63,249,250],{},"  return \u003Csection className=\"card\">{children}\u003C\u002Fsection>;\n",[63,252,253],{"class":65,"line":78},[63,254,142],{},[63,256,257],{"class":65,"line":84},[63,258,100],{"emptyLinePlaceholder":99},[63,260,261],{"class":65,"line":90},[63,262,263],{},"\u003CCard>\n",[63,265,266],{"class":65,"line":96},[63,267,268],{},"  \u003Ch2>标题\u003C\u002Fh2>\n",[63,270,271],{"class":65,"line":103},[63,272,273],{},"  \u003Cp>内容\u003C\u002Fp>\n",[63,275,276],{"class":65,"line":109},[63,277,278],{},"\u003C\u002FCard>;\n",[231,280,282],{"id":281},"插槽式-props","插槽式 props",[53,284,286],{"className":55,"code":285,"language":57,"meta":58,"style":58},"function Layout({\n  header,\n  footer,\n  children,\n}: {\n  header?: React.ReactNode;\n  footer?: React.ReactNode;\n  children: React.ReactNode;\n}) {\n  return (\n    \u003C>\n      {header}\n      \u003Cmain>{children}\u003C\u002Fmain>\n      {footer}\n    \u003C\u002F>\n  );\n}\n",[60,287,288,293,298,303,308,313,318,323,328,333,337,342,347,352,358,364,369],{"__ignoreMap":58},[63,289,290],{"class":65,"line":66},[63,291,292],{},"function Layout({\n",[63,294,295],{"class":65,"line":72},[63,296,297],{},"  header,\n",[63,299,300],{"class":65,"line":78},[63,301,302],{},"  footer,\n",[63,304,305],{"class":65,"line":84},[63,306,307],{},"  children,\n",[63,309,310],{"class":65,"line":90},[63,311,312],{},"}: {\n",[63,314,315],{"class":65,"line":96},[63,316,317],{},"  header?: React.ReactNode;\n",[63,319,320],{"class":65,"line":103},[63,321,322],{},"  footer?: React.ReactNode;\n",[63,324,325],{"class":65,"line":109},[63,326,327],{},"  children: React.ReactNode;\n",[63,329,330],{"class":65,"line":115},[63,331,332],{},"}) {\n",[63,334,335],{"class":65,"line":121},[63,336,112],{},[63,338,339],{"class":65,"line":127},[63,340,341],{},"    \u003C>\n",[63,343,344],{"class":65,"line":133},[63,345,346],{},"      {header}\n",[63,348,349],{"class":65,"line":139},[63,350,351],{},"      \u003Cmain>{children}\u003C\u002Fmain>\n",[63,353,355],{"class":65,"line":354},14,[63,356,357],{},"      {footer}\n",[63,359,361],{"class":65,"line":360},15,[63,362,363],{},"    \u003C\u002F>\n",[63,365,367],{"class":65,"line":366},16,[63,368,136],{},[63,370,372],{"class":65,"line":371},17,[63,373,142],{},[231,375,377],{"id":376},"复合组件compound-components","复合组件（Compound Components）",[24,379,380,381,46],{},"父组件通过 Context 向子组件共享隐式状态（如 Tabs、Select），避免 props 层层传递。实现时常配合 ",[37,382,384],{"href":383},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fcontext","Context",[48,386,388],{"id":387},"提升状态lifting-state-up","提升状态（Lifting State Up）",[24,390,391,392,395],{},"多个兄弟组件共享状态时，将 state 提升到",[28,393,394],{},"最近共同父组件","，再通过 props 下发。",[48,397,399],{"id":398},"与-vue-对照简表","与 Vue 对照（简表）",[168,401,402,414],{},[171,403,404],{},[174,405,406,408,411],{},[177,407],{},[177,409,410],{},"React",[177,412,413],{},"Vue",[184,415,416,432,449,460],{},[174,417,418,421,427],{},[189,419,420],{},"传参",[189,422,423,426],{},[60,424,425],{},"props"," 只读",[189,428,429,431],{},[60,430,425],{}," 单向",[174,433,434,437,444],{},[189,435,436],{},"双向绑定",[189,438,439,440,443],{},"受控 + ",[60,441,442],{},"onChange"," 或库",[189,445,446],{},[60,447,448],{},"v-model",[174,450,451,454,458],{},[189,452,453],{},"默认插槽",[189,455,456],{},[60,457,233],{},[189,459,453],{},[174,461,462,465,468],{},[189,463,464],{},"作用域插槽",[189,466,467],{},"render props \u002F 函数 children",[189,469,470],{},[60,471,472],{},"v-slot",[48,474,475],{"id":475},"参考文献",[24,477,478],{},"以下链接在编写时均可正常访问：",[168,480,481,490],{},[171,482,483],{},[174,484,485,488],{},[177,486,487],{},"资料",[177,489,182],{},[184,491,492,504,516],{},[174,493,494,501],{},[189,495,496],{},[37,497,500],{"href":498,"rel":499},"https:\u002F\u002Fzh-hans.react.dev\u002Flearn\u002Fyour-first-component",[224],"React：组件",[189,502,503],{},"首组件",[174,505,506,513],{},[189,507,508],{},[37,509,512],{"href":510,"rel":511},"https:\u002F\u002Fzh-hans.react.dev\u002Flearn\u002Fpassing-props-to-a-component",[224],"React：Props",[189,514,515],{},"传递 props",[174,517,518,525],{},[189,519,520],{},[37,521,524],{"href":522,"rel":523},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact-dom\u002Fcomponents\u002Finput#controlling-an-input-with-a-state-variable",[224],"React：受控组件",[189,526,527],{},"受控 input",[529,530,531],"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":58,"searchDepth":72,"depth":72,"links":533},[534,535,536,541,542,543],{"id":50,"depth":72,"text":51},{"id":166,"depth":72,"text":166},{"id":229,"depth":72,"text":229,"children":537},[538,539,540],{"id":233,"depth":78,"text":233},{"id":281,"depth":78,"text":282},{"id":376,"depth":78,"text":377},{"id":387,"depth":72,"text":388},{"id":398,"depth":72,"text":399},{"id":475,"depth":72,"text":475},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fcomponents","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fcomponents.md","md",{"items":549},[550,554,558,562,566,570],{"path":551,"title":552,"description":553,"category":10,"score":96},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering","渲染与协调","React 将组件渲染为虚拟 DOM 树，再协调（reconciliation）到浏览器 DOM。理解该过程有助于解释 key、memo 与性能优化。涵盖渲染流程、Fiber、startTransition、Suspense、最佳实践与常见坑。",{"path":555,"title":556,"description":557,"category":10,"score":96},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject","工程化","新建 React 项目时，官方与社区主流工具为 Vite；全栈场景常用 Next.js。涵盖 Vite + React、Create React App 现状、Next.js、目录结构、环境变量、部署、最佳实践与常见坑。",{"path":559,"title":560,"description":561,"category":10,"score":96},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftypescript","TypeScript","React 官方推荐 TypeScript。类型主要落在 Props、事件、Ref 与 Hooks 返回值上。见 组件。",{"path":563,"title":564,"description":565,"category":10,"score":96},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftest","测试","React 组件测试推荐 React Testing Library（RTL）：从用户可见行为断言，而非实现细节。运行器常用 Vitest 或 Jest。涵盖安装配置、查询优先级、异步测试、Mock 网络请求、最佳实践与常见坑。",{"path":567,"title":568,"description":569,"category":10,"score":96},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild","构建与分包","通过代码分割与构建配置减小首屏 JavaScript 体积。涵盖 React.lazy\u002FSuspense、路由级分割、Tree Shaking、Vite 生产构建、体积分析、最佳实践与常见坑。运行时优化见 runtime。",{"path":571,"title":572,"description":573,"category":10,"score":96},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Feffect","Effect 与副作用","useEffect 用于在组件渲染后执行与外部系统同步的逻辑（请求、订阅、手动改 DOM 等）。涵盖基本用法、依赖数组、适用\u002F不适用场景、useLayoutEffect、最佳实践与常见坑。",{"series":575,"title":12,"items":576,"index":72,"prev":622,"next":623},"booklet\u002Fframe\u002Freact",[577,581,583,584,586,590,592,595,599,601,603,607,611,614,618,620],{"path":578,"title":579,"date":580,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fbasics","React 基础","2020-02-12",{"path":567,"title":568,"date":582,"pinned":15},"2025-07-23",{"path":7,"title":8,"date":14,"pinned":15},{"path":383,"title":384,"date":585,"pinned":15},"2021-10-16",{"path":587,"title":588,"date":589,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fecology","生态","2023-02-04",{"path":571,"title":572,"date":591,"pinned":15},"2024-10-11",{"path":39,"title":593,"date":594,"pinned":15},"JSX 与语法","2020-02-29",{"path":596,"title":597,"date":598,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fmemo","Memo、Callback 与 Ref","2021-02-28",{"path":555,"title":556,"date":600,"pinned":15},"2026-05-08",{"path":551,"title":552,"date":602,"pinned":15},"2026-07-10",{"path":604,"title":605,"date":606,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter","路由","2023-09-06",{"path":608,"title":609,"date":610,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fruntime","运行时优化","2020-02-05",{"path":44,"title":612,"date":613,"pinned":15},"State 与 Reducer","2021-02-21",{"path":615,"title":616,"date":617,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fstate","状态管理","2024-02-07",{"path":563,"title":564,"date":619,"pinned":15},"2025-09-26",{"path":559,"title":560,"date":621,"pinned":15},"2026-03-13",{"path":567,"title":568,"date":582,"pinned":15},{"path":383,"title":384,"date":585,"pinned":15}]