2018-05-09 手写一份Vue响应式原理代码

暂时是给自己看的

// 改造数据对象属性
function defineReactive(obj, key, val) {
  const dep = new Dep()

  Reflect.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log('get: ', val)
      if (Dep.target) {
        // 手机依赖,等同于 dep.addSub(Dep.target)
        dep.depend()
      }
      return val
    },
    set: function(newVal) {
      console.log('set: ', newVal)
      // 判断新值与旧值是否相等
      // 判断后半段是为了验证新值与旧值都为NaN的情况
      if (newVal === val || (newVal !== newVal && value !== value)) {
        return;
      }
      val = newVal
      // 发布改变
      dep.notify()
    }
  })
}


// 删除数组中元素
function remove(arrs, item) {
  console.log(arrs, item)
  const index = arrs.indexOf(item)
  if (index !== -1) {
    arrs.splice(index, 1)
  }
} 


// 依赖收集器
class Dep {
  constructor() {
    // 订阅的信息
    this.subs = []
  }

  // 添加一个观察者对象
  addSub(sub) {
    this.subs.push(sub)
  }

  // 删除一个观察者对象
  removeSub(sub) {
    remove(this.subs, sub)
  }

  // 此方法作用等同于 this.subs.push(Watcher)
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  // 发布通知,有更新
  notify() {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
Dep.target = null


// 观察者
class Watcher {
  constructor(vm, cb) {
    this.vm = vm
    this.cb = cb
    Dep.target = this
    this.value = cb()
  }

  addDep(dep) {
    dep.addSub(this)
  }

  update() {
    if (this.cb) this.cb()
    Dep.target = null
  }
}


// 双向绑定
class Observer {
  constructor(data) {
    for (let key in data) {
      defineReactive(data, key, data[key])
    }
  }
}


// 创建 Observer 实例
function observe(data) {
  return new Observer(data)
}


// 操作库
class Vue {
  constructor(options) {
    if (options && typeof options.data === 'function') {
      this._data = options.data.apply(this)
    }
    this._el = options.el
    observe(this._data)
  }

  // 挂载函数
  mount() {
    new Watcher(this, this.render.bind(this))
  }

  // 渲染函数
  render() {
    document.querySelector(this._el).innerHTML = this._data.text
  }
}


// demo
const vm1 = new Vue({
  el: '#app1',
  data() {
    return {
      text: 'app1'
    }
  }
})

const vm2 = new Vue({
  el: '#app2',
  data() {
    return {
      text: 'app2'
    }
  }
})

vm1.mount()
vm2.mount()

vm1._data.text = 'app1___'
vm2._data.text = 'app2___'

你可能感兴趣的:(2018-05-09 手写一份Vue响应式原理代码)