文章目录
- 一、你的第一个组件
-
- [1. 什么是组件?(核心定义)](#1. 什么是组件?(核心定义))
- [2. 如何定义一个组件?(三个步骤)](#2. 如何定义一个组件?(三个步骤))
- [3. 组件的组织逻辑(父与子)](#3. 组件的组织逻辑(父与子))
- [4. 核心思想:万物皆组件](#4. 核心思想:万物皆组件)
- [二、 组件的导入与导出](#二、 组件的导入与导出)
-
-
- [1. 为什么要拆分组件?](#1. 为什么要拆分组件?)
- [2. 导出与导入的两种方式](#2. 导出与导入的两种方式)
-
- [三、使用 JSX 书写标签语言](#三、使用 JSX 书写标签语言)
-
- [1. 为什么要使用 JSX?](#1. 为什么要使用 JSX?)
- [2. JSX 的核心语法规则](#2. JSX 的核心语法规则)
-
- [① 只能有一个根元素](#① 只能有一个根元素)
- [② 标签必须闭合](#② 标签必须闭合)
- [③ JS 表达式要写在花括号 {} 中](#③ JS 表达式要写在花括号 {} 中)
- [④ 属性名使用小驼峰命名 (camelCase)](#④ 属性名使用小驼峰命名 (camelCase))
- [⑤ 内联样式必须是对象](#⑤ 内联样式必须是对象)
- [四、 将 Props 传递给组件](#四、 将 Props 传递给组件)
-
- [1. 什么是 Props?](#1. 什么是 Props?)
- [2. 如何使用 Props(两步走)](#2. 如何使用 Props(两步走))
- [3. 高级传递技巧](#3. 高级传递技巧)
- [4. 特殊属性:children](#4. 特殊属性:children)
- [5. Props 的不可变性 (Immutability)](#5. Props 的不可变性 (Immutability))
- 五、条件渲染
-
- [1. 三种常用的逻辑控制方式](#1. 三种常用的逻辑控制方式)
- [2. 详细语法说明](#2. 详细语法说明)
-
- [A. 条件返回 `null`](#A. 条件返回
null) - [B. 三元运算符 `? :` (最常用)](#B. 三元运算符
? :(最常用)) - [C. 逻辑与 && (短路运算符)](#C. 逻辑与 && (短路运算符))
- [D. 使用变量赋值](#D. 使用变量赋值)
- [A. 条件返回 `null`](#A. 条件返回
- [六、 列表渲染 (Rendering Lists)](#六、 列表渲染 (Rendering Lists))
-
- [1. 核心方法:`map()` 与 `filter()`](#1. 核心方法:
map()与filter()) - [2. 为什么需要 `key`?](#2. 为什么需要
key?) - [3. `key` 的使用规则](#3.
key的使用规则) - [4. 常见陷阱与注意事项](#4. 常见陷阱与注意事项)
- [1. 核心方法:`map()` 与 `filter()`](#1. 核心方法:
- 七、保持组件纯粹 (Keeping Components Pure)
-
- [1. 什么是纯组件 (Pure Components)?](#1. 什么是纯组件 (Pure Components)?)
- [2. 严禁突变 (Mutation):常见的错误](#2. 严禁突变 (Mutation):常见的错误)
-
- [❌ 错误示例:修改外部变量](#❌ 错误示例:修改外部变量)
- [✅ 正确示例:通过 Props 传递](#✅ 正确示例:通过 Props 传递)
- [4. 严格模式 (Strict Mode) 的作用](#4. 严格模式 (Strict Mode) 的作用)
- [5. 副作用 (Side Effects) 的安放地](#5. 副作用 (Side Effects) 的安放地)
- [6. 为什么必须保持组件的纯粹性?](#6. 为什么必须保持组件的纯粹性?)
-
- [1. 提高性能:跳过不必要的渲染](#1. 提高性能:跳过不必要的渲染)
- [2. 随时中断与恢复:支持并发模式](#2. 随时中断与恢复:支持并发模式)
- [3. 跨环境运行:从浏览器到服务器](#3. 跨环境运行:从浏览器到服务器)
- [4. 预测性与易于测试](#4. 预测性与易于测试)
- [5. 避免渲染顺序导致的 Bug](#5. 避免渲染顺序导致的 Bug)
- [💡 核心"超能力"总结](#💡 核心“超能力”总结)
- [💡 核心心法总结](#💡 核心心法总结)
- [八、将 UI 视为树 (UI as a Tree)](#八、将 UI 视为树 (UI as a Tree))
-
- [1. 渲染树 (Render Tree)](#1. 渲染树 (Render Tree))
- [2. 模块依赖树 (Dependency Tree)](#2. 模块依赖树 (Dependency Tree))
- [3. 关键区别](#3. 关键区别)
- [4. 快速记忆](#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 函数:
javascript
// 不使用 JSX
const element = React.createElement('h1', {className: 'title'}, 'Hello');
// 使用 JSX (直观、易读)
const element = <h1 className="title">Hello</h1>;
2. JSX 的核心语法规则
① 只能有一个根元素
JSX 表达式必须被包裹在一个闭合标签内。如果你不想增加额外的 DOM 层级,可以使用 Fragment (<>...</>)。
javascript
// ❌ 错误:不能并列两个根标签
return (
<div>A</div>
<div>B</div>
);
// ✅ 正确
return (
<>
<div>A</div>
<div>B</div>
</>
);
② 标签必须闭合
在 HTML 中某些标签可以不写结束符号(如 <img>),但在 JSX 中,所有标签必须闭合,或者使用自闭合。
javascript
<img src="logo.png" /> // ✅ 必须有斜杠闭合
<br />
③ JS 表达式要写在花括号 {} 中
如果你想在 UI 中引用变量、执行运算或调用函数,必须将其包裹在 {} 里。
javascript
const name = "Gemini";
<h1>Hello, {name}</h1>; // 输出: Hello, Gemini
<p>1 + 1 = {1 + 1}</p>; // 输出: 1 + 1 = 2
④ 属性名使用小驼峰命名 (camelCase)
JSX 本质上更接近 JavaScript 而非 HTML,因此属性名要遵循 JS 的变量命名规则。
-
class 变为 className(因为 class 是 JS 的保留关键字)。
-
onclick 变为 onClick。
-
tabindex 变为 tabIndex。
⑤ 内联样式必须是对象
在 HTML 中样式是字符串,但在 JSX 中必须是一个对象,且 CSS 属性名也要用小驼峰。
javascript
// 注意:外层花括号表示进入 JS 环境,内层表示对象
<div style={{ color: 'red', fontSize: '20px' }}>
红色的文字
</div>
四、 将 Props 传递给组件
1. 什么是 Props?
-
定义:Props 是你传递给 JSX 标签的信息,类似于 HTML 的属性(如 src, alt)。
-
用途:父组件通过 Props 将信息(对象、数组、函数等)传递给子组件。
-
特性:只读(不可变)。Props 就像一张时间快照,反映了组件在特定瞬间的数据状态。
2. 如何使用 Props(两步走)
向子组件传递:在父组件中像写 HTML 属性一样写 Props。
javascript
<Avatar person={{ name: 'Lin' }} size={100} />
在子组件中读取:通过解构赋值语法直接获取变量。
javascript
function Avatar({ person, size }) {
// 直接使用 person 和 size
}
- ⚠️ 陷阱:解构时不要忘记花括号 { },否则参数会变成整个 props 对象。
3. 高级传递技巧
- 默认值:如果父组件没传某个值,可以设置备选默认值。
javascript
function Avatar({ size = 100 }) { ... } // 仅在 size 缺失或为 undefined 时生效
- 展开语法 (...):当需要将所有属性原封不动转发给下层时使用。
- ⚠️ 建议:不要过度使用,清晰的逐个传递通常更有利于代码维护。
javascript
<Avatar {...props} />
4. 特殊属性:children
-
定义:当你嵌套 JSX 内容时(如 ),嵌套的内容会被父组件在 children 属性中接收。
-
比喻:父组件像是一个带有"洞"的容器,children 就是填入这个洞的内容。这种模式常用于布局组件(如面板、网格)。
javascript
import Avatar from "./components/Avatar";
function Card(props: { hh: string; children: React.ReactNode }) {
console.log("====================================");
console.log("Card props", props);
console.log("====================================");
return <div className="card">{props.children}</div>;
}
function App() {
return (
<>
<Card hh="pk">
<Avatar
size={100}
person={{
name: "Katsuko Saruhashi",
imageId: "YfeOqp2",
}}
/>
</Card>
</>
);
}
export default App;
5. Props 的不可变性 (Immutability)
-
核心规则:永远不要尝试修改 Props。
-
如何更新:如果需要响应用户输入或改变数据,组件必须 "请求"其父组件传递新的 Props 对象。
-
内存管理:旧的 Props 会被丢弃,由 JavaScript 引擎自动回收内存。
| 场景 | 语法 / 操作示例 | 说明 |
|---|---|---|
| 传递数据 | <Component name="value" /> |
在父组件中像 HTML 属性一样传递数据。 |
| 读取数据 | function Component({ name }) { ... } |
在子组件参数中使用 解构赋值 直接读取属性。 |
| 缺失值处理 | function Component({ name = "默认值" }) |
为可选属性设置默认值,仅在未传值或值为 undefined 时生效。 |
| 包装嵌套 UI | function 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 中产生任何节点。
javascript
if (isPacked) {
return null;
}
return <li className="item">{name}</li>;
B. 三元运算符 ? : (最常用)
适合在 HTML 结构内部做局部的内容切换。
javascript
// 如果已打包显示删除线和对勾,否则只显示名称
return (
<li>
{isPacked ? (
<del>{name + ' ✅'}</del>
) : (
name
)}
</li>
);
C. 逻辑与 && (短路运算符)
适合"有则显示,无则隐藏"的简单开关场景。
javascript
// 只有当 isPacked 为 true 时,才渲染对勾
return (
<li>
{name} {isPacked && '✅'}
</li>
);
⚠️ 避坑指南:不要把数字放在 && 左侧!
-
❌
{count && <p>消息</p>}:如果 count 是 0,React 会在页面上渲染出数字 0。 -
✅
{count > 0 && <p>消息</p>}:确保左侧是一个明确的布尔值。
D. 使用变量赋值
当判断逻辑非常复杂,导致 JSX 嵌套严重时,这是最清晰、最易维护的方法。
javascript
let content = name;
if (isPacked) {
content = <del>{name + " ✅"}</del>;
}
return (
<li className="item">
{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. 为什么需要 key?
key 是列表渲染中最重要的属性。它不是传给组件的普通 Prop,而是 React 内部专用的**"身份证"**。
- 唯一标识 :
key帮助 React 建立数据与组件之间的一一对应关系。 - 性能优化 :当列表发生排序、插入或删除时,React 通过
key快速识别哪些元素是移动的、哪些是新出的,从而避免暴力重新渲染整个列表,极大提升效率。 - 状态保持 :防止出现 Bug。如果没有稳定的
key,React 可能会错误地关联组件状态(例如:删除第一行文字后,第二行的输入框内容却消失了)。
3. key 的使用规则
| 规则 | 详细说明 |
|---|---|
| 兄弟间唯一 | 在同一个 map() 产生的数组中,每个 key 必须是唯一的,不要求全局唯一。 |
| 稳定性 | key 在组件生命周期内必须保持不变。绝对不要 使用 Math.random() 或时间戳。 |
| 来源 | 优先使用数据库主键(ID)、UUID 或数据中具备唯一特性的字段。 |
| 位置 | key 必须直接写在 map() 循环返回的最外层 JSX 标签上。 |
4. 常见陷阱与注意事项
- 慎用索引 (Index) :不要默认使用数组下标作为
key。如果列表涉及重新排序、插入或删除,使用索引会导致组件状态错位。 - 禁止动态生成 :在
render过程中实时生成的key会导致组件在每次更新时都"彻底销毁并重建",造成严重的性能问题且会丢失 DOM 状态。 - Fragment 嵌套 :
- 如果你希望
map返回多个并列节点(例如一对dt和dd),不能使用简写<> </>。 - 必须使用显式的
<Fragment key={...}>,因为简写形式不支持携带任何属性。
- 如果你希望
javascript
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
七、保持组件纯粹 (Keeping Components Pure)
1. 什么是纯组件 (Pure Components)?
React 的设计哲学假设你编写的每个组件都是一个纯函数。一个纯组件必须满足以下两个特征:
- 只负责自己的任务 :它不应该更改在调用前就已存在的任何对象或变量。
- 类比 :就像数学公式 y = 2 x y = 2x y=2x,计算 y y y 的过程不应该偷偷改掉外部 x x x 的值。
- 输入相同,输出相同 :只要
props、state和context一致,返回的 JSX 必须永远一致。
2. 严禁突变 (Mutation):常见的错误
组件"不纯"最常见的原因是在渲染过程中修改了外部变量。
❌ 错误示例:修改外部变量
javascript
let guest = 0;
function Cup() {
// 🔴 错误:在渲染期间更改了函数外部声明的变量
guest = guest + 1;
return <h2>Guest #{guest}</h2>;
}
✅ 正确示例:通过 Props 传递
javascript
function Cup({ guest }) {
// 🟢 正确:组件输出完全取决于传入的参数
return <h2>Guest #{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. 避免渲染顺序导致的 Bug
React 并不保证组件的渲染顺序(例如它可能先渲染底部的组件,再渲染顶部的组件)。
- 风险:如果组件 A 在渲染时修改了全局变量,而组件 B 依赖该变量,那么渲染顺序的变化会导致 UI 彻底乱套。
- 解决:纯组件"独立思考",互不干扰,无论 React 以什么顺序执行,结果都保持稳定。
💡 核心"超能力"总结
| 能力 | 说明 |
|---|---|
| 可缓存性 | 输入不变,直接复用结果,极大提升渲染速度。 |
| 可中断性 | 渲染过程可以安全地暂停或重启,响应更敏捷。 |
| 可移植性 | 同样的代码在 Server 和 Client 端运行结果完美一致。 |
| 可维护性 | 逻辑清晰,Bug 易于定位,不产生"幽灵"副作用。 |
一句话总结 :
保持纯粹是为了让 React 能够完全掌控 UI 的更新流程,从而为你提供更好的性能保障和更少的意外 Bug。
💡 核心心法总结
- UI 即公式 : U I = f ( d a t a ) UI = f(data) UI=f(data) 只要数据 ( d a t a data data) 不变,输出的 U I UI UI 就不该变。
- 只读原则 :将 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 逻辑)。
依赖树是"谁引了谁的文件"(工程结构)。