VUE3.X——响应数据原理

Vue2.x采用数据劫持和发布-订阅者模式实现双向绑定,通过Object.defineProperty()监听属性变化。Vue3.x则引入了ES6的Proxy对象,创建代理对象来拦截和修改对象操作。Proxy提供了更灵活的拦截行为,但语法相对复杂,且不支持部分旧版浏览器。在数据响应方面,get和set方法分别用于获取和设置属性值,同时可以进行额外的操作,如日志记录或验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VUE2.X双向绑定是通过数据劫持结合发布-订阅者模式实现的,核心是通过 Object.defineProperty() 劫持各个属性值的 gettersetter,在数据变动时发布消息给订阅者,从而触发相应的监听回调,而VUE3.X则是通过ES6的 Proxy 对象实现的。

  1. 什么是 Proxy

    Proxy 意为代理,它是 js 的内置对象,用于拦截和更改对象相关的不同操作行为,不支持 IE 和 Safari <10 的版本。

    let obj = {name: 'zs', age: 18};
    // Proxy 接收两个参数,target 和 handler
    // target: 需要代理的目标对象
    // handler: 行为对象,属性是当执行一个操作时定义代理的行为函数
    let state = new Proxy(obj, {
        get(obj, key) { ... },
        set(obj, key, newVal) { ... }
    })
    
  2. Proxygetter/setter 的区别

    • 语法不同

      相比 getter/setterProxy 的语法会更为冗长。

    • 与原始对象的交互方式不同

      Proxy 会创建一个新对象供用户与之交互,而不是直接修改原对象,在使用 Proxy 的情况下,原始对象与代理对象为引用关系,即对原始对象做的修改都会影响到代理对象,但是不会触发 handler 里的行为函数。

      (更多行为函数可查阅:Proxy行为函数

      getter/setter 会直接修改原对象。

  3. Proxy 实现数据响应

    • get 有两个参数,分别是原始对象 target 和获取的对象属性名 key,例:

      let obj = {name: 'zs', age: 18};
      let state = new Proxy(obj, {
          get(obj, key) {
              console.log(obj, key); // {name: 'zs', age: 18} 'name'
              return obj[key];
          }
      })
      
      console.log(state.name); //'zs'
      
    • set 有三个参数,分别是原始对象 target 、设置的对象属性名 key 和设置的新值 value ,值得注意的是, set 函数最后需要返回值,用来告知 Proxy 本次操作是否成功 ,例:

      let arr = [1, 3, 5];
      let state = new Proxy(arr, {
          get(arr, index) {
              return arr[index];
          },
          set(arr, index, value) {
              // [1, 3, 5] '3' 7
              // [1, 3, 5] 'length' 4
              console.log(arr, index, value);
              arr[index] = value;
              return true;
          }
      })
      
      state.push(7);
      console.log(state); // Proxy {0: 1, 1: 3, 2: 5, 3: 7}
      console.log(obj); // [1, 3, 5, 7]
      

    目录:VUE3.X笔记——目录
    上一篇:VUE3.X——递归监听与非递归监听
    下一篇:VUE3.X——获取dom元素

### Vue.js 响应式机制解析 Vue.js 的核心特性之一就是其响应式系统,该系统能够自动追踪数据的变化并更新视图。以下是关于 Vue.js 使用 `Object.defineProperty` 或 `Proxy` 追踪数据变化的具体工作原理。 #### 数据劫持与依赖收集 Vue.js 中的数据劫持主要依靠 JavaScript 提供的对象属性定义功能。在 Vue 2.x 版本中,使用的是 `Object.defineProperty` 方法;而在 Vue 3.x 版本中,则升级为了更强大的 `Proxy` API[^1]。 对于对象类型的响应式处理,在 Vue 2.x 中会通过递归的方式遍历对象中的每一层嵌套属性,并调用 `Object.defineProperty` 将它们转化为带有 getter 和 setter 的形式。当某个属性被访问时(即触发 getter),框架会记录当前活动的 watcher 到对应的依赖列表中;而当这个属性发生改变时(即触发 setter),则通知所有关联的 watcher 执行相应的回调函数完成 DOM 更新操作[^3]。 然而需要注意的是,由于 `Object.defineProperty` 只能作用于已存在的属性上,因此如果动态新增了一个不存在的字段或者是直接更改数组索引值等情况下的变动将无法被捕获到[^2]。针对这些问题,在实际开发过程中可以考虑采用 `$set()` 方法手动添加新成员使其具备反应能力或者切换至支持全量代理模式的新一代解决方案——基于 ES6 标准设计出来的 Proxies 结构体作为替代品。 下面是利用 `Object.defineProperty` 创建简单版本响应式的例子: ```javascript function observe(obj) { if (!obj || typeof obj !== &#39;object&#39;) return; Object.keys(obj).forEach(key => { defineReactive(obj, key, obj[key]); }); } function defineReactive(target, key, value) { const dep = new Dep(); // 假设我们有一个Dep类用于管理订阅者 observe(value); // 如果value还是个对象的话继续观察内部结构 Object.defineProperty(target, key, { enumerable: true, configurable: false, get(){ console.log(`get ${key}`); if(Dep.target){ dep.addSub(Dep.target); } return value; }, set(newValue){ if(newValue === value) return; console.log(`${key} changed`); value = newValue; dep.notify(); } }); } ``` 上述代码片段展示了如何构建基础版的 Observer 功能模块。其中包含了两个重要组成部分:一个是用来监控目标对象及其子节点状态变化情况的方法集合 (`observe`) ,另一个则是具体实现 Getter/Setter 行为逻辑的部分 (`defineReactive`) 。每当有新的 Watcher 加入进来的时候都会被注册进对应 Property 上面所维护的那个 Subscription List 当中去等待后续可能发生的事件驱动流程执行完毕之后再依次唤醒这些监听器实例从而达到同步刷新 UI 层面上显示效果的目的。 相比之下,Vue 3.x 引入了更加现代化的技术栈 —— **Proxy** 来代替原有的方式来进行深层次的数据拦截控制作业。相比起前者来说后者不仅性能表现优异而且适用范围也更为广泛因为它可以直接应用于整个容器级别而不是局限于单个键名之上同时还解决了诸如数组变异方法覆盖以及新增属性检测等诸多历史遗留难题。 下面是一个简单的示例展示如何借助 Proxy 构建类似的响应式环境: ```javascript const handler = { get(target, propKey, receiver){ track(propKey); return Reflect.get(...arguments); }, set(target, propKey, value, receiver){ let oldValue = target[propKey]; let result = Reflect.set(...arguments); if(oldValue != value){ triggerEffects(propKey); } return result; } }; let reactiveObj = new Proxy(originalData,{...handler}); ``` 在这里可以看到相比于之前的方案这里采用了完全不同的思路来解决问题。通过对原始数据源施加一层额外封装后的虚拟镜像副本使得我们可以自由定制各种自定义规则以便更好地满足项目需求场景下的特殊要求比如延迟加载资源文件等等。 综上所述无论是哪种技术选型背后都蕴含着深刻的设计哲学理念那就是尽可能减少不必要的计算开销提高整体运行效率的同时也要兼顾灵活性便于开发者快速搭建复杂交互应用界面原型产品出来进行验证迭代优化直至最终定稿发布上线运营推广阶段结束为止始终保持着高度一致性的用户体验质量标准不变。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值