vue2手写实现双向数据绑定(面试)

!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>
  </head>
  <body>
    <div id="app">
      <input v-model="text" type="text" />
      {{text}}
    </div>
  </body>
  <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
  <script>
    //observer  数据的劫持
    // wathcer 依赖侦听器
    // dep  依赖收集
    // compile 编译dom
    class Vue {
      constructor(options) {
        //将new Vue 的参数传入
        this.$options = options; // {el: '#app', data: {…}}
        new Observer(this.$options.data);
        //把跟节点传入
        let dom = document.querySelector(this.$options.el);
        const f = new Compile(dom, this.$options.data);
        dom.append(f);
      }
    }
    class Observer {
      //接收一个data
      constructor(data) {
        this.walk(data);
      }
      walk(data) {
        //数据劫持
        Object.keys(data).forEach((key) =>
          this.defineReactive(data, key, data[key])
        );
      }
      defineReactive(data, key, value) {
        let dep = new Dep();
        Object.defineProperty(data, key, {
          get() {
            //添加watcher 实例
            if (Dep.target) {
              //   console.log(Dep.target);
              dep.addSub(Dep.target);
            }
            return value;
          },
          set(newValue) {
            value = newValue;
            dep.notify();
          },
        });
      }
    }
    class Dep {
      constructor() {
        this.sub = [];
      }
      addSub(wathcer) {
        //push watcher的实例子
        this.sub.push(wathcer);
      }
      notify() {
        this.sub.forEach((wathcer) => wathcer.update()); //更新值
      }
    }
    class Wathcer {
      constructor(vm, node, name) {
        Dep.target = this;
        this.vm = vm;
        this.node = node;
        this.name = name;
        this.update(); //初始化赋默认值
        Dep.target = null; //销毁防止重复添加
      }
      get() {
        //取值
        this.value = this.vm[this.name];
      }
      update() {
        this.get();
        this.node.nodeValue = this.value; //更新花括号内容
      }
    }
    class Compile {
      //一个node 一个vm
      constructor(node, vm) {
        this.node = node;
        this.vm = vm;
        return this.nodeList(this.node); // 不知道为啥return
      }
      parse(node, vm) {
        let reg = /\{\{(.*)\}\}/;
        //nodeType node 节点13种
        if (node.nodeType === 1) {
          //获取v-model
          //转为真数组
          //   console.log(Array.from(node.attributes));
          Array.from(node.attributes).forEach((v) => {
            if (v.nodeName === "v-model") {
              //添加input事件 更新nodeValue
              node.addEventListener("input", (e) => {
                // v.nodeValue 是 data下的text
                vm[v.nodeValue] = e.target.value;
              });
              //  node 为 input 框
              node.value = vm[v.nodeValue]; //绑定初始值
            }
          });
        }
        if (node.nodeType === 3) {
          //要收集依赖 监听 更新节点
          //   console.log(node);
          if (reg.test(node.nodeValue)) {
            //vm[RegExp.$1] 为 data中的 ‘text’
            // console.log(vm[RegExp.$1]);
            node.nodeValue = vm[RegExp.$1];
            //收集依赖
            new Wathcer(vm, node, RegExp.$1);
          }
          //处理花括号
        }
      }
      nodeList(node) {
        //创建文档碎片
        let f = document.createDocumentFragment();
        let child;
        //传入根元素节点
        while ((child = node.firstChild)) {
          this.parse(child, this.vm);
          f.appendChild(child); //不添加文档碎片会死循环
        }
        return f;
      }
    }
    new Vue({
      el: "#app",
      data: {
        text: "text",
      },
    });
  </script>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值