手记

动手撸起来,ES6实现简单的vue

//模板

<!DOCTYPE html>
<html>

<head>
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
</head>

<body>
  <div id="test">
    <ol>
      <li>v-model:
        <input v-model="msg" placeholder="please...">
      </li>
      <li>v-text:<mark v-text="msg"></mark</li>
      <li >v-show :<mark v-show="isShow">hide or show</mark></li>
      <li>v-on:click:
        <button v-on:click="changeShow"  v-text="btn"></button>
      </li>
    </ol>
  </div>

  <script>
    var vm = new Vue('#test', {
      msg: '',
      isShow: true,
      btn: 'toggle',
      changeShow() {
        vm.$data.isShow = !vm.$data.isShow;
      }
    });
  </script>
</body>

</html>

//逻辑

//declaring directives
var Directives = {
    text(el, value) {
        el.textContent = value || ''
      },
      show(el, value) {
        el.style.display = value ? '' : 'none'
      },
      on: {
        update(el, handler, event, directive) {
          if (!directive.handlers) {
            directive.handlers = {}
          }
          var handlers = directive.handlers
          if (handlers[event]) {
            el.removeEventListener(event, handlers[event])
          }
          if (handler) {
            handler = handler.bind(el);
            el.addEventListener(event, handler);
            handlers[event] = handler;
          }
        }
      },
      model: {
        bind(el, key, directive, seed) {
            el.addEventListener('keyup', () => seed.$data[key] = el.value);
          },
          update(el, value) {
            el.value = value;
          }
      }
  }
  //difine utils
var Utils = {
  cloneAttributes(attributes) {
      return Array.from(attributes).map(attribute => {
        return {
          name: attribute.name,
          value: attribute.value
        }
      })
    },
    parseDirective(attr) {
      if (!attr.name.includes(prefix)) return;
      var noprefix = attr.name.slice(prefix.length + 1),
        argIndex = noprefix.indexOf(':'),
        dirname = argIndex === -1 ? noprefix : noprefix.slice(0, argIndex),
        def = Directives[dirname],
        arg = argIndex === -1 ? null : noprefix.slice(argIndex + 1);
      var exp = attr.value,
        key = exp.trim();
      return def ? {
        attr: attr,
        key: key,
        definition: def,
        argument: arg,
        update: typeof def === 'function' ? def : def.update,
        bind: typeof def === 'function' ? null : def.bind ? def.bind : null
      } : null;
    }
};
//prefix
var prefix = 'v',
  selector = Object.keys(Directives).map(d => `[${prefix}-${d}]`).join();
//constructor
function Vue(el, opts = {}) {
  var root = self.$el = document.querySelector(el),
    els = root.querySelectorAll(selector),
    _bindings = {};
  this.$opts = opts;
  this.$data = {}; //export api
  this.processNode(els, _bindings);
  this.initData(_bindings);
}
//handle nodes
Vue.prototype.processNode = function(els, _bindings) {
    els.forEach(el => {
      Utils.cloneAttributes(el.attributes).forEach(attr => {
        var directive = Utils.parseDirective(attr);
        directive && this.bindDirective(el, _bindings, directive)
      });
    });
  }
  //attribute remove and directive binding
Vue.prototype.bindDirective = function(el, _bindings, directive) {
    var self = this;
    el.removeAttribute(directive.attr.name);
    var key = directive.key,
      binding = _bindings[key];
    if (!binding) {
      _bindings[key] = binding = {
        value: undefined,
        directives: []
      }
    }
    directive.el = el;
    binding.directives.push(directive);

    if (directive.bind) {
      directive.bind(el, key, directive, self);
    }
    if (!self.$data.hasOwnProperty(key)) {
      self.bind(key, binding);
    }
  }
  //set and get
Vue.prototype.bind = function(key, binding) {
  Object.defineProperty(this.$data, key, {
    set: value => {
      binding.value = value;
      binding.directives.forEach(directive => {
        directive.update(
          directive.el,
          value,
          directive.argument,
          directive,
          this
        )
      })
    },
    get: () => binding.value,
  })
};
//initial
Vue.prototype.initData = function(_bindings) {
  for (let variable in _bindings) {
    this.$data[variable] = this.$opts[variable];
  }
};
0人推荐
随时随地看视频
慕课网APP