Vue 双向数据绑定实现

版权声明:【原创】GitHub:https://github.com/susuGirl,微信公众号:fuxiaodexing,博客:https://blog.csdn.net/weixin_41845146 https://blog.csdn.net/weixin_41845146/article/details/84872298

<!DOCTYPE html>
<html>

<head>
  <title>myVue</title>
  <style>
    #app{
      text-align: center;
    }
</style>
</head>

<body>
  <div id="app">
    <form>
      <input type="text" v-model="number" />
      <button type="button" v-click="increment">增加</button>
    </form>
    <h3 v-bind="number"></h3>
  </div>
</body>

<script>
  function myVue(option) {
    this._init(option)
  }

  myVue.prototype._init = function (options) { // 传了一个配置对象
    this.$options = options
    this.$el = document.querySelector(options.el)
    this.$data = options.data
    this.$methods = options.methods

    this._binding = {}

    this._obsever(this.$data)
    this._compile(this.$el)
  }

  myVue.prototype._obsever = function (obj) {
    let _this = this
    Object.keys(obj).forEach((key) => {
      if (obj.hasOwnProperty(key)) {
        _this._binding[key] = {
          directives: []
        }
      }
      let value = obj[key]
      if (typeof value === 'Object') {
        _this._obsever(value)
      }
      let binding = _this._binding[key]
      Object.defineProperty(_this.$data, key, {
        enumerable: true,
        configurable: true,
        get: () => {
          return value
        },
        set: (newVal) => {
          if (value !== newVal) {
            value = newVal
            binding.directives.forEach((item) => {
              item.update()
            })
          }
        }
      })
    })
  }

  myVue.prototype._compile = function (root) {
    let _this = this
    let nodes = root.children
    for (let i = 0; i < nodes.length; i++) {
      let node = nodes[i]
      if (node.children.length > 0) {
        _this._compile(node)
      }
      if (node.hasAttribute('v-click')) {
        let attrVal = node.getAttribute('v-click')
        node.onclick = _this.$methods[attrVal].bind(_this.$data)
      }
      if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
        let attrVal = node.getAttribute('v-model')
        _this._binding[attrVal].directives.push(new Watcher(
          'input',
          node,
          _this,
          attrVal,
          'value'
        ))
        node.addEventListener('input', () => {
          _this.$data[attrVal] = node.value
        })
      }
      if (node.hasAttribute('v-bind')) {
        let attrVal = node.getAttribute('v-bind')
        _this._binding[attrVal].directives.push(new Watcher(
          'text',
          node,
          _this,
          attrVal,
          'innerHTML'
        ))
      }
    }
  }

  function Watcher(name, el, vm, exp, attr) {
    this.name = name
    this.el = el
    this.vm = vm
    this.exp = exp
    this.attr = attr

    this.update()
  }
  Watcher.prototype.update = function () {
    this.el[this.attr] = this.vm.$data[this.exp]
  }

  window.onload = () => { // 当文档内容完全加载完成会触发该事件,避免获取不到对象的情况
    new myVue({
      el: '#app',
      data: {
        number: 0,
        count: 0
      },
      methods: {
        increment() {
          this.number++
        },
        incre() {
          this.count++
        }
      }
    })
  }
</script>

</html>

猜你喜欢

转载自blog.csdn.net/weixin_41845146/article/details/84872298