跳到主要内容

组件框架

组件框架把界面拆成有明确输入、状态和生命周期的单元,并协调这些单元的更新。React、Vue 都能让开发者根据数据描述界面,但它们组织状态、发现变化和计算更新的方式不同。

理解框架的价值,要从界面状态怎样保持一致开始。按钮文字、禁用状态、列表和表单可能同时依赖同一份数据;只比较 JSX 与模板的写法,很难解释为什么需要框架,也很难判断它的运行成本。

1. 面试核心​

先记住这几条,再读正文
  • 命令式代码描述具体的 DOM 操作,声明式代码描述状态对应的界面。框架把状态到 DOM 的同步规则集中起来,减少业务代码手工维护多个显示位置的负担。卡 · 命令式与声明式界面
  • React 和 Vue 都根据状态更新界面,但React 的 state 更新请求新的渲染,Vue 3 的响应式读取与写入建立、触发依赖关系。二者最终都需要把变化提交到宿主界面。卡 · React 与 Vue 的更新模型
  • 重新计算组件不等于重建全部 DOM。组件身份、更新范围和最终 DOM 修改需要分开讨论;虚拟 DOM 本身也有分配与比较成本。卡 · 运行成本与选型
  • Vue 的 v-model 与 React 的受控表单都能实现输入与状态同步。表单的双向绑定语法不等于整个应用可以任意双向修改数据。卡 · 表单绑定与数据流
  • 选型要落到业务、已有代码、组件生态、团队熟悉度和运行约束。性能结论应来自相同任务与目标设备下的测量,不能只由框架名称推出。卡 · 综合题

2. 命令式与声明式界面​

命令式界面代码直接说明怎样操作节点,例如修改文字、增加子节点、切换属性。声明式界面代码说明某个状态下应该显示什么,由运行机制把这个描述落实到页面。

假设一个计数器最多增加到 3,页面同时展示计数与按钮可用状态。每次 count 改变,都必须让这两个显示位置一致。原生 DOM 完全可以做到,关键在于由谁维护这份关系。

2.1 原生 DOM​

下面把更新逻辑放进 render(),用闭包保存 count。传入的 host 是由这个计数器独占的空容器:

export function mountNative(host) {
let count = 0;
const text = document.createElement('p');
const button = document.createElement('button');
button.type = 'button';
button.textContent = '增加';
const listeners = new AbortController();
function render() {
text.textContent = `次数:${count}`;
button.disabled = count >= 3;
}
button.addEventListener('click', () => {
count++;
render();
}, { signal: listeners.signal });
host.replaceChildren(text, button);
render();
return () => {
listeners.abort();
host.replaceChildren();
};
}

调用 mountNative(host) 后显示 次数:0。每次点击增加一,到 3 时按钮禁用;清理函数移除监听和节点。这个写法没有重新创建整个子树,更新成本也很直接。

真正增加复杂度的是其他入口:网络返回、重置操作、父组件参数变化都可能修改 count。如果每个入口都各自写一次 DOM 更新,就容易漏掉禁用状态或其他相关显示。把数据变化与渲染集中管理,可以减少遗漏;原生代码也能建立这种约定,只是需要自己维护组件组合、更新调度和生命周期。

2.2 React 组件​

React 用组件返回的元素描述界面。下面适用于 React 18、19,JSX 需要构建工具转换:

import { useState } from 'react';
import { createRoot } from 'react-dom/client';

function Counter() {
const [count, setCount] = useState(0);
return <>
<p>次数:{count}</p>
<button type="button" disabled={count >= 3}
onClick={() => setCount(value => value + 1)}>增加</button>
</>;
}

export function mountReact(host) {
const root = createRoot(host);
root.render(<Counter />);
return () => root.unmount();
}

setCount 请求更新,后续渲染根据新的 count 同时计算文字和 disabled。业务代码不必分别查找这两个 DOM 位置,但仍要把条件写对。组件执行得到的是这一轮的 state 快照,setter 不会改写已经取得的旧值,具体规则见 React 组件。

2.3 Vue 组件​

Vue 3 可以用响应式状态与模板表达同一个关系。下面使用包含模板编译能力的 Vue 构建;工程里的单文件组件通常在构建阶段预编译模板:

import { createApp, ref } from 'vue';

export function mountVue(host) {
const app = createApp({
setup() {
const count = ref(0);
return { count };
},
template: `
<p>次数:{{ count }}</p>
<button type="button" :disabled="count >= 3" @click="count++">
增加
</button>
`,
});
app.mount(host);
return () => app.unmount();
}

模板读取 count 时参与依赖收集;count 改变后,相关更新被调度,文字和按钮状态随之刷新。模板中的 ref 会按 Vue 规则解包,因此这里直接写 count。脚本中读写同一个 ref 则使用 .value,机制见 Vue 响应式。

三个示例都得到相同的交互结果,但实现交互的责任分配不同。原生实现直接维护节点,React 和 Vue 把节点更新交给框架。这个对照只用于理解职责,不构成三种方案的性能基准。

3. React 与 Vue 的更新模型​

比较更新模型,要分别看变化怎样被发现、哪些计算需要重做,以及最终改动哪些 DOM。 “用了虚拟 DOM”只回答了其中一部分,不能替代完整链路。

维度React 18、19 的常见函数组件Vue 3 的常见组件
界面描述JSX 或元素创建函数模板或渲染函数
本地状态useState、useReducer 等ref、reactive 等
变化触发setter 等更新入口请求渲染响应式依赖被写入后触发相关作用
组件执行渲染时调用组件函数,读取该轮快照setup 建立组件状态,渲染作用读取当前响应式值
更新工作计算新的元素树并协调、提交调度组件更新,结合依赖与渲染信息执行 patch
编译辅助JSX 转换;其他优化取决于构建配置模板编译可提取静态内容、标记动态部分
DOM 更新时机更新调度与提交有各自阶段响应式值变化与批量 DOM 刷新有各自时机

React 组件函数再次执行,不表示其 state 从初值重新开始,也不表示所有后代 DOM 都被删除。类型、位置和 key 影响身份与复用,具体见 React 协调。

Vue 的响应式系统可以知道某个渲染作用读取了哪些字段;模板编译器还可以给出动态节点信息。但不能把它简化成“任意一个字段永远只直接更新一个 DOM 节点”。一个字段可能影响多个位置,组件更新仍涉及渲染、子组件和 patch,具体见 Vue 渲染机制。

同样,不应说“React 完全没有编译优化”或“Vue 不需要运行时”。实际构建可以引入编译辅助,Vue 也有运行时依赖追踪与更新工作。讨论性能时必须带上版本、构建方式、组件写法及已启用的优化。

3.1 副作用与 DOM 所有权​

框架管理的 DOM,应通过它的状态与生命周期维护。 在框架之外随意删除、替换同一批节点,会让框架保存的结构与真实页面不一致。

图表、编辑器等库需要直接控制 DOM 时,可以给它一个独占容器:框架负责容器的挂载与卸载,外部库负责容器内部内容,并在卸载时释放资源。读焦点、测量布局等操作也可以使用引用与相应生命周期接口,不需要因此放弃声明式状态管理。

请求、订阅和计时器不会因为写在组件文件里就自动获得正确生命周期。它们应在适合的阶段建立,并在相关条件变化或卸载时清理。渲染计算本身应保持可重复,不能把“重新渲染”当成再次发送业务请求的许可。

4. 表单绑定与数据流​

表单双向同步包括两个方向:状态决定控件显示,用户输入再通过事件更新状态。 Vue 的 v-model 把相应绑定与事件处理组织成简洁语法;React 受控输入也可以通过 value 与事件处理器完成这个过程。

因此,“Vue 双向、React 单向”不足以说明两个框架的区别。比较时应问:状态归谁所有,子组件怎样请求修改,以及变化通过什么路径回到显示层。Vue 的组件 props 同样有单向传递约束,子组件不应随意修改父组件传来的值。

模板绑定与响应式依赖也不是同一件事。依赖追踪让状态变化触发相关计算,输入绑定则还需要 DOM 事件把用户输入写回状态。它们的分工见 Vue 响应式。

5. 运行成本与选型​

框架以运行时和构建规则换取统一的组件与更新模型。 它可能减少业务代码反复实现同步逻辑的成本,但也会引入下载、解析、状态追踪、调度和比较等工作。

对于固定的局部任务,直接操作已知 DOM 节点可以省去框架的抽象成本。上面的原生计数器只需更新文字和一个属性,没有理由先构建完整虚拟树再比较。但真实项目中的原生实现也可能反复重建节点、遗漏清理或强制同步布局,不能从“没有框架”直接推出性能更好。

React 和 Vue 都有减少无用计算或更新的机制;具体收益取决于组件边界、数据依赖与构建配置。Vue 模板编译提供的静态提升、动态标记等信息,可以减少部分运行时工作,见 Vue 渲染机制。这些机制没有消除页面布局、图片解码和网络成本,也不是跨项目的速度排名。

5.1 业务约束与可验证的选择​

选型可以围绕实际项目逐项说明,而不是给所有网站一个统一答案:

约束应当验证的内容
已有代码与团队经验能否逐步接入,迁移会触及哪些页面与测试
交互复杂度状态同步、表单、列表身份和异步任务是否易于维护
组件与工具生态必要组件是否可用,行为、无障碍和扩展边界是否满足需求
首屏与运行环境产物体积、加载策略、渲染方式和目标设备表现
长期维护类型支持、调试方式、依赖升级与问题定位成本

例如一个已有 Vue 组件库的内部表单系统,改用 React 需要把迁移成本与实际收益一起评估;只有一个静态页面上的独立交互,可以先判断是否需要引入完整运行时。两者都应以已知需求为依据,不从个人熟悉程度直接推出技术优劣。

比较性能时使用相同功能、相同数据规模、生产构建和目标设备,分别测量首屏、交互响应、更新耗时与内存。开发模式的检查开销、不同组件库和不同网络资源会污染对比;只测一个按钮的循环更新,也不能证明整个业务系统的表现。

6. 常见问题​

6.1 不用框架就不能写组件吗?​

可以。函数、模块和 Web Components 都能组织可复用界面。框架提供的是一组状态、组合、更新与生命周期约定,是否采用取决于这些约定能否降低当前项目的复杂度。

6.2 JSX 和 Vue 模板都只是 HTML 字符串吗?​

6.3 框架能自动解决请求乱序和内存泄漏吗?​

7 · 面试题卡片​

本页题目与参考回答

7.1 框架开发与原生 DOM 开发的主要区别是什么?​

考察重点:命令式操作、声明式描述,以及状态与多个显示位置的同步。

相关内容:命令式与声明式界面、副作用与 DOM 所有权。

参考回答

原生 DOM 代码可以直接描述节点操作,组件框架主要让业务代码描述状态对应的界面,再统一协调更新。 一个状态同时影响文字、按钮和列表时,框架把它们的显示关系放进组件渲染逻辑,减少各事件入口分别维护节点的负担。

原生实现也可以组织渲染函数和组件,并不缺少完成交互的能力。选择框架是采用它的组合、调度和生命周期约定,同时接受相应运行成本;外部 DOM 库仍需明确所有权与清理边界。

7.2 React 与 Vue 的状态更新过程有什么区别?​

考察重点:状态快照、响应式依赖、组件执行与 DOM 提交。

相关内容:React 与 Vue 的更新模型、React 协调、Vue 响应式。

参考回答

React 的 setter 等入口请求后续渲染,函数组件读取该轮 state 快照;Vue 3 通过响应式读取建立依赖,写入时触发相关更新。 React 重新计算元素描述并协调、提交,Vue 的组件更新结合响应式依赖和模板编译信息执行渲染与 patch。

不能把函数重新执行说成全部 DOM 重建,也不能把 Vue 的依赖追踪说成任何变化都只改一个节点。比较时还应注明版本、模板或渲染函数、构建优化与组件边界。

7.3 框架与原生实现相比,性能上限和实际性能应该怎样判断?​

考察重点:抽象成本、实际更新工作、测量条件与结论范围。

相关内容:运行成本与选型、业务约束与可验证的选择。

参考回答

固定任务下,专门编写的原生操作可以省去框架的部分通用工作,但实际项目是否更快必须测量。 框架仍要执行 JavaScript 并操作宿主界面,其依赖追踪、调度与协调都有成本,也能帮助避免重复更新和混乱的同步逻辑。

比较应固定功能、数据、生产构建与设备,分别看加载、交互、更新和内存。单个微型示例不能推出所有业务的性能排名;更不能只凭“虚拟 DOM”或“响应式”就断言一定更快。

7.4 为什么不能只用“Vue 双向、React 单向”概括数据流?​

考察重点:表单语法、组件状态归属和响应式依赖的不同层次。

相关内容:表单绑定与数据流。

参考回答

这混合了表单同步与组件数据流两个层次。 Vue 的 v-model 组织值绑定与更新事件,React 的受控输入也能实现状态到显示、输入到状态的同步;Vue props 仍遵守单向传递约束。

真正需要说明的是状态由谁维护,谁能通过什么入口修改,以及变化怎样触发界面更新。响应式追踪本身不等于输入事件已经写回了状态。

7.5 新项目选择 React、Vue 或原生方案时,应比较哪些条件?​

题型:综合题

考察重点:业务约束、已有资产、实现成本与可验证的技术判断。

相关内容:React 与 Vue 的更新模型、业务约束与可验证的选择。

参考回答

先确认交互复杂度、已有代码与团队经验,再检查必要生态和目标环境是否满足需求。 需要复杂表单、共享组件和持续迭代时,统一组件模型的收益更明显;简单独立交互则应评估额外运行时是否值得。

性能用相同任务的生产构建与目标设备验证,同时考虑迁移、调试、测试和维护成本。答案应给出项目里的具体约束与证据,而不是把个人偏好包装成某个框架普遍更好。

8. 参考资料​

来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明