Vue——实现一个的简单Vue框架

VUE对象的创建

class Vue {
  constructor(options) {
    this.$el = document.querySelector(options.el); //DOM树
    this.$options = options; //传入的options
    this.$watchEvent = {}; //收集事件的对象

    //beforeCreate周期
    if (typeof this.$options.beforeCreate == "function") {
      this.$options.beforeCreate.call(this);
    }

    this.proxyData(); //代理数据
    this.proxyMethods(); //代理方法

    //created周期
    if (typeof this.$options.created == "function") {
      this.$options.created.call(this);
    }

    //beforeMount周期
    if (typeof this.$options.beforeMount == "function") {
      this.$options.beforeMount.call(this);
    }

    this.compile(this.$el); //编译模板

    //mounted周期
    if (typeof this.$options.mounted == "function") {
      this.$options.mounted.call(this);
    }
  }
  proxyData() {
    //该函数首先用于代理数据,使得this.xxx等效于this.$options.xxx
    //并且使用Object.defineProperty来监听数据变化,一旦数据发生变化
    //就根据数据的key值调用vue对象中$watcher中相对应的事件
    for (const key in this.$options.data) {
      let value = this.$options.data[key];
      Object.defineProperty(this, key, {
        configurable: false,
        enumerable: true,
        get() {
          return value;
        },
        set(val) {
          value = val;
          if (this.$watchEvent[key]) {
            this.$watchEvent[key].forEach((item, i) => {
              item.update(this);
            });
          }
        },
      });
    }
  }
  compile(DOMnode) {
    //该函数根据传入的DOM节点来编译(解析v-html、@click、v-model等),并递归
    DOMnode.childNodes.forEach((node, index) => {
      if (node.nodeType == 3) {
        //文本节点
        let value = node.textContent;
        let reg = /\{\{(.*?)\}\}/;
        node.textContent = value.replace(reg, (match, val) => {
          let watcher = new Watcher(this, val, node, "textContent");
          if (this.$watchEvent[val]) {
            this.$watchEvent[val].push(watcher);
          } else {
            this.$watchEvent[val] = [];
            this.$watchEvent[val].push(watcher);
          }
          return this[val];
        });
      } else if (node.nodeType == 1) {
        //元素节点
        var VueEvents = ["v-html", "v-model"];
        VueEvents.forEach((event, i) => {
          if (node.hasAttribute(event)) {
            var vmKey = node.getAttribute(event).trim();
            var watcher;
            switch (event) {
              case "v-html":
                watcher = new Watcher(this, vmKey, node, "innerHTML");
                node.innerHTML = this[vmKey];
                break;
              case "v-model":
                if (this.hasOwnProperty(vmKey)) {
                  watcher = new Watcher(this, vmKey, node, "value");
                  node.value = this[vmKey];
                  node.addEventListener("input", (e) => {
                    this[vmKey] = e.target.value;
                  });
                }
                break;
            }
            if (this.$watchEvent[vmKey]) {
              this.$watchEvent[vmKey].push(watcher);
            } else {
              this.$watchEvent[vmKey] = [];
              this.$watchEvent[vmKey].push(watcher);
            }
            if (node.hasAttribute("@click")) {
              node.addEventListener("click", (e) => {
                this[node.getAttribute("@click")].call(this, e);
              });
            }
            node.removeAttribute(event);
          }
          if (node.childNodes.length != 0) {
            this.compile(node);
          }
        });
      }
    });
  }
  proxyMethods() {
    //该函数用于代理方法,使得this.xxx()等效于this.$options.xxx()
    for (const key in this.$options.methods) {
      let method = this.$options.methods[key];
      this[key] = method;
    }
  }
}

//该类用于创建监听器,根据不同的事件名称,来对dom节点执行不同的操作
class Watcher {
  constructor(vm, key, node, attr) {
    this.vm = vm;
    this.key = key;
    this.node = node;
    this.attr = attr;
  }
  update(vm) {
    //beforeUpdate周期
    if (typeof vm.$options.beforeUpdate == "function") {
      vm.$options.beforeUpdate.call(this);
    }
    this.node[this.attr] = this.vm[this.key];
    //updated周期
    if (typeof vm.$options.updated == "function") {
      vm.$options.updated.call(this);
    }
  }
}

在模板中使用VUE对象

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <p>{{value}}</p>
      <input type="text" v-model="value" />
      <p v-html="value"></p>
      <button @click="changeValue">changeValue</button>
    </div>
    <script src="./myVue.js"></script>
    <script>
      let vm = new Vue({
        el: "#app",
        data: {
          value: "",
        },
        beforeCreate() {
          console.log(`开始渲染数据,value=${this.value}`);
        },
        created() {
          this.changeValue();
          console.log(`数据渲染完毕,value=${this.value}`);
        },
        beforeMount() {
          console.log(`开始编译dom`);
        },
        mounted() {
          console.log(`dom编译完毕`);
        },
        beforeUpdate() {
          console.log("马上要更新dom啦");
        },
        updated() {
          console.log("dom更新完啦");
        },
        methods: {
          changeValue() {
            this.value = "321";
          },
        },
      });
    </script>
  </body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的前端小黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值