Vue数据双向绑定

169 阅读1分钟

class Vue {
  constructor(options) {
    this.$data = options.data
    // 数据劫持的方法
    Observe(this.$data)
    // 数据代理的方法
    ProxyData(this.$data, this)
    // 模板编译的方法
    Compile(this, options.el)

  }

}
// 数据劫持的方法
function Observe(data) {
  if (data && typeof data === 'object') {
    const dep = new Dep()
    Object.keys(data).forEach(key => {
      let value = data[key]
      Observe(value)
      Object.defineProperty(data, key, {
        get() {
          console.log(`获取了${key}的值`)
          Dep.target && dep.addSub(Dep.target)
          return value
        },
        set(newval) {
          console.log(`修改${key}的值`)
          value = newval
          Observe(value)
          dep.notify()
        }
      })
    })
  }
}

// 数据代理的方法
function ProxyData(data, vm) {
  if (data && typeof data === 'object') {
    Object.keys(data).forEach(key => {
      Object.defineProperty(vm, key, {
        get() {
          return data[key]
        },
        set(newval) {
          data[key] = newval
        }
      })
    })
  }
}

// 通过路径获取对象上的属性方法
function getValue(path, data) {
  return path.split('.').reduce((newObj, key) => newObj[key], data)
}

// 模板编译的方法
function Compile(vm, el) {
  // 获取页面Dom区域
  vm.$el = document.querySelector(el)
  // 创建一个文档碎片
  const fragment = document.createDocumentFragment()
  // 将页面中所有元素添加到文档碎片中
  while (childNode = vm.$el.firstChild) {
    fragment.appendChild(childNode)
  }
  replace(fragment)
  // 将文档碎片中的元素在全部添加到页面中
  vm.$el.appendChild(fragment)

  function replace(node) {
    const reg = /\{\{\s*(\S+?)\s*\}\}/
    if (node.nodeType === 1 && node.nodeName.toUpperCase() === "INPUT") {  // nodeType 返回1 是元素节点
      let temp = [...node.attributes]
      let result = temp.find(item => item.name === 'v-module')
      if (result) {
        let value = getValue(result.nodeValue, vm)
        node.value = value
        new Watcher(vm, result.nodeValue, (newval) => {
          node.value = newval
        })
        node.addEventListener('input', (e) => {
          let arr = result.nodeValue.split('.')
          let obj = arr.slice(0, arr.length - 1).reduce((newObj, key) => newObj[key], vm)
          obj[arr[arr.length - 1]] = e.target.value
        })

      }
    }
    if (node.nodeType === 3) { // 说明是文本节点
      let text = node.nodeValue
      let path = reg.exec(text)
      if (path) {
        let value = getValue(path[1], vm)
        node.nodeValue = value
        new Watcher(vm, path[1], (newval) => {
          node.nodeValue = newval
        })
      }
    }
    node.childNodes.forEach(node => {
      replace(node)
    })
  }
}

// 发布者类、订阅者收集
class Dep {
  constructor() {
    this.subs = []
  }
  addSub(watcher) {
    this.subs.push(watcher)
  }
  notify() {
    this.subs.forEach(watcher => watcher.update())
  }
}

// 订阅者类
class Watcher {
  constructor(vm, path, callback) {
    this.vm = vm
    this.path = path
    this.callback = callback

    // 下面的代码很重要
    Dep.target = this
    getValue(path, vm)
    Dep.target = null

  }
  // 更新方法
  update() {
    let value = getValue(this.path, this.vm)
    this.callback(value)
  }
}