一、设计哲学与底层原理差异

1.1 响应式系统的基因差异

Vue3 的 Composition API 建立在 Proxy-based 响应式系统之上,通过劫持对象的 getter/setter 实现依赖收集。当访问响应式对象时,Vue 会自动建立组件与数据的依赖关系。

// Vue 响应式原理简版实现
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key) // 依赖收集
      return Reflect.get(target, key)
    },
    set(target, key, value) {
      Reflect.set(target, key, value)
      trigger(target, key) // 触发更新
    }
  })
}

React Hooks 基于函数组件的闭包机制和状态队列实现。每次渲染都会捕获当前闭包中的状态值,通过链表结构维护 Hook 的顺序:

// React 状态队列简版实现
let hookIndex = 0
let hooks = []

function useState(initial) {
  const currentIndex = hookIndex++
  hooks[currentIndex] = hooks[currentIndex] || initial
  
  const setState = (newValue) => {
    hooks[currentIndex] = newValue
    scheduleUpdate() // 触发重新渲染
  }
  
  return [hooks[currentIndex], setState]
}

1.2 更新机制的差异

  • Vue 采用精确的依赖追踪,只有真正被使用的数据变化才会触发组件更新
  • React 依赖状态变化的显式通知,当状态变更时总是触发整个组件重新渲染

1.3 生命周期管理

  • Vue 的 setup() 函数在组件实例创建时执行一次,与组件生命周期解耦
  • React Hooks 在每次渲染时都会执行,需要依赖数组控制执行时机

二、实践中的核心差异

2.1状态管理的不同范式Vue的响应式系统允许直接修改状态:

// Vue
const count = ref(0)
const increment = () => count.value++
React强调不可变性:
// React
const [count, setCount] = useState(0)
const increment = () => setCount(prev => prev + 1)

2.2副作用处理的对比Vue的watchEffect自动追踪依赖:

// Vue
watchEffect(() => {
  console.log(`Count changed: ${count.value}`)
})
React需要显式声明依赖:
// React
useEffect(() => {
  console.log(`Count changed: ${count}`)
}, [count])

2.3逻辑复用模式对比

Vue复用:

// useCounter.js
export function useCounter() {
  const count = ref(0)
  const increment = () => count.value++
  return { count, increment }
}

React自定义Hook:

// useCounter.js
export function useCounter() {
  const [count, setCount] = useState(0)
  const increment = () => setCount(p => p + 1)
  return { count, increment }
}

三、典型场景对比分析

3.1 复杂状态管理

Vue 组合式方案:
function useUser() {
  const user = reactive({
    name: '',
    age: 0,
    // 计算属性
    isAdult: computed(() => user.age >= 18)
  })

  async function loadUser() {
    const data = await fetchUser()
    Object.assign(user, data)
  }

  return { user, loadUser }
}
React 实现方案:
function useUser() {
  const [user, setUser] = useState({ name: '', age: 0 })
  const isAdult = useMemo(() => user.age >= 18, [user.age])

  const loadUser = useCallback(async () => {
    const data = await fetchUser()
    setUser(data)
  }, [])

  return { user, isAdult, loadUser }
}

3.2 DOM 操作场景

Vue 的 ref 模板引用:
const inputRef = ref(null)

onMounted(() => {
  inputRef.value.focus()
})

return { inputRef }
React 的 useRef:
const inputRef = useRef(null)

useEffect(() => {
  inputRef.current.focus()
}, [])

return <input ref={inputRef} />

四、深度设计对比

4.1 心智模型差异

  • Vue 采用 "状态 + 依赖追踪" 模型,开发者关注数据变化
  • React 采用 "状态快照 + 渲染函数" 模型,开发者关注状态到 UI 的映射

4.2 更新粒度控制

  • Vue 的响应式系统实现组件级精准更新
  • React 需要配合 memo、useMemo 等手动优化

4.3 TypeScript 支持

  • Vue 的响应式系统能自动推导 ref.value 类型
  • React 需要显式声明泛型类型参数

4.4 Hook 调用顺序的约束与自由

ReactHooks的链表实现机制 \
React Hooks 通过维护一个隐式的链表来跟踪 Hook 的调用顺序。每次组件渲染时,Hooks 必须按完全一致的顺序 被调用,否则会导致状态错乱。这一设计直接导致以下限制:
// React 错误示例:条件中调用 Hook
function MyComponent({ isAdmin }) {
  if (isAdmin) {
    const [adminData, setAdminData] = useState(null); // 违反规则
  }
  const [userData, setUserData] = useState({});
  // ...
}

此处条件语句中的 Hook 调用会导致后续渲染时 Hook 顺序不一致,引发难以追踪的 Bug。

VueCompositionAPI的响应式解耦 \
Vue 的响应式系统基于 Proxy 的依赖收集,与 Hook 调用顺序完全解耦。setup 函数仅在组件实例化时执行一次,状态通过闭包持久化:
// Vue 合法代码:条件中使用 ref
export default {
  setup(props) {
    if (props.isAdmin) {
      const adminData = ref(null); // 完全合法
    }
    const userData = ref({});
    return { userData };
  }
}

Vue 的响应式标记在初始化阶段即完成,后续状态变化通过依赖触发更新,无需维护调用顺序。

五、性能优化的不同路径

5.1 渲染机制对 GC 的影响

React的渲染周期压力
每次重新渲染时,函数组件内的所有 Hook 都需要重新执行,导致:

  • 临时闭包对象频繁创建/销毁
  • 依赖数组的浅比较带来额外计算
  • 需要手动优化(如 useMemo/useCallback)来减少子组件渲染

Vue的响应式优化
基于 Proxy 的响应式系统实现精准更新:

// Vue 自动依赖追踪示例
const state = reactive({ a: 1, b: 2 });

watchEffect(() => {
  console.log(state.a); // 仅当 a 变化时触发
});

当 state.b 变化时,不会触发上述副作用。这种细粒度更新显著降低 GC 压力,尤其在复杂组件中优势明显。
 

六、框架哲学总结

维度Vue Composition APIReact Hooks
设计基础响应式系统函数闭包机制
状态更新可变状态不可变状态
依赖管理自动追踪显式声明
生命周期与实例绑定与渲染周期绑定
逻辑复用组合式函数自定义 Hook
类型推导基于响应式结构泛型参数
更新粒度组件级 + 精准更新组件级 + 手动优化
适用场景复杂响应式应用声明式UI + 不可变数据流

七、总结:

差异背后的统一思想尽管存在显著差异,两者都致力于解决同类问题

  • 逻辑复用 :通过组合取代继承(React)和混入(Vue)
  • 关注点分离 :按功能而非生命周期组织代码
  • 类型支持 :更好的 TypeScript 集成

最终选择取决于:

  • 团队偏好 :响应式思维 vs 函数式思维
  • 项目规模 :Vue 适合快速迭代,React 适合严格架构
  • 性能需求 :高频更新场景优选 Vue,复杂渲染控制优选 React

技术交流沟通➕V:yinzhixiaxue


银之夏雪
13 声望0 粉丝