// 在Vue对象创建之前,我们需要对数据进行响应式处理
function defineReactive(obj, key, val) {
  // 为每个属性创建一个Dep实例
  const dep = new Dep();

  // 属性配置
  Object.defineProperty(obj, key, {
    // 可枚举
    enumerable: true,
    // 可配置
    configurable: true,
    // 获取属性值时触发
    get: function reactiveGetter() {
      // 将当前订阅者添加到Dep中
      if (Dep.target) {
        dep.addSub(Dep.target);
      }
      return val;
    },
    // 设置属性值时触发
    set: function reactiveSetter(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;

      // 通知所有订阅者更新数据
      dep.notify();
    }
  });
}

// 创建观察者
class Watcher {
  constructor(vm, key, cb) {
    this.vm = vm;
    this.key = key;
    this.cb = cb;

    // 在创建Watcher对象时立即将该对象与最新的Dep关联
    Dep.target = this;
    this.vm[this.key]; // 触发getter,添加订阅者
    Dep.target = null; // 添加完订阅者后重置
  }

  // 更新数据时调用
  update() {
    this.cb.call(this.vm);
  }
}

// Dep用于管理订阅者
class Dep {
  constructor() {
    // 存储所有订阅者
    this.subs = [];
  }

  // 添加订阅者
  addSub(sub) {
    this.subs.push(sub);
  }

  // 通知所有订阅者更新数据
  notify() {
    this.subs.forEach(sub => {
      sub.update();
    });
  }
}

// Vue对象
class Vue {
  constructor(options) {
    this.$options = options;
    this._data = options.data;

    // 将data中的属性转换为响应式数据(使用defineReactive处理)
    Object.keys(this._data).forEach(key => {
      defineReactive(this, key, this._data[key]);
    });

    // 创建观察者对象,用于监听数据变化
    new Watcher(this, 'updateData', this.render);
  }

  // 数据变化时执行的更新方法
  updateData() {
    // 数据变化逻辑处理
    console.log('数据发生变化');
  }

  // 渲染方法
  render() {
    // 渲染逻辑处理
    console.log('渲染页面');
  }
}

// Vue实例化
const app = new Vue({
  data: {
    title: 'Vue响应式原理学习',
    content: '数据双向绑定'
  }
});