实现:

/* * Time: 2021/09/09 * Author: tony_cyt * About: 简要实现Vue */class Vue {  /**   * @description: Vue构造函数   * @param {*} options   * @return {*}   */  constructor(options) {    // 赋值el和data    const { el, data, computed } = options;    this.$el = el;    this.$data = data;    // 如果$el存在,就走编译流程    if (this.$el) {      new Observer(this.$data);      for (const key in computed) {        Object.defineProperty(this.$data, key, {          get: () => {            return computed[key].call(this);          }        })      }      this.proxyData(this.$data);      new Compiler(this.$el, this);    }  }  proxyData(data) {    for (const key in data) {      Object.defineProperty(this, key, {        get: () => {          return data[key];        }      })    }  }}/** * @description: 自定义指令集 * @param {*} * @return {*} */CompilerUtil = {  getVal(vm, expr) {    return expr.split('.').reduce((data, current) => {      return data[current];    }, vm.$data)  },  setVal(vm, expr, value) {    expr.split('.').reduce((data, current, index, arr) => {      if (index === arr.length - 1) {        return data[current] = value;      }      return data[current];    }, vm.$data);  },  model(node, expr, vm) {    const value = this.getVal(vm, expr);    const fn = this.updater['modeUpdater'];    new Watcher(vm, expr, newValue => {      fn(node, newValue);    })    node.addEventListener('input', e => {      this.setVal(vm, expr, e.target.value);    })    fn(node, value);  },  getContentValue(vm, expr) {    return expr.replace(/\{\{(.+?)\}\}/g, (...args) => {      return this.getVal(vm, args[1]);    })  },  text(node, expr, vm) {    const fn = this.updater['textUpdater'];    const content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {      new Watcher(vm, args[1], () => {        fn(node, this.getContentValue(vm, expr));      })      return this.getVal(vm, args[1]);    })    fn(node, content);  },  html() {  },  updater: {    modeUpdater(node, value) {      node.value = value;    },    textUpdater(node, value) {      node.textContent = value;    },    htmlUpdater() {    }  }}class Compiler {  /**   * @description: 编译器构造函数   * @param {*} el   * @param {*} vm   * @return {*}   */  constructor(el, vm) {    this.vm = vm;    this.el = this.isElementNode(el) ? el : document.querySelector(el);    const fragment = this.nodeToFragment(this.el);    this.compile(fragment);    this.el.append(fragment);  }  /**   * @description: 判断是不是元素节点   * @param {*} node   * @return {*}   */  isElementNode(node) {    return node.nodeType === 1;  }  /**   * @description: 生成文档片段   * @param {*} node   * @return {*}   */  nodeToFragment(node) {    const fragment = document.createDocumentFragment();    let firstChild;    while (firstChild = node.firstChild) {      fragment.append(firstChild);    }    return fragment;  }  /**   * @description: 依据类型编译   * @param {*} node   * @return {*}   */  compile(node) {    [...node.childNodes].forEach(child => {      if (this.isElementNode(child)) {        this.compileElement(child);        // 递归节点遍历        this.compile(child);      } else {        this.compileText(child);      }    })  }  /**   * @description: 编译文本节点   * @param {*} node   * @return {*}   */  compileText(node) {    const reg = /\{\{(.+?)\}\}/;    if (reg.test(node.textContent)) {      CompilerUtil['text'](node, node.textContent, this.vm);    }  }  /**   * @description: 判断是否是指令   * @param {*} attrName   * @return {*}   */  isDirective(attrName) {    return attrName.startsWith('v-');  }  /**   * @description: 编译元素节点   * @param {*} node   * @return {*}   */  compileElement(node) {    [...node.attributes].forEach(attr => {      const { name, value: expr } = attr;      if (this.isDirective(name)) {        const [, directive] = name.split('-');        CompilerUtil[directive](node, expr, this.vm);      }    })  }}/** * @description: 实现数据响应式 * @param {*} * @return {*} */class Observer {  constructor(data) {    this.observer(data);  }  observer(data) {    if (data && typeof data === 'object') {      for (const key in data) {        this.defineReactive(data, key, data[key]);      }    }  }  defineReactive(obj, key, value) {    this.observer(value);    const dep = new Dep();    Object.defineProperty(obj, key, {      get() {        Dep.target && dep.subs.push(Dep.target);        return value;      },      set: (newValue) => {        if (newValue !== value) {          this.observer(newValue);          value = newValue;          dep.notify();        }      }    })  }}/** * @description: 存储观察者 * @param {*} * @return {*} */class Dep {  constructor() {    this.subs = [];  }  addSub(watcher) {    this.subs.push[watcher];  }  notify() {    this.subs.forEach(watcher => {      watcher.update();    })  }}class Watcher {  constructor(vm, expr, cb) {    this.vm = vm;    this.expr = expr;    this.cb = cb;    this.oldValue = this.get();  }  get() {    Dep.target = this;    const value = CompilerUtil.getVal(this.vm, this.expr);    Dep.target = null;    return value;  }  update() {    const newValue = CompilerUtil.getVal(this.vm, this.expr);    if (newValue !== this.oldValue) {      this.cb(newValue);    }  }}

应用:

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>MVVM</title></head><body>  <div id="app">    <input type="text" v-model='school.name'>    <div>{{school.name}}</div>    <div>{{school.age}}</div>    {{newName}}    <ul>      <li>1</li>      <li>2</li>    </ul>  </div>  <script src="VueMVVM.js"></script>  <script>    let vm = new Vue({      el: "#app",      data: {        school: {          name: "qinghua",          age: 20        }      },      computed: {        newName() {          return this.school.name + 'new'        }      }    })  </script></body></html>

成果: