vue响应式原理

class Vue {
  constructor(options) {
    this.$options = options || {}
    this.$data = options.data
    this.$el = document.querySelector(options.el)
    //this._dep = {}
    this.Observer(this.$data)
    this.Compile(this.$el)
  }
  // 劫持监听所有属性
  Observer(data) {
    for(let key in data) {
      const dep = new Dep()
      //this._dep[key] = []
      let Val = data[key]
      Object.defineProperty(this.$data, key, {
        get: function () {
          if(Dep.target) {
            dep.addSubs(Dep.target)
          }
          return Val
        },
        set: function(newVal) {
          if(newVal !== Val) {
            Val = newVal
            dep.notify()
          }
        }
      })
    }
  }

  // 解析指令
  Compile(el) {
    const children = el.children
    if(!children.length) throw new Error('挂载元素里面内容为空!')
    for(let i=0; i< children.length; i++) {
      const node = children[i]
      if(node.hasAttribute('v-text')) {
        const expValue = node.getAttribute('v-text')
        new Watcher(node, this, expValue, 'innerHTML')
        //this._dep[expValue].push()
      }
      if(node.hasAttribute('v-model')) {
        const expValue = node.getAttribute('v-model')
        new Watcher(node, this, expValue, 'value')
        //this._dep[expValue].push()
        node.addEventListener('input', (function() {
          return function() {
            this.$data[expValue] = node.value
          }
        })().bind(this))
      }
      if(node.children.length) {
        this.Compile(node)
      }
    }
  }
}

class Dep {
  constructor() {
    this.target = null
    this.subs = []
  }
  addSubs(sub) {
    this.subs.push(sub)
  }
  notify() {
    this.subs.forEach(item => {
      item.update()
    })
  }
}

// 订阅者
class Watcher {
  constructor(el, vm, exp, attr) {
    this.el = el
    this.vm = vm
    this.exp = exp
    this.attr = attr
    this.update()
    this.value = this.get()
  }
  update() {
    this.el[this.attr] = this.vm.$data[this.exp]
    
  }
  get() {
    Dep.target = this // 缓存自己
    const value = this.vm.$data[this.exp]
    Dep.target = null // 释放自己
    return value
  }
}

你可能感兴趣的:(vue响应式原理)