多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

vue和react的响应式有什么区别?(上)

vue和react的响应式有什么区别?(上) Vue 和 React 都能做到“数据变化后更新页面”但它们感知变化的方式不同Vue 追踪“哪些响应式属性被读取和修改”React 追踪“哪个组件调用了状态更新函数”。可以先看这张核心对比表一、React显式通知式响应React 不会监听普通变量或对象属性的修改。function Counter() { let count 0 function add() { count } return button onClick{add}{count}/button }点击以后变量确实变了但页面不会更新因为 React 不知道发生了变化。必须调用 React 提供的 setterfunction Counter() { const [count, setCount] useState(0) function add() { setCount(count 1) } return button onClick{add}{count}/button }setCount做两件事记录下一次状态 请求 React 重新渲染当前组件下一次渲染时React 会重新调用Counter()生成新的 JSX再把新旧结果进行协调比较。React 官方将这一过程划分为触发渲染、执行渲染和提交 DOM 三个阶段。可以简化成setCount(1) ↓ React 安排更新 ↓ Counter() 再执行一次 ↓ 得到新的 JSX ↓ 比较新旧 UI ↓ 更新必要的 DOM二、Vue依赖追踪式响应Vue 会把状态转换成响应式对象import { reactive } from vue const state reactive({ count: 0 })Vue 3 对响应式对象主要使用Proxy。简化原理如下function reactive(obj) { return new Proxy(obj, { get(target, key) { track(target, key) return target[key] }, set(target, key, value) { target[key] value trigger(target, key) return true } }) }当组件渲染时读取state.countVue 的get拦截器会记录当前组件依赖 state.count以后执行state.countset拦截器会通知之前依赖state.count的响应式 effect组件随后重新渲染。Vue 3 对响应式对象使用 Proxy对ref的.value使用 getter/setter并通过track和trigger完成依赖收集及更新触发。过程可以简化为组件读取 state.count ↓ Vue 记录依赖关系 ↓ state.count ↓ Proxy 拦截修改 ↓ 通知依赖它的组件 ↓ 组件重新渲染三、最大的区别谁负责告诉框架发生了变化React开发者显式通知setCount(count 1)React 不关心你之前读取了哪些普通变量。它只知道某个 state setter 被调用了 当前组件需要重新计算Vue框架自动追踪count.value或者state.countVue在组件渲染过程中已经记录了这个组件读取过 count所以 count 修改后它知道应该通知哪些响应式 effect。因此可以概括为React事件驱动组件更新 Vue依赖驱动组件更新不过两者最后通常都会重新执行相应组件的渲染逻辑并同步必要的 DOM 变化。四、对象更新方式不同这是实际开发中最明显的区别之一。Vue 可以直接修改响应式对象script setup import { reactive } from vue const user reactive({ name: Alice, age: 20 }) function rename() { user.name Bob } /script template button clickrename {{ user.name }} /button /template执行user.name Bob会经过 Proxy 的set拦截因此可以触发更新。React 通常不能直接修改状态对象错误写法const [user, setUser] useState({ name: Alice, age: 20 }) function rename() { user.name Bob setUser(user) }这里传回去的仍然是原来的对象Object.is(oldUser, newUser) // trueReact 可能认为状态没有变化并跳过更新。应创建新对象function rename() { setUser(previous ({ ...previous, name: Bob })) }React 官方建议把 state 中的对象视为只读数据通过创建并传入新对象完成更新而不是直接修改原对象所以Vue以可变响应式对象为主 React以状态快照和不可变更新为主五、更新粒度有什么区别假设状态是const state { name: Alice, age: 20, address: { city: Beijing } }Vue 的依赖追踪更细如果模板只读取p{{ state.name }}/pVue 会追踪对state.name的读取。修改state.age不会触发所有读取state的响应式逻辑Vue知道修改的是age而不是name。但要注意Vue知道哪些属性触发了组件的渲染 effect不代表它直接绕过组件重新渲染、只修改一个表达式。当前 Vue 默认渲染系统仍会重新执行相关组件的渲染 effect然后借助虚拟 DOM 和编译器生成的优化信息减少比较及 DOM 操作。React 的触发单位更偏向组件const [user, setUser] useState({ name: Alice, age: 20 })调用setUser(previous ({ ...previous, age: previous.age 1 }))React知道的是这个组件的 user 状态更新了
返回列表