Vue's double binding implementation

// html

<body>
  <div id="app">
    <input type="text" v-model="number">
    <input type="text" v-model="num">
    <input type="button" v-click="increment" value="加1">
    <input type="button" v-click="increment" value="加2">
    <h3 v-bind="number"></h3>
    <h3 v-bind="num"></h3>
  </div>
</body>
// Instance of js vue 

  window.onload = function () {
     var app = new Vue({
      el: ' #app ' ,
      data: {
        number: 0,
        num: 5 ,
      },
      methods: {
        increment: function () {
          this.number++;
          this.num++;
        },
      }
    })
  }
// constructor of vue

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

    this._binding = {};
    this._obverse(this.$data);
    this._complie(this.$el);
  }
  Vue.prototype._obverse = function (obj) {
    var _this = this
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        this._binding[key] = {
          _directives: []
        };
        let value = obj[key];
        if (typeof value === 'object') {
          this._obverse(value);
        }
        let binding = this._binding[key];
        Object.defineProperty(this.$data, key, {
          enumerable: true , // Whether the target property can be enumerated. true | false 
          configurable: true , // Whether the target property can be deleted or whether the property can be modified again true | false 
          get : function () {
             return value;
          },
          set: function (newVal) {
            if (value !== newVal) {
              value = newVal;
              binding._directives.forEach(function (item) {
                item.update();
              })
            }
          }
        })
      }
    }
  }
 

  Vue.prototype._complie = function (root) {
    var _this = this;
    var nodes = root.children;
    for (let i = 0; i < nodes.length; i++) {
          let node = nodes[i];
      if (node.children.length) {
        _this._complie(node);
      }

      if (node.hasAttribute('v-click')) {
        node.onclick = (function () {
          var attrVal = nodes[i].getAttribute('v-click');
          return _this.$methods[attrVal].bind(_this.$data);
        })(i);
      }

      if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {
        node.addEventListener('input', (function() {
          var attrVal = node.getAttribute('v-model');
          _this._binding[attrVal]._directives.push(new Watcher(
            'input',
            node,
            _this,
            attrVal,
            'value'
          ))
          return function () {
            _this.$data[attrVal] = nodes[key].value;
          }
        })());
      }
      if(node.hasAttribute("v-bind")){
        var 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;          // Instruction name, such as text node, the value is set to "text" 
    this .el = el;              // The DOM element corresponding to the instruction 
    this .vm = vm;              // The myVue instance to which the instruction belongs 
    this .exp = exp;            // The value corresponding to the instruction, this example is "number" 
    this .attr = attr;          // The bound attribute value, this example is "innerHTML"

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

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324796672&siteId=291194637