Skip to content
On this page

miniVue

🕒 Published at:

MiniVue.js

js
// MiniVue 构造函数
function MiniVue(options) {
    this.data = options.data;
  
    this.observeData(this.data);
  
    // 编译模板
    if (options.el) {
      this.compileTemplate(options.el);
    }
  }
  
  // 数据劫持,实现数据的响应式
  MiniVue.prototype.observeData = function(data) {
    var self = this;
  
    Object.keys(data).forEach(function(key) {
      var value = data[key];
      var dep = new Dep();
  
      Object.defineProperty(self.data, key, {
        get: function() {
          if (Dep.target) {
            dep.addSub(Dep.target);
          }
          return value;
        },
        set: function(newValue) {
          if (newValue !== value) {
            value = newValue;
            dep.notify();
          }
        }
      });
    });
  };
  
  // 编译模板
  MiniVue.prototype.compileTemplate = function(el) {
    var self = this;
    var root = document.querySelector(el);
  
    this.compileNode(root);
  
    // 绑定数据更新的回调函数
    Dep.target = function() {
      self.compileNode(root);
    };
  };
  
  // 编译节点
  MiniVue.prototype.compileNode = function(node) {
    var self = this;
  
    if (node.nodeType === 1) { // 元素节点
      var attrs = node.attributes;
      for (var i = 0; i < attrs.length; i++) {
        var attr = attrs[i];
        if (attr.name === 'v-model') {
          var exp = attr.value;
          node.value = this.data[exp];
          node.addEventListener('input', function(event) {
            self.data[exp] = event.target.value;
          });
        }
      }
    } else if (node.nodeType === 3) { // 文本节点
      var reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g;
      var text = node.textContent;
  
      node.textContent = text.replace(reg, function(_, exp) {
        return self.data[exp];
      });
    }
  
    if (node.childNodes && node.childNodes.length > 0) {
      for (var i = 0; i < node.childNodes.length; i++) {
        this.compileNode(node.childNodes[i]);
      }
    }
  };
  
  // 发布订阅模式的订阅者
  function Dep() {
    this.subs = [];
  }
  
  Dep.prototype.addSub = function(sub) {
    this.subs.push(sub);
  };
  
  Dep.prototype.notify = function() {
    this.subs.forEach(function(sub) {
      sub.update();
    });
  };
  
  // 订阅者
  function Watcher(node, vm, exp) {
    this.node = node;
    this.vm = vm;
    this.exp = exp;
  
    Dep.target = this;
    this.update();
    Dep.target = null;
  }
  
  Watcher.prototype.update = function() {
    this.node.textContent = this.vm.data[this.exp];
  };
  
  // 示例用法
  var vm = new MiniVue({
    el: '#app',
    data: {
      message: 'Hello, MiniVue!'
    }
  });

数据劫持,实现数据的响应式

编译模板