zoukankan      html  css  js  c++  java
  • 手写vue双向绑定数据

    来一张原理图:

    实现思路:

      (1)绑定data 种的数据,为每个数据添加指令。通过Object,defineProperty() 来通知属性是否更改

      (2) 找到每个DOM节点的指令。绑定事件。并绑定watcher

      (3)  实现DOM事件改变之后, 响应data数据,实现视图更新

    <!DocType>
    <html>
      <title>vue 的双向绑定事件</title>
      <body id="app">
        <input type="text" v-model="number"/>
        <span v-bind="number"></span>
        <input type="text" v-model="age"/>
        <span v-bind="age"></span>
      </body>
       
    <script>
      function Vue (options) {
         this._init(options);
      }
      
      Vue.prototype._init = function (options) {
        this.$data = options.data;
        this.$methods = options.data.methods;
        this.$el = document.querySelector(options.el);
        this.$methods = options.methods;
        this.$key = '';
        
        this._binding = {};
        
        // 观测数据
        this._observer(this.$data);
        
        this._complie(this.$el);
        
        // this._test(this.$data);
      }
      
      // 观测数据
      Vue.prototype._observer = function (obj) {
        var value;
        let _this = this;
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            this._binding[key] = {                                                                                                                                                          
              _directives: []
            };
            value = obj[key];
            if (typeof value === 'object') {
              this._observer(value);
            }
            Object.defineProperty(this.$data, key, {
              enumerable: true,
              configurable: true,
              get: function () {
                console.log(`获取${value}`, key);
                return value;
              },
              set: function (newVal) {
                console.log('key:', key, _this.$key);
                if (value !== newVal) {
                  value = newVal;
                  _this._binding[_this.$key]._directives.forEach(function (item, index) {
                    item.update();
                  })
                }
              }
            })
          }
        }
      }
      
      // 为DOM节点添加指令事件
      Vue.prototype._complie = 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._complie(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._binding[attrVal]._directives.push(new Watcher(
                'input',
                node,
                _this,
                attrVal,
                'value'
              ))
    
              return function() {
                _this.$key = attrVal
                _this.$data[attrVal] =  nodes[key].value;
              }
            })(i));
          } 
    
          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];
      }
      
      
      // 测试
      Vue.prototype._test = function($data) {
         var a = $data.number;
         $data.number = 32;
      }
      
      window.onload = function () {
        var app = new Vue({
          el: '#app',
          data: {
             number: 12,
             age: 444
          },
          methods: {}
        })
      }
    
    </script>
    </html>
  • 相关阅读:
    c#中MessageBox的使用(转)
    ArcGIS中地图配准(转)
    VS2010中VC9.0Runtime与VC10.0Runtime在win7上装不上提示error code 1603(转)
    win7下安装Oracle 10G 的成功方法
    解决win 7 下打不开chm格式文件的办法
    vi 编辑器
    开源赚钱思路
    如何开始linux 编程
    Linux C语言开发基础
    如何做开源项目——ABC
  • 原文地址:https://www.cnblogs.com/yaobolove/p/8885878.html
Copyright © 2011-2022 走看看