Tetegw
4/9/2018 - 3:50 AM

模拟vue响应式

模拟vue响应式,依赖收集

// 通过调用实现对对象的[响应式]化
function defineReactive(obj, key, val) {
  // 初始化一个订阅者
  const dep = new Dep()
  // 响应
  Object.defineProperty(obj, key, {
    enumerable: true,     // 属性可枚举
    configurable: true,   // 属性可被修改或删除
    get: function reactiveGetter() {
      // 将Dep.target(即当前的Watcher对象存入dep的subs中)
      dep.addSub(Dep.target)
      return val
    },
    set: function reactiveSetter(newVal) {
      if (newVal === val) {
        return
      }
      // 在set的时候触发dep的notify来通知所有的Watcher对象更新视图
      dep.notify()
    }
  })
}
// 批量实现响应式
function observer(obj) {
  if (!obj || (typeof obj !== 'object')) {
    return
  }
  Object.keys(obj).forEach((key) => {
    defineReactive(obj, key, obj[key])
  })
}

// 封装一个Vue
/* class Vue {
  // Vue构造类
  constructor(options) {
    this._data = options.data
    observer(this._data)
  }
} */

// 订阅者,存放Watcher观察者对象
class Dep {
  constructor() {
    // 用来存储Watcher对象的数组
    this.subs = []
  }
  // 在subs中添加一个Watcher对象
  addSub(sub) {
    this.subs.push(sub)
  }
  // 通知所有Watcher对象更新视图
  notify() {
    this.subs.forEach((sub) => {
      sub.update()
    })
  }
}

// 观察者
class Watcher {
  constructor() {
    // 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到
    Dep.target = this
  }
  // 更新视图的方法
  update() {
    console.log('视图更新了...')
  }
}
Dep.target = null

// 实例化
class Vue {
  constructor(options) {
    this._data = options.data
    observer(this._data)
    /* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
    new Watcher()
  }
}
let newVue = new Vue({
  data: {
    'aa': 11,
    'test': '212'
  }
})
console.log(newVue)
/* 在这里模拟render的过程,为了触发test属性的get函数,这样就加入依赖了 */
console.log('render~', newVue._data.test)
/* 在这里模拟触发test属性的set函数,这样就触发依赖的监听者从而update */
newVue._data.test = '改变'
    
// 流程图: https://github.com/Tetegw/summarize/blob/master/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91vue/%E6%A8%A1%E6%8B%9F%E5%93%8D%E5%BA%94%E5%BC%8F.jpg