悠扬随风 发表于 2025-2-21 14:57:43

请谈谈 Vue 中的相应式原理,如何实现?

一、Vue2相应式原理:Object.defineProperty的利与弊

实现原理:
// 数据劫持核心实现
function defineReactive(obj, key, val) {
const dep = new Dep(); // 依赖收集容器
Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) { // 当前Watcher实例
      dep.addSub(Dep.target); // 收集依赖
      }
      return val;
    },
    set(newVal) {
      if (val === newVal) return;
      val = newVal;
      dep.notify(); // 触发更新
    }
});
}

// 遍历对象属性实现响应式
function observe(data) {
Object.keys(data).forEach(key => {
    defineReactive(data, key, data);
});
}

// 使用示例
const data = { count: 0 };
observe(data); 典范问题:

[*]无法检测新增属性:
data.newProp = 'test'; // 不会触发更新
// 必须使用 Vue.set(data, 'newProp', 'test')
[*]数组操纵须要特殊处理处罚:
// 直接修改数组下标无效
data.arr = 1; // 不触发更新
// 必须使用变异方法:push/pop/splice等
data.arr.splice(0, 1, 1); 二、Vue3相应式原理:Proxy的降维打击

实现原理:
function reactive(obj) {
return new Proxy(obj, {
    get(target, key, receiver) {
      track(target, key); // 依赖收集
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      trigger(target, key); // 触发更新
      return true;
    }
});
}

// 使用示例
const state = reactive({ count: 0 });
state.newProp = 'test'; // 直接生效!
state.arr = 1; // 直接生效! 优势对比:
特性Vue2(defineProperty)Vue3(Proxy)新增属性监听❌ 须要Vue.set✅ 原生支持数组操纵❌ 需特殊方法✅ 原生支持嵌套对象性能❌ 递归劫持✅ 按需署理 三、日常开发建议与避坑指南

1. 数据操纵规范

// Vue2正确姿势
this.$set(this.obj, 'newKey', value);
this.arr.splice(index, 1, newValue);

// Vue3正确姿势(直接操作)
state.obj.newKey = value;
state.arr = newValue; 2. 性能优化技巧

// 避免深层响应式(Vue3)
import { shallowRef } from 'vue';
const bigObject = shallowRef({ ... }); // 只跟踪.value变化

// 计算属性缓存
const doubleCount = computed(() => count.value * 2);

// 批量更新(Vue3)
import { nextTick } from 'vue';
async function batchUpdate() {
state.a = 1;
state.b = 2;
await nextTick(); // DOM更新完成
} 3. 典范错误示例

// 错误1:解构丢失响应式(Vue3)
const { count } = reactiveObj; // ❌ 丢失响应式
const count = toRef(reactiveObj, 'count'); // ✅ 正确方式

// 错误2:异步更新陷阱
setTimeout(() => {
state.count++; // 可能触发多次渲染
}, 100);

// 正确做法(Vue3)
watchEffect(() => {
// 自动追踪依赖
console.log(state.count);
}); 四、相应式系统筹划启示


[*] 依赖收集流程:

[*]组件渲染时触发getter
[*]将当前Watcher存入Dep
[*]数据变更时通过Dep通知所有Watcher

[*] 更新队列机制:
// 伪代码实现
let queue = [];
function queueWatcher(watcher) {
if (!queue.includes(watcher)) {
    queue.push(watcher);
    nextTick(flushQueue);
}
}
function flushQueue() {
queue.forEach(watcher => watcher.run());
queue = [];
}
五、面试高频问题参考


[*] Vue2/3相应式实现差别的本质原因是什么?

[*]答:Object.defineProperty的范围性 vs Proxy的语言层支持

[*] 为什么Vue3放弃defineProperty?

[*]答:无法处理处罚Map/Set等新数据结构、数组操纵限制、性能开销大

[*] 如何实现自定义相应式系统?

[*]参考思路:Proxy + 依赖收集 + 调理器筹划

总结建议



[*]项目选型:新项目直接用Vue3,老项目渐渐迁移
[*]开发风俗:避免深层嵌套数据结构,合理利用shallowRef
[*]调试技巧:利用Vue Devtools观察依赖关系
[*]进阶学习:阅读@vue/reactivity源码(仅1800行)
相应式系统是Vue的焦点竞争力,明确其实现原理能帮助开发者写出更高效可靠的代码。建议联合项目实际,多实践不同场景下的数据流管理。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 请谈谈 Vue 中的相应式原理,如何实现?