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

打印 上一主题 下一主题

主题 962|帖子 962|积分 2886

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

实现原理
  1. // 数据劫持核心实现
  2. function defineReactive(obj, key, val) {
  3.   const dep = new Dep(); // 依赖收集容器
  4.   Object.defineProperty(obj, key, {
  5.     get() {
  6.       if (Dep.target) { // 当前Watcher实例
  7.         dep.addSub(Dep.target); // 收集依赖
  8.       }
  9.       return val;
  10.     },
  11.     set(newVal) {
  12.       if (val === newVal) return;
  13.       val = newVal;
  14.       dep.notify(); // 触发更新
  15.     }
  16.   });
  17. }
  18. // 遍历对象属性实现响应式
  19. function observe(data) {
  20.   Object.keys(data).forEach(key => {
  21.     defineReactive(data, key, data[key]);
  22.   });
  23. }
  24. // 使用示例
  25. const data = { count: 0 };
  26. observe(data);
复制代码
典范问题

  • 无法检测新增属性
  1. data.newProp = 'test'; // 不会触发更新
  2. // 必须使用 Vue.set(data, 'newProp', 'test')
复制代码

  • 数组操纵须要特殊处理处罚
  1. // 直接修改数组下标无效
  2. data.arr[0] = 1; // 不触发更新
  3. // 必须使用变异方法:push/pop/splice等
  4. data.arr.splice(0, 1, 1);
复制代码

二、Vue3相应式原理:Proxy的降维打击

实现原理
  1. function reactive(obj) {
  2.   return new Proxy(obj, {
  3.     get(target, key, receiver) {
  4.       track(target, key); // 依赖收集
  5.       return Reflect.get(target, key, receiver);
  6.     },
  7.     set(target, key, value, receiver) {
  8.       Reflect.set(target, key, value, receiver);
  9.       trigger(target, key); // 触发更新
  10.       return true;
  11.     }
  12.   });
  13. }
  14. // 使用示例
  15. const state = reactive({ count: 0 });
  16. state.newProp = 'test'; // 直接生效!
  17. state.arr[0] = 1; // 直接生效!
复制代码
优势对比
特性Vue2(defineProperty)Vue3(Proxy)新增属性监听❌ 须要Vue.set✅ 原生支持数组操纵❌ 需特殊方法✅ 原生支持嵌套对象性能❌ 递归劫持✅ 按需署理
三、日常开发建议与避坑指南

1. 数据操纵规范

  1. // Vue2正确姿势
  2. this.$set(this.obj, 'newKey', value);
  3. this.arr.splice(index, 1, newValue);
  4. // Vue3正确姿势(直接操作)
  5. state.obj.newKey = value;
  6. state.arr[index] = newValue;
复制代码
2. 性能优化技巧

  1. // 避免深层响应式(Vue3)
  2. import { shallowRef } from 'vue';
  3. const bigObject = shallowRef({ ... }); // 只跟踪.value变化
  4. // 计算属性缓存
  5. const doubleCount = computed(() => count.value * 2);
  6. // 批量更新(Vue3)
  7. import { nextTick } from 'vue';
  8. async function batchUpdate() {
  9.   state.a = 1;
  10.   state.b = 2;
  11.   await nextTick(); // DOM更新完成
  12. }
复制代码
3. 典范错误示例

  1. // 错误1:解构丢失响应式(Vue3)
  2. const { count } = reactiveObj; // ❌ 丢失响应式
  3. const count = toRef(reactiveObj, 'count'); // ✅ 正确方式
  4. // 错误2:异步更新陷阱
  5. setTimeout(() => {
  6.   state.count++; // 可能触发多次渲染
  7. }, 100);
  8. // 正确做法(Vue3)
  9. watchEffect(() => {
  10.   // 自动追踪依赖
  11.   console.log(state.count);
  12. });
复制代码

四、相应式系统筹划启示


  • 依赖收集流程

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

  • 更新队列机制
    1. // 伪代码实现
    2. let queue = [];
    3. function queueWatcher(watcher) {
    4.   if (!queue.includes(watcher)) {
    5.     queue.push(watcher);
    6.     nextTick(flushQueue);
    7.   }
    8. }
    9. function flushQueue() {
    10.   queue.forEach(watcher => watcher.run());
    11.   queue = [];
    12. }
    复制代码

五、面试高频问题参考


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

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

  • 为什么Vue3放弃defineProperty?

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

  • 如何实现自定义相应式系统?

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


总结建议



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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表