Vue 双向数据绑定原理及实现

class Vue {
  constructor(option) {
    // 1. 保存数据
    this.$options = option
    this.$data = option.data
    this.$el = option.el

    // 2. 将 data 中数据保存到响应式系统中
    new Observer(this.$data)

    // 3. 代理 this.$data 的数据
    Object.keys(this.$data).forEach(key => {
      this._proxy(key)
    })

    // 4. 处理 el
    new Compiler(this.$el, this)
  }

  // 把 this.$data 的数据代理到 this 上
  _proxy(key) {
    Object.defineProperty(this, key, {
      configurable: true,
      enumerable: true,
      get() {
        return this.$data[key]
      },
      set(newVal) {
        this.$data[key] = newVal
      }
    })
  }
}

class Observer {
  constructor(data) {
    this.data = data

    Object.keys(data).forEach(key => {
      this.defineReactive(data, key, data[key])
    })
  }

  defineReactive(data, key, val) {
    const dep = new Dep(key)

    Object.defineProperty(data, key, {
      configurable: true,
      enumerable: true,
      get() {
        if (Dep.target) {
          dep.addSubs(Dep.target)
        }
        return val
      },
      set(newVal) {
        if (val === newVal)
          return
        val = newVal
        dep.notify()
      }
    })
  }
}

const reg = /\{\{(.+)\}\}/

class Compiler {
  constructor(el, vm) {
    this.vm = vm

    this.el = document.querySelector(el)
    // 创建虚拟 DOM
    const frag = this._createFragment(this.el)
    // 把虚拟 DOM 挂载到页面上去
    this.el.appendChild(frag)
  }

  _createFragment(node) {
    const frag = document.createDocumentFragment()

    let child
    while (child = node.firstChild) {
      console.log('push child', child);
      if (child.firstChild) {
        child = this._createFragment(child)
      } else {
        this._compiler(child)
      }
      frag.appendChild(child)
    }
    return frag
  }

  _compiler(node) {
    if (node.nodeType === 1) {
      // 标签节点
      const attrs = node.attributes
      console.log(attrs);
      if (attrs.hasOwnProperty('v-model')) {
        const name = attrs['v-model'].nodeValue
        console.log(name);
        node.value = this.vm[name]
        node.addEventListener('input', e => {
          let ev = e || event;
          this.vm[name] = ev.target.value
        })
        new Watcher(node, name, this.vm)
      }
    } else if (node.nodeType === 3) {
      // 文本节点
      if (reg.test(node.nodeValue)) {
        const name = RegExp.$1.trim()
        console.log(node, name, this.vm);
        new Watcher(node, name, this.vm)
      }
    }
  }
}

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

  addSubs(watcher) {
    this.subs.push(watcher)
  }

  notify() {
    this.subs.forEach(sub => {
      console.log('sub', sub);
      sub.update()
    })
  }
}

class Watcher {
  constructor(node, name, vm) {
    this.vm = vm
    this.node = node
    this.name = name
    Dep.target = this
    this.update()
    Dep.target = null
  }

  update() {
    if (this.node.nodeType === 1) {
      this.node.value = this.vm[this.name]
    } else if (this.node.nodeType === 3) {
      this.node.nodeValue = this.vm[this.name]
    }
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值