美文网首页
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