
一句话:设计一个 UI 框架,本质是回答五个问题——「怎么描述 UI(元素树)→ 怎么高效更新(协调)→ 怎么组织逻辑(组件/Hooks)→ 怎么调度(优先级/并发)→ 怎么与宿主交互(host config)」——而 React 的每个「奇怪设计」都能从「它选择了哪种取舍」反推出来。
一、先立「第一性原理」
⭐⭐⭐ 三条不可绕开的前提(任何 UI 框架都要面对):
① 【「UI 是状态的函数」】
⭐ `UI = f(state)` —— 「同一份状态 → 同一份 UI」(**声明式**替代「命令式操作 DOM」)
⭐ 这是「一切设计的起点」:它带来「可预测、可测试、可组合」,也带来
「每次状态变都要「重新算一遍 UI」(⭐ 所以「怎么算得便宜」成了核心问题)」。
② 【「DOM 操作是昂贵的」】
⭐ 「重新创建整棵 DOM」不可接受 → 所以要么「找出差异」(diff),
要么「精确知道哪个值变了」(细粒度订阅,见 12.13),
要么「编译期算出来」(编译时优化)。
⭐ 这三条路 = 「React / Signals / Svelte 的分野」。
③ 【【单线程 + 用户感知】】
⭐ 「计算」和「用户输入」抢同一个线程 →
所以「长计算要能被「打断/让路/分片」」→ ⭐ **这就是「并发渲染」的动机**。
二、五个必须回答的问题
问题 1:怎么描述 UI
⭐⭐⭐ 候选方案与取舍:
【A. 模板(`<div>{{x}}</div>`)】
✅ 「接近 HTML」、对编译器友好(⭐ Svelte/Vue 走这条)
⚠️ 「表达能力受限」(要额外语法)、「类型推断难」(⭐ 要靠工具链补救)
【B. 函数调用 / JSX(`jsx('div', {}, x)`)】⭐ React 选这条
✅ **「就是 JS」**——「表达式、组合、泛型、类型」全都直接可用
⚠️ 「需要编译」(JSX → 函数调用);⚠️ 「容易「每次渲染都创建新对象」」
【C. 字符串模板(运行时解析)】
✅ 「无需编译」;⚠️ 「运行时开销 + 类型不安全」→ 现代框架基本淘汰
⭐ React 选 B 的深层理由「与「问题 3(怎么组织逻辑)」连在一起」——
它想让「UI 描述」与「逻辑」用「同一种语言」(JS),
⭐ 因为「UI 里天然混着逻辑」(条件、循环、映射),
而「模板语言必然要「再造一套控制流」」(⭐ 见 `v-if`/`v-for`)。
⭐⭐⭐ 「元素」的设计(⭐ 这是 React 最容易被误解的一层):
元素 = 「一个普通的、不可变的对象描述」(`{ type, props }`)
⭐ 关键决定:
① 【「元素」不是「组件实例」】⭐
「每次渲染都创建新元素对象」→ 所以「比较元素」要靠「diff」(而不是「复用对象」)
⭐ 这是「React 的 diff 存在的原因」,也是「为什么「每次渲染创建新对象」是正常的」
② 【「组件」是「函数」】⭐
函数组件 = 「一个「props → 元素」的纯函数」
✅ 「易测试、易组合、易推理」;⚠️ 「每次渲染都是一次函数调用」(⭐ 所以有 Hooks 的顺序问题)
③ 【「组件树」是「嵌套」的(不是「图」)】
⚠️ 这带来「跨层传递要靠 props/Context」(⭐ 成本)
✅ 换来「可预测的更新顺序」与「简单的协调算法」
问题 2:怎么高效更新
⭐⭐⭐ 三条路线(⭐ 这是「框架分野」的核心):
【路线 A:Virtual DOM + diff】⭐ React 选这条
① 生成新元素树 → ② 与旧树「同层比较」→ ③ 得出「最小变更集」→ ④ 应用
✅ 「心智模型简单、不必关心「哪些值变了」」(⭐ 你只管「写 UI」)
✅ 「可以做「跨平台的抽象」(⭐ 因为「树 + diff」与宿主无关,见问题 5)
⚠️ 「diff 有成本」(要「遍历树」+ 「比较 props」)
⭐ 优化手段:「`key`(帮助「识别同一元素」)、`memo`(剪枝)、
「不可变数据(⭐ 引用比较 O(1) 就能判断「变没变」)」
【路线 B:细粒度订阅(Signals)】⭐ Solid/Vue/Svelte 5
✅ 「不读就不订阅」→ 「无额外 diff」
⚠️ 「需要「响应式值」的概念」+ 「与「快照式渲染」冲突」(⭐ 撕裂,见 12.13)
【路线 C:编译期「静态分析 + 精确更新」】⭐ Svelte
✅ 「编译期知道「哪个表达式对应哪个 DOM 节点」」→ 「运行时几乎零 diff」
⚠️ 「动态性受限」(⭐ 「运行时动态拼装的结构」难以优化)
⭐⭐ React 选 A 的两个「非性能」理由(⭐ 这点最容易被忽略):
① ⭐ **「不必让使用者理解「依赖追踪」」**——你不需要「声明「这个 UI 依赖哪些值」」,
所以「心智模型小」(⭐ 代价是「框架要更努力地优化」)
② ⭐ **「与宿主解耦」**——「树 + diff」可以直接复用到
「Native(RN)/ Canvas / 文档(react-pdf)/ 测试(react-test-renderer)」(⭐ 见 10.15)
⚠️ 「Signals/编译期方案」都「更绑定 Web DOM」
⭐ 「diff 的关键设计」(⭐ 面试常问「为什么「同层比较」+「为什么需要 key」」):
① 【同层比较(不做「跨层移动」的检测)】
⚠️ 因为「判断「一棵子树是否被移动到另一处」是 O(n³)」(⭐ 通用树编辑距离)
✅ React 的取舍是「只看「同一层」」→ 算法降到 O(n)(⭐ 见 4.4)
⭐ 代价:「跨层移动」会「被当成「删除 + 新建」」(⚠️ 丢状态)
② 【`key`:「让「跨渲染的同一元素」被识别出来」】
✅ 「列表重排」时「复用 DOM 与状态」(⭐ 否则「就地更新」导致错位,见 5.5)
③ 【「类型不同 → 整棵重建」】
✅ 因为「`div` 与 `span` 的属性/事件语义完全不同」→ 「重建比「改」更简单可靠」
问题 3:怎么组织逻辑
⭐⭐⭐ 候选方案(⭐ 这条演化史最能说明「取舍」):
【A. 类组件 + 生命周期】⭐ React 早期
✅ 「把「状态 + 逻辑 + 渲染」放在一起」(⭐ 比「模板 + 全局函数」好得多)
⚠️ 「逻辑被「生命周期」切碎」:
「同一个功能」的代码「散落在 componentDidMount/Update/Unmount」→
⭐ 「难以复用、难以理解」
【B. 混入(Mixin)】
⚠️ 「来源不明、命名冲突、隐式依赖」→ 「被抛弃」
【C. HOC / Render Props】
✅ 「能复用逻辑」;⚠️ 「嵌套地狱」+ 「props 来源不明」
【D. Hooks】⭐ 现在
✅ ⭐ **「按「功能」聚合逻辑」(而不是「按生命周期切碎」)**
✅ 「复用是「函数级」的(不必「包一层组件」)」
⚠️ ⭐ **代价:「必须「按顺序无条件调用」」**——
因为「Hooks 的值存在「fiber 上的数组」里,靠「调用顺序的下标」定位」(见 11.11)
⚠️ 「闭包陷阱」(⭐ 每次渲染有独立的闭包,见 2.13)
【E. 「编译期响应式」(Svelte 5 runes / Solid)】
✅ 「不需要「Hooks 顺序」规则」(⭐ 因为「值在编译期被转成 signal」)
⚠️ 「与「纯 JS 语义」有距离」(⭐ 要理解「编译后的东西」)
⭐ React 选 D 的原因:「**保留「纯 JS 的函数语义」**」(⭐ 不引入「编译期魔法」)
并「接受「顺序规则 + 闭包陷阱」这两个代价」。
⭐ 而且「Hooks 可测试」——「自定义 Hook」就是「一个函数」,
可以「单独测」(⭐ 与「组件测试」解耦)。
问题 4:怎么调度
⭐⭐⭐ 这是 React 「最难被替代」的地方(⭐ 也是「为什么 React 还在用 VDOM」的原因之一):
【要解决的真实问题】
· ⭐ 「用户输入必须「立刻响应」」(哪怕「后台正在渲染一个大列表」)
· ⭐ 「不同更新「优先级不同」」(输入 > 点击 > 数据刷新 > 预加载)
· ⭐ 「一次渲染可能「很久」」→ 要能「打断、让路、稍后继续」
【方案 A:同步渲染(最早期)】
⚠️ 「一次渲染必须「一口气做完」」→ 「大列表渲染时「输入卡成 PPT」」
【方案 B:时间切片 + 优先级(Concurrent)】⭐ React 18 走这条
· ⭐ 「把渲染「切成小片」」(每片约 5ms,见 4.17)
· ⭐ 「每片后检查「是否有更高优先级的工作」」(有就「让路」)
· ⭐ 「不同更新进不同「车道(Lane)」」
⚠️ 代价:「渲染可能「被丢弃并重来」」→ ⭐ **「渲染必须是纯函数」**(⭐ 这条「反过来约束了一切」)
【方案 C:细粒度更新「天然不需要调度」】
✅ 「更新一个值只改一个节点」→ 「本来就不会「一次做很久」」
⚠️ 但「首屏渲染」「结构性变化」「数据获取」仍然要「调度」——
⭐ 所以「Signals 方案也要解决「并发/调度」问题」(⭐ 这是两者都在做的部分)
⭐⭐ 「时间切片」的实现前提(⭐ 面试加分点):
· ⭐ 「渲染过程的遍历状态要「能从「递归栈」里搬出来」」
→ 所以「fiber 用「链表(child/sibling/return)」+「全局指针」」
→ ⭐ 「这样才能「随时保存并返回」」(见 4.2/11.11)
· ⭐ 「让出主线程的机制」要「比 setTimeout 更及时」
→ 所以用 **`MessageChannel`**(见 4.17)
⭐ 一句话:「Concurrent 逼出了 Fiber 的「链表结构」与 Scheduler 的「MessageChannel」」。
问题 5:怎么与宿主交互
⭐⭐⭐ 「宿主配置(host config)」这个抽象(⭐ 这是 React 最有远见的设计之一):
把「reconciler(协调器:diff / Fiber / 优先级 / Hooks / Suspense)」
与「具体平台的「渲染操作」」解耦:
· ⭐ **Web**:`createElement` / `appendChild` / `setAttribute`
· ⭐ **Native**:创建原生视图(`UIView` / `android.view.View`)
· ⭐ **Canvas / WebGL**:命令式的绘制调用
· ⭐ **文档**:生成 PDF 元素
· ⭐ **测试**:生成「可断言的树」(⭐ 所以「组件测试可以「不跑真 DOM」」)
⭐ 收益:
① ⭐ **「一次实现,多端复用」**(⭐ RN 的全部基础,见 10.15)
② ⭐ **「可测试性」**(⭐ `react-test-renderer` 就是「一个假的 host config」)
③ ⭐ 「自定义渲染目标」成为可能(`react-three-fiber` 等)
⚠️ 代价:「抽象层「隔掉了「平台的直接控制」」」——
⚠️ 「极端性能场景」下(如「游戏级渲染」)要「绕开 React」
(⭐ 所以「Canvas 里用命令式引擎 + React 只做外壳」是常见做法)
三、把五个问题串起来:React 的设计「为什么会是这样」
⭐⭐⭐⭐ 一条「因果链」(⭐ 这是本题最有价值的输出):
「要让「UI = f(state)」在「大应用」里可用」
→ 必须解决「更新效率」→ 选「VDOM + diff」(⭐ 保住「不必理解依赖追踪」+ 保住「跨平台」)
→ diff 需要「不可变数据」→ 「单向数据流 + props 只读」
→ 需要「逻辑复用」→ Hooks(⭐ 换取「按功能聚合」,代价是「顺序规则」)
→ 需要「大更新不卡输入」→ 时间切片 + 优先级(Concurrent)
→ 「可中断」要求「渲染可丢弃/重放」→ ⭐ **「渲染必须纯」**(这条被反复强调)
→ 「纯渲染」又与「细粒度可变响应式(signals)」冲突 → ⭐ 「不上 signals,改走 `useSyncExternalStore` + 编译器」
→ 「渲染要能被中断」→ 遍历状态要「外化」→ ⭐ **Fiber 链表 + 全局指针**(见 4.2)
→ 「让出主线程」要可靠 → ⭐ **`MessageChannel` + 时间片**(见 4.17)
→ 「多个平台要复用」→ ⭐ **host config 抽象**(见 10.15)
→ 「开发者不该手动管 memo」→ ⭐ **React Compiler**(见 4.15)
⭐ 你会发现:「**React 的每一个「奇怪设计」都是「某个约束的必然结果」**」——
这就是「设计一个框架」与「背 API」的本质区别。
⭐⭐⭐ 「如果重来一次,哪些会不一样」(⭐ 展示「批判性思考」):
① ⭐ **「Hooks 的顺序规则」也许能避免**
如果「值不是「存在 fiber 的数组里」」而是「绑定到「编译期确定的槽位」」(⭐ Compiler 已经在做类似的事)
② ⭐ **「Context 的粒度太粗」**
当前「一变全量重渲染」(见 5.9)→ 也许「选择器式订阅」应该更原生
③ ⭐ **「`useEffect` 被过度使用」**
「数据获取」+「派生计算」都该「不在 Effect 里」(⭐ React 官方文档也在强调这个,见 8.1)
④ ⭐ **「并发渲染的默认开启」来得太晚**
「很多库的开发模式」建立在「同步渲染」上 → 迁移成本高
⑤ ⭐ **「Server Components 的引入方式」有争议**
「'use client' 的边界」对「生态库的作者」是「全新的约束」(见 4.12)
⭐ 这些问题「正是 React 团队现在在「新版本 + Compiler」里解决的」——
⭐ 所以「设计框架」不是「一次做完」,而是「在约束下持续演化」。
四、面试怎么答(⭐ 结构化输出)
⭐⭐⭐ 一个「高分答法」的四段结构:
① 【先立框架(⭐ 30 秒内说清楚「要回答哪几个问题」)】
「设计一个 UI 框架要回答五个问题:
「怎么描述 UI、怎么高效更新、怎么组织逻辑、怎么调度、怎么与宿主交互」。」
② 【逐题给「选项 + 取舍」(⭐ 不要「只讲一种方案」)】
每个问题「至少说两条路线 + 各自的代价」:
· 描述 UI:模板 vs JSX(⭐ React 选 JSX → 「UI 与逻辑同语言」+ 「不必重造控制流」)
· 更新:VDOM+diff vs 细粒度订阅 vs 编译期(⭐ React 选 diff → 「不必理解依赖追踪」+「跨平台」)
· 逻辑:类 vs Hooks(⭐ Hooks 的代价是「顺序规则 + 闭包陷阱」)
· 调度:同步 vs 时间切片 + 优先级(⭐ 这是 React 最难替代的部分)
· 宿主:host config(⭐ 一次实现多端复用 + 可测试)
③ 【串成因果链(⭐ 这是「加分项」)】
⭐ 「因为要「可中断」→ 渲染必须能被丢弃重放 → 渲染必须纯 →
所以不上 signals;因为渲染要能中断 → 遍历状态要外化 → 所以 Fiber 用链表;
因为要让出主线程 → 所以用 MessageChannel」
④ 【给「如果重来会怎么改」(⭐ 展示批判性)】
「Hooks 的顺序规则、Context 粒度、Effect 的过度使用、并发默认开启的时机」
—— ⭐ 并指出「这些正是 React 现在通过 Compiler/新版本在解决的」。
五、本篇特有的坑(表达层面的)
① 「只讲 React 做了什么,不讲「为什么」(⭐ 那是「背 API」不是「设计」)
✅ 每个设计都要能回答「它解决了什么问题、代价是什么」
② 「把「VDOM 更快」当成前提」(⭐ 不准确)
⭐ 「VDOM 本身是「更慢」的」(多一层 diff);
它的价值是「**不必理解依赖追踪 + 跨平台 + 可测试**」——
⚠️ 「性能优化是「与之配套的工程」」(memo/Compiler/走合成)
③ 「忽略「调度」这一层」(⭐ 只讲 diff/Hooks 会显得浅)
⭐ 「Concurrent 是 React 最难被替代的能力」,也是「Fiber 与 Scheduler 存在的原因」
④ 「把「Hooks 的顺序规则」说成「React 的缺陷」」(⭐ 太片面)
⭐ 它是「选择「纯 JS 语义(不引入编译期魔法)」的**代价**」——
而且「Compiler 正在「把「手动 memo」这件事从开发者手上拿走」」
⑤ 「忽略「host config」」(⭐ 这是「RN/react-three-fiber/react-pdf」的全部基础)
⑥ 「不提「撕裂(tearing)」」(⭐ 那是「不上 signals」的技术核心,见 12.13)
⑦ 「把所有「性能问题」都归给「框架」」(⭐ 太简化)
⭐ 「DOM 数量、包体积、网络、长任务」都「与框架无关」(见 5.5/5.14/8.10)
⑧ 「不提「取舍的反面」(⭐ 设计题的关键是「代价」)」
⭐ 「每个「优点」都要说「它的代价」」——⭐ 这才是「设计」的语汇
⑨ 「把「Signals/Svelte」说成「全面更好」」
⭐ 它们的「优势场景」很明确(高频细粒度),但「并发调度/生态/跨平台」上各有取舍
⑩ 「不谈「演化」」(⭐ 框架是「在约束下持续改」的)
⭐ 说清「Compiler/RSC/并发」是「怎么在「不换模型」的前提下继续推进的」
六、面试延伸
- 「如果让你设计一个前端框架,你会先回答哪些问题?」
⭐ 五个:① 怎么描述 UI——模板 vs JSX/函数调用(⭐ React 选后者:「UI 与逻辑同一种语言」,不必「重造控制流」);② 怎么高效更新——VDOM + diff vs 细粒度订阅(Signals)vs 编译期精确更新(⭐ React 选 diff:「不必让使用者理解依赖追踪」+「与宿主解耦」);③ 怎么组织逻辑——类/生命周期 vs Hooks(⭐ Hooks 按「功能」聚合逻辑,代价是「顺序规则 + 闭包陷阱」);④ 怎么调度——同步 vs 时间切片 + 优先级(⭐ 这是 React 最难被替代的部分);⑤ 怎么与宿主交互——⭐ host config 抽象(一次实现多端复用 + 可测试)。⭐ 先立框架再展开取舍,比「直接讲 VDOM」高一个层次。 -
「React 为什么用 VDOM 而不是「直接操作 DOM」或「细粒度订阅」?」
⭐ VDOM 本身不是「更快」的(多一层 diff),它的价值在三处:① ⭐ 「不必让使用者理解依赖追踪」——你只管「写 UI」(UI = f(state)),框架负责「找差异」;而 Signals 「要求你理解「谁订阅了谁」」;② ⭐ 「与宿主解耦」——「树 + diff」可以复用到 Native / Canvas / 文档 / 测试(⭐ RN 的全部基础,见 10.15);③ ⭐ 「可测试性」——react-test-renderer就是「一个假 host config」。代价:多一层「diff 成本」+ 「需要「不可变数据」来让「比较变便宜」(引用比较 O(1))」+ 「需要memo/Compiler/结构优化来「剪枝」」——⭐ 所以 React 的性能故事是「一套配套工程」,而不是「VDOM 本身快」。 -
「为什么 React 的「渲染必须纯」这条被反复强调?」
因为 ⭐ 「并发渲染(可中断)」直接要求它:① 「渲染可以被中断」(让出主线程);② 「中断后可能「被丢弃并「用更高优先级重来」」;③ ⭐ 所以「一次渲染可能被执行 0 次、1 次或多次」。如果渲染有副作用(改外部变量、发请求、读写 DOM)→ 「它会「重复执行」或「产生不一致」」。这条约束「反过来约束了一切」:① 它解释「为什么「不能不上 signals」(signals 是「活的值」,与「快照式渲染」冲突,见 12.13);② 它解释「为什么 StrictMode 要「双调用」(主动暴露不纯的渲染,见 8.5);③ 它解释「为什么useEffect要「在提交后异步执行」(副作用不能发生在渲染期)」。 -
「Concurrent(并发渲染)为什么难实现?」
因为它要同时满足三件事:① ⭐ 「渲染要能「随时暂停」」——所以「遍历的状态不能放在「递归调用栈」里」→ ⭐ Fiber 用「child/sibling/return链表 + 全局workInProgress指针」(见 4.2/11.11);② ⭐ 「让出主线程后要「及时」恢复」——setTimeout有「最小延迟 + 后台节流」、requestIdleCallback「频率受限 + 兼容差」→ ⭐ 所以用MessageChannel+ 约 5ms 的时间片(见 4.17);③ ⭐ 「优先级要能「插队」」——所以要有「车道(Lane)模型」+ 「可丢弃的「工作中的树」」→ ⭐ 而这要求「渲染是纯的」(否则「丢弃」会留下副作用)。三者互相约束——这就是「为什么「并发」是 React 最难被替代、也最难被抄的部分」。 -
「host config 这个抽象的价值是什么?」
⭐ 它把「reconciler(diff/Fiber/优先级/Hooks/Suspense)」与「具体平台的渲染操作」彻底解耦——于是「同一套 React」可以驱动:⭐ Web(DOM)、⭐ Native(iOS/Android 原生视图,见 10.15)、Canvas/WebGL(react-three-fiber)、文档(react-pdf)、⭐ 测试(react-test-renderer → 生成可断言的树)。收益:① ⭐ 「一次实现、多端复用」(RN 的全部基础);② ⭐ 「可测试性」(组件测试可以「不跑真 DOM」);③ 「自定义渲染目标」变得可能。代价:⚠️ 「抽象层隔掉了「对平台的直接控制」」——所以「极端性能场景」(游戏级渲染)要「绕开 React」(⭐ 「Canvas 里用命令式引擎 + React 只做外壳」是常见架构)。 -
「如果 React 重来一次,你会改什么?」
五处(⭐ 每处都要说「为什么」,而不是「我觉得」):① ⭐ Hooks 的顺序规则——如果「Hook 的值绑定到「编译期确定的槽位」」(而不是「运行时数组下标」),也许能「允许条件调用」(⭐ Compiler 已经在做类似的事);② ⭐ Context 的粒度——当前「一变全量重渲染」(见 5.9),也许「选择器式订阅」应该更原生;③ ⭐useEffect的过度使用——「数据获取」与「派生计算」都不该在 Effect 里(⭐ React 官方文档现在也在强调「你可能不需要 Effect」,见 8.1);④ ⭐ 并发渲染的默认开启时机——很多库建立在「同步渲染」的假设上,迁移成本高;⑤ ⭐ Server Components 的引入方式——「'use client'的边界」对「生态库作者」是全新约束(见 4.12)。⭐ 注意:「这些「不该改」也有理由」(⭐ 每个设计都「换来了什么」)——所以「改」要说清「代价与收益的再平衡」。 -
「为什么说「React 的每个奇怪设计都是某个约束的必然结果」?」
⭐ 用一条因果链串:「要让UI = f(state)在大应用里可用」→ 必须解决「更新效率」→ 选「VDOM + diff」(保住「不必理解依赖追踪」+「跨平台」)→ diff 需要「不可变数据」→ 「单向数据流 + props 只读」→ 需要「逻辑复用」→ Hooks(代价:顺序规则)→ 需要「大更新不卡输入」→ 时间切片 + 优先级(Concurrent)→ 「可中断」要求「渲染可丢弃重放」→ ⭐ **「渲染必须纯」 → 「纯渲染」与「可变响应式(signals)」冲突 → ⭐ 不上 signals,改走useSyncExternalStore+ Compiler → 「渲染要能中断」→ 遍历状态要「外化」→ ⭐ Fiber 链表 + 全局指针 → 「让出主线程」要可靠 → ⭐MessageChannel+ 时间片 → 「多平台复用」→ ⭐ host config 抽象」。⭐ 这说明:「设计」不是「发明 API」,而是「在约束下做出一致的选择」**。 -
「这道题考的是什么?」
⭐ 考的是「架构思维」而不是「记忆」:① 能不能从需求出发(「UI 框架要解决什么问题」);② ⭐ 能不能「给选项 + 说代价」(⭐ 只说「React 这样做」是背 API;说「为什么不做另一种、另一种的代价是什么」才是设计);③ ⭐ 能不能「串成因果」(「为什么 Fiber 是链表」「为什么渲染必须纯」「为什么不用 signals」——⭐ 这些是「同一条因果链上的节点」);④ 能不能「批判性思考」(「如果重来会改什么、为什么当初不那么做」);⑤ 能不能「落到工程」(「DOM 数量、包体积、长任务这些「与框架无关」的问题该怎么办」)。⭐ 一句话:「这题答得好不好,不看你「知道多少 API」,而看你「能不能在约束之间做出一致的取舍」」。
一句话速记
设计一个 UI 框架要回答五个问题:① 怎么描述 UI(模板 vs ⭐ JSX/函数调用——React 选 JSX 是为了「UI 与逻辑同一种语言」+「不必重造控制流」);② 怎么高效更新(⭐ VDOM+diff vs 细粒度订阅 vs 编译期精确更新——React 选 diff 的真正理由不是「快」而是「不必让使用者理解依赖追踪」+「与宿主解耦」,代价是「需要不可变数据 + memo/Compiler/结构优化这套配套工程」);③ 怎么组织逻辑(类/生命周期 vs Hooks——⭐ Hooks「按功能聚合」但代价是「顺序规则 + 闭包陷阱」);④ 怎么调度(⭐ 时间切片 + 优先级(Concurrent),这是 React 最难被替代的部分);⑤ 怎么与宿主交互(⭐ host config——「一次实现多端复用(RN/Canvas/PDF/测试)+可测试」,代价是「隔掉平台直接控制」);⭐ 最有价值的输出是「因果链」:
UI=f(state)→ 需要更新效率 → VDOM+diff → 不可变数据 → Hooks → 并发 → 「可中断」要求「渲染可丢弃重放」→ ⭐ **「渲染必须纯」 → 与 signals 冲突 → 不上 signals(改走useSyncExternalStore+ Compiler)→ 渲染要能中断 → Fiber 链表 + 全局指针 → 让出主线程要可靠 →MessageChannel+ 5ms 时间片 → 多平台 → host config」;⭐ 面试的四段结构:「先立「五个问题」的框架 → 每题给「两条路线 + 代价」(⭐ 设计题的关键是「代价」)→ 串成因果链 → 给「如果重来会改什么」(Hooks 顺序规则 / Context 粒度 / Effect 过度使用 / 并发默认开启时机 / RSC 的'use client'边界)」;⚠️ 不要「把 VDOM 说成更快」、不要「只讲 API 不讲取舍」、不要「忽略调度与 host config」——这题考的是「在约束之间做出一致取舍」的能力,不是「记住了多少 API」。



最新评论
读过书不知道欧·亨利的人少。教科书上选文有
这小生活不错呀
不错,必须顶一下!
看着你还在坚持,很好
看来忙了也没时间更新博客了
NIce。学习了。。。。
网站不错!!!!
简洁实用,好文章!