美文网首页
Vue的双向绑定原理

Vue的双向绑定原理

作者: Yuqin_Shi | 来源:发表于2018-04-18 22:52 被阅读0次

利用js的defineProperty方法,setter/getter。

<!DOCTYPE html>
<head>
  <title>myVue</title>
</head>
<body>
  <div id="app">
    <form>
      <input type="text" v-model="number" />
      <button type="button" v-click="increase">Add</button>
    </form>
    <h3 v-bind="number"></h3>
  </div>
</body>
<script>
  function myVue(options) {
    this._init(options)
  }

  myVue.prototype._init = function (options) {
    this.$options = options
    this.$el = document.querySelector(options.el)
    this.$data = options.data
    this.$methods = options.methods

    this._bindings = {}
    this._obverse(this.$data)
    this._compile(this.$el)
  }

  myVue.prototype._obverse = function (obj) {
    var value
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        this._bindings[key] = {
          _directives: []
        }
        value = obj[key]
        if (typeof value === 'object') {
          this._obverse(value)
        }
        var binding = this._bindings[key]
        Object.defineProperty(this.$data, key, {
          enumerable: true,
          configurable: true,
          get: function () {
            return value
          },
          set: function (newVal) {
            if (value !== newVal) {
              value = newVal
            }
            binding._directives.forEach(function (item) {
              item.update()
            })
          }
        })
      }
    }
  }
  myVue.prototype._compile = function (root) {
    var _this = this
    var nodes = root.children
    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i]
      if (node.children.length) {
        this._compile(node)
      }
      if (node.hasAttribute('v-click')) {
        node.onclick = (function() {
          var attrVal = nodes[i].getAttribute('v-click')
          return _this.$methods[attrVal].bind(_this.$data)
        })()
      }

      if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
        node.addEventListener('input', (function(key) {
          var attrVal = node.getAttribute('v-model')
          _this._bindings[attrVal]._directives.push(new Watcher(
            'input',
            node,
            _this,
            attrVal,
            'value'
          ))
          return function () {
            _this.$data[attrVal] = nodes[key].value
          }
        })(i))
      }

      if (node.hasAttribute('v-bind')) {
        var attrVal = node.getAttribute('v-bind')
        _this._bindings[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 = function () {
    var app = new myVue({
      el: '#app',
      data: {
        number: 0
      },
      methods: {
        increase: function () {
          this.number ++
        }
      }
    })
  }
</script>

相关文章

网友评论

      本文标题:Vue的双向绑定原理

      本文链接:https://www.haomeiwen.com/subject/npibkftx.html