美文网首页
defineproperty实现数据监听

defineproperty实现数据监听

作者: 小杰66 | 来源:发表于2021-04-05 21:48 被阅读0次

简单实现vue2.0中的数据监听,大概过程如下
1.递归遍历监听对象,创建Observer通过defineproperty劫持属性,并给每个属性创建一个消息管理器(dep)
2.当监听某个属性时,会创建Watcher作为订阅者注册到该属性创建的消息管理器(dep中)
3.当监听对象属性发生改变时,该属性的消息管理器(dep)就会通知到订阅的Watcher来指定回调

代码和注释如下,主要魅力在Watcher的get方法这里

//判断是否是对象 是对象就通过Observer进行对象属性劫持
function observe(data) {
  //只有对象才需要劫持
  if (!data || typeof data !== "object") return;
  return new Observer(data);
}

//Observer类 作用就是通过defineProperty来劫持所有对象属性
class Observer {
  constructor(obj) {
    this.obj = obj;
    this.walk(obj);
  }
  walk(obj) {
    Object.keys(obj).forEach((key) => {
      this.convert(key, obj[key]);
    });
  }
  convert(key, val) {
    defineReactive(this.obj, key, val);
  }
}

function defineReactive(obj, key, val) {
  //每个属性都需要创建一个消息管理器dep
  const dep = new Dep();
  //继续劫持属性值
  let childOb = observe(val);
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: true,
    get: () => {
      //该属性被监听时,会实例化一个watcher
      //实例化过程中会将实例赋值给Dep.target然后获取监听的属性值
      //这时候会进入到这里调用dep.depend来将watcher加入到消息管理器dep的订阅中
      if (Dep.target) {
        dep.depend();
      }
      return val;
    },
    set: (newVal) => {
      if (newVal === val) return;
      val = newVal;
      //数据改变时,劫持新的属性值
      childOb = observe(val);
      //然后通过消息管理器dep通知所有的订阅者
      dep.notify();
    },
  });
}

//Dep是消息管理器,负责保存订阅属性的watcher和当属性值变化时通知订阅者更新
let uid = 0;
class Dep {
  constructor() {
    //唯一id
    this.id = uid++;
    //记录订阅者
    this.subs = [];
  }
  depend() {
    //这里将是否需要添加订阅的逻辑交给watcher来判断,避免重复添加同一个watcher
    Dep.target.addDep(this);
  }
  addSub(sub) {
    //添加订阅
    this.subs.push(sub);
  }
  notify() {
    //向订阅者发送更新消息
    this.subs.forEach((sub) => sub.update());
  }
}

//watcher 订阅者 负责更新
class Watcher {
  constructor(data, prop, cb) {
    //记录dep 用于避免重复添加到同一dep
    this.depIds = {};
    this.data = data;
    this.prop = prop;
    this.cb = cb;
    //这里的get一是记录属性值,二是将watcher实例赋值给Dep.target,
    //然后获取属性值进入属性的get方法来使watcher订阅该属性的消息管理器
    this.val = this.get();
  }

  addDep(dep) {
    //如果depIds中已经记录来dep.id说明该watcher已经添加到该dep中了
    if (!this.depIds.hasOwnProperty(dep.id)) {
      this.depIds[dep.id] = dep;
      dep.addSub(this);
    }
  }

  //更新操作
  update() {
    this.run();
  }

  //执行更新回调
  run() {
    let val = this.get();
    if (val !== this.val) {
      this.val = val;
      this.cb(this.data, val);
    }
  }

  get() {
    //实例赋值给Dep.target
    Dep.target = this;
    //获取属性值进入到get方法 调用dep.depend
    let val = this.data[this.prop];
    //还原
    Dep.target = null;
    return val;
  }
}

//监听对象属性方法
function watch(obj, prop, cb) {
  new Watcher(obj, prop, cb);
}

var obj = {
  name: "jay",
  age: 28,
  watch(prop, cb) {
    new Watcher(this, prop, cb);
  },
};
observe(obj);
obj.watch("name", function (obj, val) {
  console.log(val + "!!@#$");
});

obj.name = "xu";

相关文章

  • defineproperty实现数据监听

    简单实现vue2.0中的数据监听,大概过程如下1.递归遍历监听对象,创建Observer通过defineprope...

  • vue双向数据绑定、路由

    vue双向数据绑定原理: 开发订阅者模式。 实现的关键是object.defineProperty监听。gette...

  • 每天一提问

    问题 如何监听数据数据变化 definePropety 语法 Object.defineProperty(obj,...

  • 探索defineproperty 和 proxy

    方法概念兼容性监听应用defineproperty数据劫持任意浏览器监听对象各个属性,无法监听新增属性。使用时需要...

  • Vue3.0 API的使用

    Vue3.0 整体优势 proxy数据劫持代替object.defineProperty(特点:可以监听数组变化,...

  • vue 双向数据绑定

    Vue实现数据双向绑定的原理:Object.defineProperty()vue实现数据双向绑定主要是:采用数据...

  • 双向绑定

    我们知道可以利用Obeject.defineProperty()来监听属性变动那么将需要observe的数据对象进...

  • Vue实现数据双向绑定的原理

    Vue实现数据双向绑定的原理:Object.defineProperty() vue实现数据双向绑定主要是:采用数...

  • 如何在ES5的环境下实现const

    Vue双向绑定的核心实现思路就是利用Object.defineProperty对get跟set进行劫持,监听用户对...

  • 原生js实现数据双向绑定效果

    通过Object.defineProperty实现效果 使用ES6 class实现数据双向绑定

网友评论

      本文标题:defineproperty实现数据监听

      本文链接:https://www.haomeiwen.com/subject/unhahltx.html