MVVM小demo完整代码

351 阅读1分钟

本文仅用于记录MVVM小demo的完整代码,关于MVVM的实现原理及分析请参考另一篇文章MVVM源码解析

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>我自己个的vue - mvvm</title>
  <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> -->
</head>
<body>
  <div id="app">
    <input type="text" v-model="name">
    <h2>{{name}}</h2>
  </div>
 </body>
</html>
// 双向绑定
class Dep {
  constructor() {
    this.subs = []
  }

  addSubs(sub) {
    this.subs.push(sub)
  }
  notify() {
    this.subs.forEach(item => {
      item.update();
    })
  }
}

class Watcher {
  constructor(node, key, vm) {
    Dep.target = this;
    this.node = node;
    this.key = key;
    this.vm = vm
    this.getValue();
    Dep.target = null;
  }
  update() {
    this.getValue();
    if (this.node.nodeType === 1) {
      this.node.value = this.value;
    } else if (this.node.nodeType === 3) {
      this.node.textContent = this.value;
    }
  }
  getValue() {
    this.value = this.vm.$data[this.key];
  }
}


function Vue(options) {
  this.$data = options.data;
  this.$el = document.querySelector(options.el);

  observe(this.$data)
  nodeTofragment(this.$el, this);
}

const vm = new Vue({
  data: { name: '西瓜watermelon' },
  el: '#app'
});


// 数据劫持
function observe(data) {
  if (({}).toString.call(data) !== '[object Object]') return;
  const keys = Object.keys(data);
  keys.forEach(key => {
    defineReactive$$1(data, key, data[key]);
  })
}
function defineReactive$$1(obj, key, val) {
  let dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) {
        dep.addSubs(Dep.target);
      }
      return val
    },
    set(newV) {
      if (newV !== val) {
        val = newV
        dep.notify();
      }
    }
  });
}

// 模板编译
function nodeTofragment(el, vm) {
  let fragment = document.createDocumentFragment();
  let child;
  while (child = el.firstChild) {
    compiler(child, vm);
    fragment.appendChild(child);
  }
  el.appendChild(fragment)
}

function compiler(node, vm) {
  if (node.nodeType === 1) {//元素节点
    [...node.attributes].forEach(item => {
      if (/^v-/.test(item.nodeName)) {
        new Watcher(node, item.nodeValue, vm)
        node.value = vm.$data[item.nodeValue];
        node.addEventListener('input', () => {
          vm.$data[item.nodeValue] = node.value;
        });
      }
    });

    [...node.childNodes].forEach(item => {
      compiler(item, vm);
    })

  } else if (node.nodeType === 3) {
    if (/\{\{\w+\}\}/.test(node.textContent)) {

      node.textContent = node.textContent.replace(/\{\{(\w+)\}\}/, function (a, b) {
        new Watcher(node, b, vm)
        return vm.$data[b]
      })
    }
  }
}