vue之双绑实现

// 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>
// js vue的实例

  window.onload = function () {
    var app = new Vue({
      el: '#app',
      data: {
        number: 0,
        num: 5,
      },
      methods: {
        increment: function () {
          this.number++;
          this.num++;
        },
      }
    })
  }
// 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,//目标属性是否可以被枚举。true | false
          configurable: true, //目标属性是否可以被删除或是否可以再次修改特性 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;         //指令名称,例如文本节点,该值设为"text"
    this.el = el;             //指令对应的DOM元素
    this.vm = vm;             //指令所属myVue实例
    this.exp = exp;           //指令对应的值,本例如"number"
    this.attr = attr;         //绑定的属性值,本例为"innerHTML"

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

 

posted @ 2018-04-24 20:15  ypm_wbg  阅读(155)  评论(0编辑  收藏  举报