菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
51
0

MVVM原理(Object.defineProperty和订阅者模式)

原创
05/13 14:22
阅读数 1457
想着去了解vue的mvvm数据驱动是怎么实现的,百度中看了这篇文章,demo很好。其他文章只是讲到defineProperty的set,get。

彻底理解Vue中的Watcher、Observer、Dep

我把文章的代码demo简化成自己的代码并且做了注释
class Observer {
  constructor(targetObject) {
    //console.log('targetObject',targetObject);
    //def(targetObject, '__ob__', this);//在 targetObject 上 添加  Observer 实例, setter时 通知该实例
    Object.defineProperty(targetObject, '__ob__', {
        value: this,
        //enumerable: !!enumerable,
        writable: true,
        configurable: true
    });  
    //给对象的每个属性设置get set方法
    Object.keys(targetObject).forEach(key => {
      //console.log('targetObject',targetObject,'key',key,'targetObject[key]',targetObject[key]);
      defineReactive(targetObject, key, targetObject[key])//给对象(包括对象内的对象)定义GET SET方法
    });

    //给每个Observer都添加dep实例
    this.dep = new Dep()
    
    //手动执行订阅
    //this.dep.depend()
  }
}
function observe(data) {
  if (Object.prototype.toString.call(data) !== '[object Object]') {
    return
  }
  new Observer(data)
}
function defineReactive(obj, key, val) {
    //再去判断对象属性的值是不是对象,是的话给该对象也新增__ob__属性
    observe(val)
    //上面的oberve()执行完毕之后对象内所有的对象都有__ob__属性

    //去给对象(包括对象内的对象)设置get set方法
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
        console.log('get');
        const ob = this.__ob__
        ob.dep.depend();
        return val
        },
        set: function reactiveSetter(newVal) {
        console.log('set');
        if (newVal === val) return
        val = newVal
        observe(newVal)//新值如果是对象也要给其设置__ob__(值为Observer实例)
        const ob = this.__ob__
        ob.dep.notify(newVal);
        },

    })
}

class Dep {
  constructor() {
    this.subs = []
  }

  addSub(sub) {
    this.subs.push(sub)
  }

  depend() {
    this.subs.push(Dep.target)
  }

  notify(val) {
    for (let i = 0; i < this.subs.length; i++) {
      this.subs[i].fn(val)
    }
  }
}

Dep.target = null

class Watcher {
  constructor(vm, exp, fn) {
    //console.log('vm',vm,'exp',exp,'fn',fn)
    this.vm = vm
    this.exp = exp
    this.fn = fn
    Dep.target = this//将自己挂载到 Dep.target,调用 Dep.depend时会读取该变量
    this.vm[exp]//取值 触发get方法 依赖
  }
}

//实验代码
const obj = {
  a: 1,
  b: {
    c: 2
  }
}

new Observer(obj)
console.log('给obj内所有对象设置__ob__以及SET,GET方法',obj)

//在obj这个对象去监听a这个属性的变化(执行订阅)
new Watcher(obj, 'a', (val) => {
  console.log('obj.a设置了新值了',val);
})
obj.a='222';

所有Vue中的MVVM是用Object.defineProperty和订阅者模式实现的。
设置GET动作是去添加依赖(订阅),SET方法是根据依赖(订阅)菜单去发布更新的值,而真正去做动作的(比如更新到DOM中去)是__ob__的值(即observer)中dep中每个watcher实例去做的。

发表评论

0/200
51 点赞
0 评论
收藏