Vue2响应式原理详解

Object.defineProperty

通过Object.defineProperty方法进行数据代理, 用vm对象的属性来代理data对象的属性

方法案例

 /* 
  此方法用于定义或修改对象属性的方法。它允许你精确地控制属性的行为,
  包括属性的值、可枚举性、可配置性和可写性。 
  接受三个参数:1 要定义属性的对象、2属性名以及一个3描述符对象。
  描述符对象包含了你想要定义的属性的特性。描述符对象的属性包括:
   value: 属性的值,默认为 undefined。
   writable: 属性是否可写,默认为 false。
   enumerable: 属性是否可枚举,默认为 false。
   configurable: 属性是否可配置,默认为 false。*/ 
  

    let person = {} // 定义一个空对象

    Object.defineProperty(person, 'name', {
      value: 'ZhangSan',
      writable: false,
      enumerable: true,
      configurable: false,

    })

    console.log(person.name);

    person.name = "Bob" // 这行代码不会修改 'name' 属性的值,因为 'writable' 属性被设置为 false

    for (let key in person) {
      console.log(key + ': ' + person[key]); // 只会输出 'name: John',因为 'enumerable' 属性被设置为 true
    }

    delete person.name; // 这行代码不会删除 'name' 属性,因为 'configurable' 属性默认为 false

    console.log('name' in person); // 输出 true,'name' 属性仍然存在于对象中

观察者Observer   订阅者Watcher模式

在Observer类中递归遍历data对象,对data对象中的每个属性都进行数据劫持,都指定一个getter、setter。

例外的,对于数组,不能通过object.defineProperty()进行数据代理,因为监听的数组下标变化时会出现数据错乱问题,所以数组是调用数组重写的原生方法来实现响应式。

当通过vm对象修改data对象中的属性时,会触发data属性的setter方法,然后触发它Dep实例的notify方法进行依赖分发,通知所有依赖的Watcher实例执行内部回调函数。

最后会触发renderWatcher回调,会重新执行render函数,重新对比新旧虚拟DOM,重新渲染页面。

模拟Observer类和 watcher 工作原理:

//简单的示例,展示了如何使用观察者模式实现数据劫持:

  // 观察者类
  class Observer {
      constructor(data) {
        this.data = data;
        this.observe(data)
      }
      // 监听
      observe(obj) {
        // 判断是否是一个对象类型
        if (!obj || typeof obj !== 'object') {
          return
        }
        Object.keys(obj).forEach(key => {
          // 拿到对象中的每一个属性
          console.log(obj, key, obj[key], '拿到对象中的每一个属性--------');
          this.defineReactive(obj, key, obj[key])
          // 是否有多层嵌套。
          this.observe(obj[key])
        })


      }

      // 定义对象的响应式属性的方法
      defineReactive(obj, key, value) {
        let _this = this;
        let dep = new Dep();

        Object.defineProperty(obj, key, {
          enumerable: true,
          configurable: true,
          get() {
            // 收集依赖
            if (Dep.target) {
              dep.addSub(Dep.target);
            }
            return value;
          },
          set(newValue) {
            if (newValue === value) {
              return;
            }
            value = newValue;
            // 通知所有订阅者
            dep.notify();
          }
        });
      }

    }

  // 代表一个依赖(Dependency)管理器  用于管理和通知订阅者
  class Dep {
      constructor() {
        this.subs = [];
      }

      // 添加订阅者
      addSub(sub) {
        console.log(sub, '添加订阅者-----------');
        this.subs.push(sub);
      }

      // 通知订阅者
      notify() {
        this.subs.forEach(sub => {
          sub.update();
        });
      }
    }

  // 订阅者
  class Watcher {
      constructor(vm, key, changeFunc) {
        this.vm = vm;
        this.key = key;
        this.changeFunc = changeFunc;
        Dep.target = this;
        this.vm[this.key]; // 触发 getter,收集依赖 没有此代码 订阅者不会被添加 也不会调用update方法 
        Dep.target = null;
      }

      update() {
        console.log(this.vm, this.vm[this.key], '更新中---------');
        // call 是函数对象的一个方法,它允许你调用一个函数并指定函数内部 this 的值,以及传递其他参数。
        this.changeFunc.call(this.vm, this.vm[this.key]);
      }
    }

    // 用法示例
    let data = {
      name: 'Alice',
      age: 30
    };

    // 创建观察者类 观察data中数据发生变化
    let observer = new Observer(data);

    // 创建 Watcher 实例 订阅者订阅data中name属性是否发生改变 并且改变的时候执行什么操作
    let watcher = new Watcher(data, 'name', function (value) {
      console.log('Name changed:', this, value);
    });

    // 修改数据,触发通知
    console.log(data.name, '更改前----------');
    data.name = 'Bob'; // 输出: Name changed: Bob

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值