2026-06-08 vue和react的内部运行机制对比(deepseek)
下面我把 Vue 2/3 和 React 16+ 的内部运行机制完整、系统地并列列出,然后在最后给出核心区别对比表。
一、Vue 内部运行机制(完整版)
1. 整体流程(一句话)
响应式数据 + 依赖收集 + 虚拟DOM + 异步更新队列
2. 核心模块
2.1 响应式系统(数据劫持)
Vue 2(Object.defineProperty)
// 递归遍历 data,为每个属性添加 getter/setter
function observe(obj) {
Object.keys(obj).forEach(key => {
let val = obj[key];
let dep = new Dep(); // 依赖收集器
Object.defineProperty(obj, key, {
get() {
if (Dep.target) dep.depend(); // 收集依赖
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 派发更新
}
});
if (typeof val === 'object') observe(val); // 递归
});
}
Vue 3(Proxy)
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key); // 依赖收集
return Reflect.get(target, key);
},
set(target, key, value) {
const result = Reflect.set(target, key, value);
trigger(target, key); // 派发更新
return result;
}
});
}
Proxy 相比 defineProperty 的优势:
- ✅ 可检测属性新增/删除
- ✅ 可检测数组索引/长度变化
- ✅ 无需递归遍历(懒代理)
2.2 依赖收集(Dep + Watcher)
class Dep {
constructor() {
this.subscribers = new Set(); // 存储 Watcher
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect);
}
}
notify() {
this.subscribers.forEach(watcher => watcher.update());
}
}
class Watcher {
constructor(getter, callback) {
this.getter = getter;
this.callback = callback;
this.value = this.get();
}
get() {
activeEffect = this;
const value = this.getter(); // 触发 get,收集此 Watcher
activeEffect = null;
return value;
}
update() {
queueWatcher(this); // 进入异步队列
}
run() {
const newVal = this.get();
const oldVal = this.value;
this.value = newVal;
this.callback(newVal, oldVal);
}
}
三种 Watcher:
| 类型 | 作用 |
|---|---|
| 渲染 Watcher | 每个组件一个,负责更新视图 |
| 计算属性 Watcher | 每个 computed 一个,懒执行 + 缓存 |
| 侦听器 Watcher | 每个 watch 一个,数据变化时执行回调 |
2.3 异步更新队列(nextTick)
const queue = []; // 存储待执行的 Watcher
let waiting = false; // 防抖标志
function queueWatcher(watcher) {
if (!queue.includes(watcher)) {
queue.push(watcher);
if (!waiting) {
waiting = true;
nextTick(flushSchedulerQueue); // 异步执行
}
}
}
function flushSchedulerQueue() {
queue.forEach(watcher => watcher.run());
queue.length = 0;
waiting = false;
}
// nextTick 实现(优先微任务)
const nextTick = (() => {
const callbacks = [];
let pending = false;
function flushCallbacks() {
callbacks.forEach(fn => fn());
callbacks.length = 0;
pending = false;
}
// 优先级:Promise → MutationObserver → setImmediate → setTimeout
const timerFunc = () => {
Promise.resolve().then(flushCallbacks);
};
return function(cb) {
callbacks.push(cb);
if (!pending) {
pending = true;
timerFunc();
}
};
})();
2.4 模板编译
模板(template)
↓
parse(解析器)→ AST(抽象语法树)
↓
transform(转换器)→ 优化AST(标记静态节点)
↓
generate(生成器)→ render 函数字符串
↓
render 函数
编译示例:
<!-- 模板 -->
<div class="box">{{ name }}</div>
// 生成的 render 函数
function render() {
with(this) {
return _c('div', { class: 'box' }, [_v(_s(name))])
}
}
// _c: createElement, _v: createTextVNode, _s: toString
2.5 虚拟DOM 和 diff 算法
虚拟DOM 结构:
{
tag: 'div',
data: { class: 'box' },
children: [{ tag: undefined, text: 'Hello' }],
elm: 真实DOM引用,
key: undefined
}
Vue 的 diff 算法特点(双端对比):
function patch(oldVNode, newVNode) {
// 1. 同层比较,不跨级
// 2. 双端对比:oldStart、oldEnd、newStart、newEnd 四个指针
// 3. 四种比较策略
if (sameVNode(oldStart, newStart)) {
patchVNode(oldStart, newStart);
oldStart++; newStart++;
}
else if (sameVNode(oldEnd, newEnd)) {
patchVNode(oldEnd, newEnd);
oldEnd--; newEnd--;
}
else if (sameVNode(oldStart, newEnd)) {
patchVNode(oldStart, newEnd);
// 移动节点...
}
else if (sameVNode(oldEnd, newStart)) {
patchVNode(oldEnd, newStart);
// 移动节点...
}
else {
// 使用 key 查找可复用节点
}
}
Vue 3 的编译优化:
- 静态提升:静态节点提升到 render 函数外,只创建一次
- Block 树:动态节点被收集到
dynamicChildren数组中 - PatchFlag:标记节点哪些属性是动态的(
TEXT、CLASS、STYLE等)
// Vue 3 的 PatchFlag 示例
createElementVNode("div", { class: _normalizeClass(_ctx.active ? 'on' : 'off') }, null, 2 /* CLASS */)
// PatchFlag = 2 表示只有 class 是动态的
2.6 完整流程图
┌─────────────────────────────────────────────────────────────┐
│ 你修改数据 │
│ this.name = 'new' │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ defineProperty/Proxy set 触发 │
│ dep.notify() │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Watcher.update() │
│ queueWatcher(this) │
│ 进入异步队列 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ nextTick(flushQueue) │
│ 同一事件循环中的多个更新合并为一个 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 组件执行 render 函数(生成新虚拟DOM) │
│ Vue 3:静态提升 + PatchFlag + Block树 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ diff 算法(同级比较,双端对比) │
│ 找出需要更新的最小范围 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 执行 patch,更新真实DOM(批量/异步) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 页面更新完成 │
└─────────────────────────────────────────────────────────────┘
二、React 内部运行机制(完整版)
1. 整体流程(一句话)
JSX → React Element → 虚拟DOM → Fiber 架构 → 可中断的协调(Render阶段)→ 同步提交(Commit阶段)→ 真实DOM
2. 核心模块
2.1 JSX 编译
// 你写的 JSX
<div className="box" onClick={handleClick}>
{name}
<span>text</span>
</div>
// Babel 编译后
React.createElement(
'div',
{ className: 'box', onClick: handleClick },
name,
React.createElement('span', {}, 'text')
)
// 最终得到 React Element(虚拟DOM)
{
$$typeof: Symbol(react.element),
type: 'div',
key: null,
ref: null,
props: {
className: 'box',
onClick: handleClick,
children: [
name, // 可能是字符串或变量
{ $$typeof: Symbol, type: 'span', props: { children: 'text' } }
]
}
}
2.2 Fiber 架构
为什么需要 Fiber?
- 旧版(React 15)递归 diff,无法中断
- 大组件树渲染会阻塞浏览器(超过 16ms 就会掉帧)
Fiber 节点结构:
{
// 标识
tag: 5, // 节点类型(FunctionComponent = 0, ClassComponent = 1, HostComponent = 5...)
key: null,
type: 'div', // 组件类型
// 树结构(链表)
return: parentFiber, // 父节点
child: firstChildFiber, // 第一个子节点
sibling: nextSiblingFiber, // 兄弟节点
// 双缓冲
alternate: null, // 指向 workInProgress 树中的对应节点
// 状态
memoizedState: null, // 上次渲染的 state(Hooks 链表头)
memoizedProps: null, // 上次渲染的 props
// 副作用
flags: 0, // 需要执行的操作(Placement = 2, Update = 4, Deletion = 8...)
subtreeFlags: 0, // 子树是否有副作用
// 优先级(React 18 Lane 模型)
lanes: 0, // 当前节点的优先级
childLanes: 0, // 子树优先级
}
2.3 调度器(Scheduler)
核心目标:将任务分级,高优先级任务打断低优先级任务。
// 优先级级别(React 18)
const ImmediatePriority = 1; // 立即执行(用户输入、点击)
const UserBlockingPriority = 2; // 用户阻塞(hover、滚动)
const NormalPriority = 3; // 普通(网络请求、数据更新)
const LowPriority = 4; // 低(分析上报)
const IdlePriority = 5; // 空闲(预加载)
// 调度器工作流程
while (workInProgress !== null && shouldYield()) {
// 执行一个工作单元
performUnitOfWork(workInProgress);
}
function shouldYield() {
// 检查当前帧是否还有剩余时间(默认 5ms)
return getCurrentTime() >= frameDeadline;
}
2.4 Render 阶段(可中断/恢复)
Render 阶段 = 构建 Fiber 树 + 标记副作用
从 rootFiber 开始
↓
beginWork(向下遍历)
- 根据 Fiber.tag 执行不同逻辑
- 对于 ClassComponent:实例化/调用 render
- 对于 FunctionComponent:执行函数(调用 Hooks)
- 对于 HostComponent:创建/更新 DOM 节点
- 返回 child(子节点)
↓
completeWork(向上回溯)
- 收集副作用(flags)
- 对 HostComponent:创建 DOM 节点(首次)或更新属性
- 冒泡 flags 到父节点
↓
完成整个树的遍历
beginWork 核心逻辑(简化):
function beginWork(current, workInProgress, renderLanes) {
switch (workInProgress.tag) {
case FunctionComponent:
// 执行函数组件
return updateFunctionComponent(current, workInProgress);
case ClassComponent:
// 实例化类组件,调用 render
return updateClassComponent(current, workInProgress);
case HostComponent:
// 原生 DOM 元素
return updateHostComponent(current, workInProgress);
case HostText:
// 文本节点
return null;
// ...
}
}
协调(Reconciliation)核心:
function reconcileChildren(current, workInProgress, nextChildren) {
// 对比 current.child 和 nextChildren,产生新的 workInProgress.child
if (current === null) {
// 首次渲染:创建所有子节点
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren);
} else {
// 更新:diff 算法
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren);
}
}
2.5 Commit 阶段(不可中断/同步执行)
Commit 阶段 = 执行 DOM 操作 + 调用生命周期
Commit 阶段(三部分)
↓
1. before mutation(DOM 修改前)
- 调用 getSnapshotBeforeUpdate
- 调度 useEffect 的清除函数
↓
2. mutation(DOM 修改)
- 执行 DOM 插入、删除、更新
- 调用 useLayoutEffect 的清除函数
- 调用 componentWillUnmount
↓
3. layout(DOM 修改后)
- 调用 useLayoutEffect 回调
- 调用 componentDidMount / componentDidUpdate
- 调度 useEffect(异步)
2.6 Hooks 原理
Hooks 的核心:每个 Fiber 节点维护一个 Hooks 链表。
// Fiber 节点中的 Hooks 链表
{
memoizedState: {
memoizedState: 'value1', // useState 的值
next: {
memoizedState: 'value2',
next: {
memoizedState: () => {}, // useEffect 的回调
next: null
}
}
}
}
useState 简化实现:
let currentlyRenderingFiber = null;
let workInProgressHook = null;
function useState(initialState) {
// 获取当前 Hook(首次创建 or 复用)
const hook = mountWorkInProgressHook();
if (isFirstRender) {
hook.memoizedState = initialState;
}
const setState = (action) => {
const newState = typeof action === 'function'
? action(hook.memoizedState)
: action;
hook.memoizedState = newState;
// 触发更新:调度一次新的渲染
scheduleUpdateOnCurrentFiber();
};
return [hook.memoizedState, setState];
}
2.7 完整流程图
┌─────────────────────────────────────────────────────────────┐
│ setState / useState 触发更新 │
│ 创建更新对象,加入更新队列 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 调度器(Scheduler)确定优先级 │
│ 高优先级任务(用户输入)→ 立即执行 │
│ 低优先级任务(数据同步)→ 可被打断 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Render 阶段(可中断/恢复) │
│ 从 rootFiber 向下深度优先遍历,执行 beginWork/completeWork │
│ - 对比新旧 props/state(diff) │
│ - 标记需要更新的 Fiber 节点(flags) │
│ - 生成 workInProgress 树 │
│ - 可以被高优先级任务打断 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Commit 阶段(不可中断,同步执行) │
│ 遍历 effect list,执行实际 DOM 操作 │
│ - 插入新节点(Placement) │
│ - 删除旧节点(Deletion) │
│ - 更新属性和文本(Update) │
│ - 调用生命周期 / Hooks │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 浏览器渲染更新 │
│ 用户看到新界面 │
└─────────────────────────────────────────────────────────────┘
三、Vue vs React 核心区别对比表
3.1 核心理念对比
| 维度 | Vue | React |
|---|---|---|
| 核心哲学 | 渐进式、模板优先、响应式自动追踪 | 组件化、JSX 优先、状态驱动 UI |
| 学习曲线 | 平缓(模板语法接近 HTML) | 陡峭(JSX、Hooks、Fiber 概念) |
| 数据流 | 双向绑定(语法糖 v-model) + 单向数据流 | 严格单向数据流 |
| 状态管理 | 响应式变量自动追踪依赖 | setState 触发更新,需手动优化 |
3.2 响应式机制对比
| 维度 | Vue 2 | Vue 3 | React |
|---|---|---|---|
| 实现方式 | Object.defineProperty |
Proxy |
setState + 调度器 |
| 依赖追踪 | 自动追踪(编译时标记) | 自动追踪(Proxy 拦截) | 手动优化(memo、useMemo) |
| 更新触发 | 直接修改数据触发 | 直接修改数据触发 | 调用 setState / dispatch |
| 粒度 | 组件级自动优化 | 组件级自动优化 | 组件级(需手动跳过子组件) |
| 新增属性检测 | ❌ 需要 $set |
✅ 自动 | N/A |
3.3 虚拟DOM 和 diff 对比
| 维度 | Vue | React |
|---|---|---|
| diff 策略 | 双端对比(更复杂) | 单端对比 + key 匹配 |
| 优化手段 | Vue 3 静态提升 + PatchFlag | 需手动 React.memo、useMemo |
| 组件跳过更新 | 自动(响应式属性未变时) | 需要 React.memo 或 PureComponent |
| 列表 key | 必须,用于 diff 优化 | 必须,用于 diff 优化 |
| 更新范围 | 依赖追踪自动确定 | 默认从触发组件向下全遍历 |
3.4 组件和状态对比
| 维度 | Vue | React |
|---|---|---|
| 组件定义 | .vue 单文件组件(template + script + style) |
JSX(JavaScript + XML) |
| 局部状态 | data() 返回对象 |
useState / this.state |
| 计算属性 | ✅ computed(缓存) |
需 useMemo 手动实现 |
| 侦听器 | ✅ watch |
需 useEffect 手动实现 |
| 副作用 | watch、生命周期 |
useEffect、useLayoutEffect |
| 跨组件通信 | provide/inject、Vuex、Pinia |
Context、Redux、Zustand |
3.5 性能对比
| 维度 | Vue | React |
|---|---|---|
| 首次渲染 | 较快(编译优化) | 一般(运行时生成) |
| 更新性能 | 自动优化,依赖追踪 | 需手动 memo,否则可能过度渲染 |
| 内存占用 | 较低(依赖追踪粒度细) | 较高(Fiber 节点 + Hooks 链表) |
| 大列表优化 | 虚拟滚动(第三方库) | 虚拟滚动(react-window) |
| 生产包大小 | ~30KB(Vue 3) | ~40KB(React + ReactDOM) |
3.6 调度和更新对比
| 维度 | Vue | React |
|---|---|---|
| 更新队列 | 异步微任务(nextTick) | 批量更新 + 优先级调度 |
| 可中断渲染 | ❌(同步) | ✅(Fiber 架构,React 18+) |
| 并发模式 | ❌ | ✅(React 18 Concurrent Mode) |
| 优先级 | ❌(但可手动 $nextTick) | ✅(Lane 模型) |
| Suspense | ✅(Vue 3 支持) | ✅(React 18 全面支持) |
3.7 开发体验对比
| 维度 | Vue | React |
|---|---|---|
| 调试工具 | Vue Devtools(优秀) | React Devtools(优秀) |
| TypeScript | ✅ 支持好 | ✅ 支持最好 |
| 移动端 | Weex、uni-app | React Native |
| SEO | Nuxt.js(SSR) | Next.js(SSR) |
| 生态成熟度 | 好(Vue 3 生态系统逐步完善) | 极好(最成熟) |
3.8 代码风格对比(同一个功能)
<!-- Vue 3 -->
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
<p>{{ doubleCount }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
</script>
// React 18
import React, { useState, useMemo } from 'react'
function Counter() {
const [count, setCount] = useState(0)
const doubleCount = useMemo(() => count * 2, [count])
const increment = () => {
setCount(c => c + 1)
}
return (
<div>
<p>{count}</p>
<button onClick={increment}>+1</button>
<p>{doubleCount}</p>
</div>
)
}
四、总结:一句话区别
Vue 是「响应式自动追踪」驱动的,你改数据,它自己知道该更新哪;
React 是「状态变化触发重新计算」驱动的,你调用 setState,它从头 diff 整个组件树,由你告诉它哪里需要优化。

浙公网安备 33010602011771号