第九节-简单实现v-if&&v-show&&生命周期

89 阅读1分钟

简单实现v-if&&v-show&&生命周期

  • 这里的事件我默认用的是click事件了
var Vue = (function() {
  function Vue(options) {
    // 生命周期
    var recycles = {
      beforeCreate: options.beforeCreate,
      created: options.created,
      beforeMount: options.beforeMount,
      mounted: options.mounted
    }

    // 一开始执行
    recycles.beforeCreate();

    this.$el = document.querySelector(options.el);
    this.$data = options.data();
    // 初始化
    this._init(this, options.template, options.methods, recycles);
  }

  Vue.prototype._init = function(vm, template, methods, recycles) {
    recycles.created();

    var container = document.createElement('div');
    container.innerHTML = template

    var showPool = new Map();
    var eventPool = new Map();
    // 1.数据劫持
    initData(vm, showPool);
    initPool(container, methods, showPool, eventPool);
    // 绑定事件处理函数
    bindEvent(vm, eventPool);
    // 渲染
    render(vm, showPool, container, recycles);
  }

  function initData(vm, showPool) {
    var _data = vm.$data;
    for(var key in _data) {
      (function(key) {
        Object.defineProperty(vm, key, {
          get: function() {
            return _data[key];
          },
          set: function(newVal) {
            _data[key] = newVal;
            // 更新
            update(vm, key, showPool);
          }
        })
      })(key);
    }
  }

  function initPool(container, methods, showPool, eventPool) {
    // 获取所有的节点
    var _allNodes = container.getElementsByTagName("*");
    var dom = null;

    for(var i = 0; i < _allNodes.length; i++) {
      dom = _allNodes[i];

      var vIfData = dom.getAttribute('v-if');
      var vShowData = dom.getAttribute('v-show');
      var vEvent = dom.getAttribute('v-on:click') || dom.getAttribute('@click');

      if (vIfData) {
        showPool.set(
          dom,
          {
            type: 'if',
            prop: vIfData
          }
        )
        // 删除属性
        dom.removeAttrbute('v-if');
      } else if (vShowData) {
        showPool.set(
          dom,
          {
            type: 'show',
            prop: vShowData
          }
        )
        dom.removeAttrbute('v-show');
      }

      if(vEvent) {
        eventPool.set(
          dom,
          methods[vEvent]
        );
        dom.removeAttrbute('@click');
        dom.removeAttrbute('v-on:click');
      }
    }
  }

  function bindEvent(vm, eventPool) {
    for(var [ dom, handler ] of eventPool) {
      // 注意: methods上所有的方法都要放到实例上面去的
      vm[handler.name] = handler;
      dom.addEventListener('click', vm[handler.name].bind(vm), false);
    }
  }

  function render(vm, showPool, container, recycles) {
    var _data = vm.$data;
    var _el = vm.$el;

    for(var [dom, info] of showPool) {
      switch(info.type) {
        case 'if':
          info.comment = document.createComment(['v-if']);
          !_data[info.prop] && dom.parentNode.replaceChild(info.comment, dom);
          break;
        case 'show':
          !_data[info.prop] && (dom.style.display = "none");
          break;
        default:
          break;
      }
    }
    // 挂载之前执行
    recycles.beforeMount();
    _el.appendChild(container);
    // 挂载完成后执行
    recycles.mounted();
  }

  // 更新数据
  function update(vm, key, showPool) {
    var _data = vm.$data;

    for(var [dom, info] of showPool) {
      if(info.prop === key) {
        switch(info.type) {
          case 'if':
            !_data[key] ? dom.parentNode.replaceChild(info.comment, dom)
                        : info.comment.parentNode.replaceChild(dom, info.comment);
            break;
          case 'show':
            !_data[key] ? (dom.style.display = "none") : (dom.removeAttrbute('style'));
            break;
          default:
            break;
        }
      }
    }
  }

  return Vue;
})();