你瞅啥呢

2026-06-08 vue和react的内部运行机制对比(deepseek)

下面我把 Vue 2/3React 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:标记节点哪些属性是动态的(TEXTCLASSSTYLE 等)
// 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 拦截) 手动优化(memouseMemo
更新触发 直接修改数据触发 直接修改数据触发 调用 setState / dispatch
粒度 组件级自动优化 组件级自动优化 组件级(需手动跳过子组件)
新增属性检测 ❌ 需要 $set ✅ 自动 N/A

3.3 虚拟DOM 和 diff 对比

维度 Vue React
diff 策略 双端对比(更复杂) 单端对比 + key 匹配
优化手段 Vue 3 静态提升 + PatchFlag 需手动 React.memouseMemo
组件跳过更新 自动(响应式属性未变时) 需要 React.memoPureComponent
列表 key 必须,用于 diff 优化 必须,用于 diff 优化
更新范围 依赖追踪自动确定 默认从触发组件向下全遍历

3.4 组件和状态对比

维度 Vue React
组件定义 .vue 单文件组件(template + script + style) JSX(JavaScript + XML)
局部状态 data() 返回对象 useState / this.state
计算属性 computed(缓存) useMemo 手动实现
侦听器 watch useEffect 手动实现
副作用 watch、生命周期 useEffectuseLayoutEffect
跨组件通信 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 整个组件树,由你告诉它哪里需要优化。


posted @ 2026-06-08 15:49  叶乘风  阅读(2)  评论(0)    收藏  举报