实现mini Vue

本文深入解析Vue框架的数据响应原理,包括Observer实现数据劫持,Dep进行依赖收集,Watcher监听数据变化以及Compile处理模板并初始化。通过实例展示了如何在输入框中使用v-model指令实时更新视图,同时解释了数据变更时如何触发页面更新的过程。
摘要由CSDN通过智能技术生成

Observer进行数据劫持,Dep进行依赖收集,Watcher进行页面数据变化的监听,Compile对不同结点进行处理,初始化模板,更新模板等

<!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>Document</title>
    <style type="text/css"></style>
  </head>
  <body>
    <div id="app">
      <input type="text" v-model="text" />
      {{text}}
    </div>

    <script>
      class Vue {
        constructor(options) {
          this.$options = options;
          // 对数据进行劫持
          new Observer(this.$options.data);
          // console.log(this.$options.data);

          let dom = document.querySelector(this.$options.el);
          // 返回新的经过parse的文档碎片
          const f = new Compile(dom, this.$options.data);
          // 将f再添加到dom中
          dom.append(f);
        }
      }

      class Observer {
        constructor(data) {
          this.walk(data);
        }

        walk(data) {
          Object.keys(data).forEach((key) => {
            let value = data[key];
            if (typeof value === "object") {
              walk(value);
            } else {
              this.defineReactive(data, key, value);
            }
          });
        }

        defineReactive(data, key, value) {
          let dep = new Dep();
          Object.defineProperty(data, key, {
            get() {
              // 这里进行依赖收集,由于这儿获取不到node结点,只能在Dep上添加一个对象指向watcher自身
              if (Dep.target) {
                dep.addSub(Dep.target);
              }
              return value;
            },
            set(newVal) {
              if (value === newVal) return;
              value = newVal;
              dep.notify();
            },
          });
        }
      }

      class Dep {
        constructor() {
          this.subs = [];
        }

        addSub(watcher) {
          this.subs.push(watcher);
        }

        // 通知每个watcher进行更新
        notify() {
          this.subs.forEach((watcher) => {
            watcher.update();
          });
        }
      }

      class Watcher {
        constructor(vm, key, callback) {
          Dep.target = this;
          // console.log("watch--------");
          this.vm = vm;
          this.key = key;
          this.callback = callback;
          this.oldVal = "";
          this.update();
          Dep.target = null;
        }

        getNewVal() {
          // 监听Data里的值
          return this.vm[this.key];
        }

        update() {
          let newVal = this.getNewVal();
          if (this.oldVal !== newVal) {
            // console.log(newVal, this.oldVal);
            this.callback(newVal);
            this.oldVal = newVal;
          }
        }
      }

      class Compile {
        constructor(node, vm) {
          this.node = node;
          // vm就是option里的data
          this.vm = vm;
          return this.nodeList(this.node);
        }

        // 对结点进行处理
        parse(node, vm) {
          // console.log(node); // 这里有三个元素:换行/n,input标签,文本数据{text}

          let reg = /\{\{(.*)\}\}/; // 正则获取文本数据{text}里的内容

          // 这里node就是input标签
          if (node.nodeType === 1) {
            Array.from(node.attributes).forEach((v) => {
              // v是input标签上的属性
              if (v.nodeName === "v-model" && node.nodeName === "INPUT") {
                // // 初始化input里的文本值
                node.value = vm[v.nodeValue];
                // 绑定事件监听文本框里值的变化
                node.addEventListener("input", (e) => {
                  // 这里的v.nodeValue就是v-model绑定的text,也就是对options里的data进行修改
                  vm[v.nodeValue] = e.target.value;
                  console.log(vm);
                });
              }
            });
          }

          // 获取{text}文本
          if (node.nodeType === 3) {
            if (reg.test(node.nodeValue)) {
              // 初始化和更新时添加一个监视器,并指定回调函数
              new Watcher(vm, RegExp.$1, (newVal) => {
                node.nodeValue = newVal;
              });
            }
          }
        }

        // 遍历nodeList对每个结点进行处理
        nodeList(node) {
          let f = document.createDocumentFragment();
          let child;
          // 每次文档碎片进行添加时会移除node里的一个子节点,这样不会无限循环
          while ((child = node.firstChild)) {
            this.parse(child, this.vm);
            f.append(child);
          }
          return f;
        }
      }

      let vue = new Vue({
        el: "#app",
        data: {
          text: "hello Vue",
        },
      });
    </script>
  </body>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值