React:描述UI 官网笔记
文章目录一、你的第一个组件1. 什么是组件核心定义2. 如何定义一个组件三个步骤3. 组件的组织逻辑父与子4. 核心思想万物皆组件二、 组件的导入与导出1. 为什么要拆分组件2. 导出与导入的两种方式三、使用 JSX 书写标签语言1. 为什么要使用 JSX2. JSX 的核心语法规则① 只能有一个根元素② 标签必须闭合③ JS 表达式要写在花括号 {} 中④ 属性名使用小驼峰命名 (camelCase)⑤ 内联样式必须是对象四、 将 Props 传递给组件1. 什么是 Props2. 如何使用 Props两步走3. 高级传递技巧4. 特殊属性children5. Props 的不可变性 (Immutability)五、条件渲染1. 三种常用的逻辑控制方式2. 详细语法说明A. 条件返回 nullB. 三元运算符 ? : (最常用)C. 逻辑与 (短路运算符)D. 使用变量赋值六、 列表渲染 (Rendering Lists)1. 核心方法map() 与 filter()2. 为什么需要 key3. key 的使用规则4. 常见陷阱与注意事项七、保持组件纯粹 (Keeping Components Pure)1. 什么是纯组件 (Pure Components)2. 严禁突变 (Mutation)常见的错误❌ 错误示例修改外部变量✅ 正确示例通过 Props 传递4. 严格模式 (Strict Mode) 的作用5. 副作用 (Side Effects) 的安放地6. 为什么必须保持组件的纯粹性1. 提高性能跳过不必要的渲染2. 随时中断与恢复支持并发模式3. 跨环境运行从浏览器到服务器4. 预测性与易于测试5. 避免渲染顺序导致的 Bug 核心“超能力”总结 核心心法总结八、将 UI 视为树 (UI as a Tree)1. 渲染树 (Render Tree)2. 模块依赖树 (Dependency Tree)3. 关键区别4. 快速记忆一、你的第一个组件1. 什么是组件核心定义本质组件是 React 应用的最小构建单元。它将标签 (HTML)、样式 (CSS)和交互逻辑 (JS)封装在一起形成一个独立的、可复用的 UI 模块。表现形式在代码层面组件本质上就是一个返回JSX标签的普通的JavaScript 函数。角色比喻类似于乐高积木你可以通过组合、嵌套这些积木来拼出复杂的页面。2. 如何定义一个组件三个步骤编写组件时必须遵守的“工业标准”流程导出组件使用export default关键字确保该组件可以被其他文件import引入。定义函数使用function关键字声明函数。⚠️ 必记陷阱组件名称必须以大写字母开头如Profile而不是profile。React 依靠首字母大小写来区分它是自定义组件还是原生 HTML 标签。编写标签 (JSX)使用return关键字返回 UI 结构。⚠️ 格式陷阱如果标签没有紧跟在return关键字后面即换行写了必须用小括号()包裹否则return下方的代码将被 JS 引擎自动忽略。3. 组件的组织逻辑父与子嵌套使用你可以像使用 HTML 标签一样使用组件例如Profile /。父子关系渲染其他组件的组件称为父组件如Gallery被渲染的组件称为子组件如Profile。⚠️ 性能陷阱重点永远不要在组件内部定义另一个组件原因每次父组件渲染时内部定义的子组件都会被重新创建。这不仅极其缓慢还会导致子组件的状态丢失产生难以调试的 Bug。正确做法所有的组件都应该在文件的最顶层 (Top-level)独立定义。4. 核心思想万物皆组件从局部到整体在 React 的世界里小到按钮 (Button)、头像 (Avatar)大到侧边栏 (Sidebar)、列表 (List)甚至整个页面 (Page)都是由组件构成的。根组件每个 React 应用都有一个起始点通常被称为“根组件”Root Component在标准脚手架中通常命名为App.js或index.js。声明式 UI你只需要通过组件定义 UI “看起来是什么样”React 的渲染引擎会自动帮你把这些 JavaScript 函数转化为浏览器能够识别并显示的真实HTML DOM。二、 组件的导入与导出1. 为什么要拆分组件可重用性将组件独立成文件可以在不同的地方重复使用。可维护性减少单个文件的体积让代码结构更清晰查找更方便。模块化每个文件只负责一个功能符合“单一职责原则”。2. 导出与导入的两种方式这是 JavaScript ES 模块的标准语法React 深度依赖这两种方式特性默认导出 (Default Export)具名导出 (Named Export)数量限制一个文件有且仅有一个一个文件可以有任意多个导出语法export default function App() {}export function Profile() {}导入语法import App from ./App.js;import { Profile } from ./App.js;命名灵活性导入时可以自定义名称(如import MyBtn)导入名必须与导出名严格一致通常文件中仅包含一个组件时人们会选择默认导出而当文件中包含多个组件或某个值需要导出时则会选择具名导出。同一文件中有且仅有一个默认导出但可以有多个具名导出文件后缀在 React 环境下import Gallery from ‘./Gallery’ 和 ./Gallery.js 通常是通用的但使用 .js 后缀更符合原生 ES 模块规范。三、使用 JSX 书写标签语言1. 为什么要使用 JSX在没有 JSX 之前创建 UI 需要调用繁琐的 React.createElement 函数// 不使用 JSXconstelementReact.createElement(h1,{className:title},Hello);// 使用 JSX (直观、易读)constelementh1 classNametitleHello/h1;2. JSX 的核心语法规则① 只能有一个根元素JSX 表达式必须被包裹在一个闭合标签内。如果你不想增加额外的 DOM 层级可以使用Fragment (…/)。// ❌ 错误不能并列两个根标签return(divA/divdivB/div);// ✅ 正确return(divA/divdivB/div/);② 标签必须闭合在 HTML 中某些标签可以不写结束符号如img但在 JSX 中所有标签必须闭合或者使用自闭合。img srclogo.png/// ✅ 必须有斜杠闭合br/③ JS 表达式要写在花括号 {} 中如果你想在 UI 中引用变量、执行运算或调用函数必须将其包裹在 {} 里。constnameGemini;h1Hello,{name}/h1;// 输出: Hello, Geminip11{11}/p;// 输出: 1 1 2④ 属性名使用小驼峰命名 (camelCase)JSX 本质上更接近 JavaScript 而非 HTML因此属性名要遵循 JS 的变量命名规则。class 变为 className因为 class 是 JS 的保留关键字。onclick 变为 onClick。tabindex 变为 tabIndex。⑤ 内联样式必须是对象在 HTML 中样式是字符串但在 JSX 中必须是一个对象且 CSS 属性名也要用小驼峰。// 注意外层花括号表示进入 JS 环境内层表示对象div style{{color:red,fontSize:20px}}红色的文字/div四、 将 Props 传递给组件1. 什么是 Props定义Props 是你传递给 JSX 标签的信息类似于 HTML 的属性如 src, alt。用途父组件通过 Props 将信息对象、数组、函数等传递给子组件。特性只读不可变。Props 就像一张时间快照反映了组件在特定瞬间的数据状态。2. 如何使用 Props两步走向子组件传递在父组件中像写 HTML 属性一样写 Props。Avatar person{{name:Lin}}size{100}/在子组件中读取通过解构赋值语法直接获取变量。functionAvatar({person,size}){// 直接使用 person 和 size}⚠️ 陷阱解构时不要忘记花括号 { }否则参数会变成整个 props 对象。3. 高级传递技巧默认值如果父组件没传某个值可以设置备选默认值。functionAvatar({size100}){...}// 仅在 size 缺失或为 undefined 时生效展开语法 (…)当需要将所有属性原封不动转发给下层时使用。⚠️ 建议不要过度使用清晰的逐个传递通常更有利于代码维护。Avatar{...props}/4. 特殊属性children定义当你嵌套 JSX 内容时如 嵌套的内容会被父组件在children属性中接收。比喻父组件像是一个带有“洞”的容器children 就是填入这个洞的内容。这种模式常用于布局组件如面板、网格。importAvatarfrom./components/Avatar;functionCard(props:{hh:string;children:React.ReactNode}){console.log();console.log(Card props,props);console.log();returndiv classNamecard{props.children}/div;}functionApp(){return(Card hhpkAvatar size{100}person{{name:Katsuko Saruhashi,imageId:YfeOqp2,}}//Card/);}exportdefaultApp;5. Props 的不可变性 (Immutability)核心规则永远不要尝试修改 Props。如何更新如果需要响应用户输入或改变数据组件必须“请求”其父组件传递新的 Props 对象。内存管理旧的 Props 会被丢弃由 JavaScript 引擎自动回收内存。场景语法 / 操作示例说明传递数据Component namevalue /在父组件中像 HTML 属性一样传递数据。读取数据function Component({ name }) { ... }在子组件参数中使用解构赋值直接读取属性。缺失值处理function Component({ name 默认值 })为可选属性设置默认值仅在未传值或值为undefined时生效。包装嵌套 UIfunction Wrapper({ children }) { ... }使用内置的children属性来渲染嵌套在组件内部的 JSX 内容。属性转发Component {...props} /使用JSX 展开语法将父组件收到的所有 props 快速转发给子组件。五、条件渲染1. 三种常用的逻辑控制方式在 React 中我们直接使用 JavaScript 的原生语法来处理 UI 的逻辑分支语法适用场景语义口诀if / else复杂的逻辑判断或者需要返回完全不同的 JSX 树。“如果…就返回 A否则返回 B。”三元运算符? :在 JSX 内部进行“二选一”的局部微调。“是真吗是就显示 A不是就显示 B。”逻辑与只有当条件为真时才显示否则什么都不显示。“如果是真的就显示它不是就算了。”2. 详细语法说明A. 条件返回null如果你不希望组件渲染任何内容可以直接返回null。注意React 会跳过该组件的渲染不会在网页 DOM 中产生任何节点。if(isPacked){returnnull;}returnli classNameitem{name}/li;B. 三元运算符? :(最常用)适合在 HTML 结构内部做局部的内容切换。// 如果已打包显示删除线和对勾否则只显示名称return(li{isPacked?(del{name ✅}/del):(name)}/li);C. 逻辑与 (短路运算符)适合“有则显示无则隐藏”的简单开关场景。// 只有当 isPacked 为 true 时才渲染对勾return(li{name}{isPacked✅}/li);⚠️ 避坑指南不要把数字放在 左侧❌{count p消息/p}如果 count 是 0React 会在页面上渲染出数字 0。✅{count 0 p消息/p}确保左侧是一个明确的布尔值。D. 使用变量赋值当判断逻辑非常复杂导致 JSX 嵌套严重时这是最清晰、最易维护的方法。letcontentname;if(isPacked){contentdel{name ✅}/del;}return(li classNameitem{content}/li);想要实现…建议使用方案代码示例页面/组件级的大替换if / else提前返回if (!isLogged) return LoginPage /;局部内容的“二选一”三元运算符{ ? : }span{isVIP ? 尊享会员 : 普通用户}/span局部内容的“显示或隐藏”逻辑与{ }{showDetails DetailedInfo /}逻辑太乱、嵌套太深变量赋值(let content)let ui isPacked ? del{name}/del : name;六、 列表渲染 (Rendering Lists)1. 核心方法map()与filter()React 深度利用 JavaScript 原生方法来处理数据集合实现声明式渲染filter()筛选用于从原始数组中挑出符合条件的子集。例如从人员名单中只挑选出职业为“化学家”的对象。map()转换将数据数组中的每一项“映射”为 JSX 元素生成组件数组。2. 为什么需要keykey是列表渲染中最重要的属性。它不是传给组件的普通 Prop而是 React 内部专用的**“身份证”**。唯一标识key帮助 React 建立数据与组件之间的一一对应关系。性能优化当列表发生排序、插入或删除时React 通过key快速识别哪些元素是移动的、哪些是新出的从而避免暴力重新渲染整个列表极大提升效率。状态保持防止出现 Bug。如果没有稳定的keyReact 可能会错误地关联组件状态例如删除第一行文字后第二行的输入框内容却消失了。3.key的使用规则规则详细说明兄弟间唯一在同一个map()产生的数组中每个key必须是唯一的不要求全局唯一。稳定性key在组件生命周期内必须保持不变。绝对不要使用Math.random()或时间戳。来源优先使用数据库主键ID、UUID 或数据中具备唯一特性的字段。位置key必须直接写在map()循环返回的最外层JSX 标签上。4. 常见陷阱与注意事项慎用索引 (Index)不要默认使用数组下标作为key。如果列表涉及重新排序、插入或删除使用索引会导致组件状态错位。禁止动态生成在render过程中实时生成的key会导致组件在每次更新时都“彻底销毁并重建”造成严重的性能问题且会丢失 DOM 状态。Fragment 嵌套如果你希望map返回多个并列节点例如一对dt和dd不能使用简写 /。必须使用显式的Fragment key{...}因为简写形式不支持携带任何属性。import{Fragment}fromreact;// ...constlistItemspeople.map(personFragment key{person.id}h1{person.name}/h1p{person.bio}/p/Fragment);七、保持组件纯粹 (Keeping Components Pure)1. 什么是纯组件 (Pure Components)React 的设计哲学假设你编写的每个组件都是一个纯函数。一个纯组件必须满足以下两个特征只负责自己的任务它不应该更改在调用前就已存在的任何对象或变量。类比就像数学公式y 2 x y 2xy2x计算y yy的过程不应该偷偷改掉外部x xx的值。输入相同输出相同只要props、state和context一致返回的 JSX 必须永远一致。2. 严禁突变 (Mutation)常见的错误组件“不纯”最常见的原因是在渲染过程中修改了外部变量。❌ 错误示例修改外部变量letguest0;functionCup(){// 错误在渲染期间更改了函数外部声明的变量guestguest1;returnh2Guest #{guest}/h2;}✅ 正确示例通过 Props 传递functionCup({guest}){// 正确组件输出完全取决于传入的参数returnh2Guest #{guest}/h2;}4. 严格模式 (Strict Mode) 的作用React 提供了StrictMode工具通常包裹在根组件外来帮助开发者在开发阶段发现“不纯”的组件。双调用机制在开发环境下React 会故意调用组件函数两次。发现漏洞如果组件是不纯的例如修改了外部变量两次调用会导致结果产生累加或偏差例如本应显示Guest #1, #2, #3由于不纯却显示了Guest #2, #4, #6从而让逻辑漏洞显形。5. 副作用 (Side Effects) 的安放地虽然渲染必须保持纯粹但程序总需要改变数据如发送请求、启动动画这些操作被称为“副作用”。为了不干扰纯粹的渲染过程副作用应该放在首选位置事件处理程序 (Event Handlers)例如点击按钮、提交表单时执行的函数。特点它们不在渲染期间运行因此不需要是纯函数。最后手段useEffect如果某些逻辑必须在组件渲染完成后自动触发。建议这是最后的手段应优先尝试在渲染过程或事件处理程序中表达逻辑。6. 为什么必须保持组件的纯粹性保持组件的纯粹性Purity并非只是为了追求代码的“优雅”它是 React 实现高性能、跨平台以及复杂交互的底层基石。1. 提高性能跳过不必要的渲染React 利用“记忆化”Memoization技术来优化速度。原理如果组件是纯粹的只要输入Props没变输出JSX就一定不会变。好处React 可以直接复用上一次渲染的结果从而省去重新执行函数和计算 Diff 的时间。2. 随时中断与恢复支持并发模式在现代 React并发模式中渲染过程是可以被高优先级任务如用户输入打断的。场景React 在渲染大型列表时用户突然点击了取消。必要性如果组件是不纯的例如修改了外部变量渲染到一半停下会导致外部数据处于“脏状态”。纯组件不改变外部环境React 可以随时安全地丢弃渲染了一半的结果并重新开始。3. 跨环境运行从浏览器到服务器纯组件不依赖于特定的局部环境如浏览器的window或document对象。服务端渲染 (SSR)纯组件保证了服务器生成的 HTML 与客户端渲染的结果完全一致避免出现“水合不匹配”Hydration Mismatch的错误。可移植性同一套逻辑可以无缝运行在 React Native移动端、Node.js服务器等多种环境。4. 预测性与易于测试可预测性组件行为仅取决于输入。调试时你只需要关注Props而不需要担心当前的系统时间、随机数或全局变量的干扰。测试友好你不需要模拟复杂的全局环境只需传入特定的 Props即可断言输出的 JSX 是否正确。5. 避免渲染顺序导致的 BugReact并不保证组件的渲染顺序例如它可能先渲染底部的组件再渲染顶部的组件。风险如果组件 A 在渲染时修改了全局变量而组件 B 依赖该变量那么渲染顺序的变化会导致 UI 彻底乱套。解决纯组件“独立思考”互不干扰无论 React 以什么顺序执行结果都保持稳定。 核心“超能力”总结能力说明可缓存性输入不变直接复用结果极大提升渲染速度。可中断性渲染过程可以安全地暂停或重启响应更敏捷。可移植性同样的代码在 Server 和 Client 端运行结果完美一致。可维护性逻辑清晰Bug 易于定位不产生“幽灵”副作用。一句话总结保持纯粹是为了让 React 能够完全掌控UI 的更新流程从而为你提供更好的性能保障和更少的意外 Bug。 核心心法总结UI 即公式U I f ( d a t a ) UI f(data)UIf(data)只要数据 (d a t a datadata) 不变输出的U I UIUI就不该变。只读原则将Props、State和Context视为不可变的快照永远不要尝试直接在渲染过程中修改它们。渲染独立性每个组件都应该“独立思考”不依赖其他组件的渲染顺序也不依赖外部环境的随机变动。八、将 UI 视为树 (UI as a Tree)1. 渲染树 (Render Tree)定义表示组件在特定渲染过程中的嵌套模型。特性每个节点代表一个组件。树的顶端是“根组件”Root Component。动态性会随 Props 或 State 的改变条件渲染而变化。用途识别顶级组件优化它们以减少不必要的子树重绘。识别叶子组件优化它们的频繁重渲染性能。2. 模块依赖树 (Dependency Tree)定义表示 JavaScript 模块文件之间的导入import关系。特性节点不仅包括组件文件还包括逻辑、数据如.js,.json,.css。打包工具Bundler依据此树构建生产环境代码。用途调试大型捆绑包Bundle size问题。优化代码分割和延迟加载。3. 关键区别维度渲染树 (Render Tree)依赖树 (Dependency Tree)节点内容组件实例文件/模块关系依据JSX 的嵌套 (Parent-Child)文件头部的import语句包含范围仅限 React 组件所有导入的资源函数、样式、图片4. 快速记忆渲染树是“谁在谁里面运行”UI 逻辑。依赖树是“谁引了谁的文件”工程结构。