vue简版原理

13 篇文章 0 订阅

html:

 <div id="app">
    <p>{{opt}}</p>
    <p>{{arr}}</p>
    <p v-text="a"></p>
    <p v-html="des"></p>
    <p><input type="text" v-model="a"></p>
  </div>

vue构造函数

   class Vue {
      constructor(options) {

        this.$options = options;
        this.$data = options.data;

        // 数据拦截
        new Observer(this.$data)

        // 代理实例
        proxy(this, this.$data)

        // 模板编译
        new Complier(this, this.$options.el);

      }
    }

 // 将data数据,代理到Vue实例上
    function proxy(vm, data) {
      Object.keys(data).forEach(key => {
        Object.defineProperty(vm, key, {
          get() {
            return data[key]
          },
          set(newVal) {
            data[key] = newVal;
          }
        });
      });
    }

observe.js数据拦截

  class Observer {

      constructor(data) {
        this.init(data)
      }

      init(data) {

        if (typeof data !== "object" || data == null) {
          return false
        }

        if (Array.isArray(data)) { // 数组
          this.walkArr(data);
          Object.defineProperty(data, '__dep__', {
            enumerable: false,
            value: new Dep()
          });
        } else { // 对象, 更新触发set
          this.walk(data)
        }
      }

      walk(data) {
        Object.keys(data).forEach(key => {
          this.walkDefinedReactive(data, key, data[key])
        });
      }

      walkDefinedReactive(data, key, val) {
        new Observer(val);

        let _this = this;

        // 一个key对应一个dep收集器
        let dep = new Dep();

        Object.defineProperty(data, key, {
          get() {
            Dep.target && dep.add(Dep.target);
            if (val.__dep__) { // 说明该值是数组, 则把使用该数组的watcher数据到该数组__dep__上
              Dep.target && val.__dep__.add(Dep.target);
            }
            return val
          },
          set(newVal) {
            if (newVal != val) {
              val = newVal;
              _this.init(val); //如果新值时对象
              dep.notify(); //所有对象的值发生改变(包含数组=> 整个值变化,不是值内部变化, 所以当数组内值变化push..等发生时,采用拦截数据方法内触发)
            }
          }
        })
      }

      walkArr(data) {

        const arrayProto = Object.create(Array.prototype); //创建新对象

        // 装饰一下方法
        const methodList = ['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'];
        methodList.forEach(methods => {
          arrayProto[methods] = function (...arg) {
            Array.prototype[methods].apply(this, arg);

            // 向数组插入值
            let inserted = null;
            switch (methods) {
              case 'push':
              case 'unshift':
                inserted = arg;
                break;

              case 'splice':
                inserted = arg.splice(2);
                break;
            };

            // Todo 。。。。

            data.__dep__.notify();
          }
        });

        // 设置数据的原型
        Object.setPrototypeOf(data, arrayProto);

        // 数据每项数据响应
        console.log(data);
        data.forEach(val => new Observer(val));

      }
    };

complier.js编译模板

 class Complier {

      constructor(vm, el) {
        this.$vm = vm;
        this.$el = document.querySelector(el);
        this.$el && this.complier(this.$el);
      }

      complier(node) {
        const childNodes = Array.from(node.childNodes);

        childNodes.forEach(node => {

          if (node.childNodes.length > 0) {
            this.complier(node)
          }

          // 文本
          if (this.isTextNode(node)) {
            let key = RegExp.$1;
            this.complierText(node, key)
          }

          // 元素
          if (this.isElementNode(node)) {
            const attrs = Array.from(node.attributes);
            attrs.forEach(att => {
              const { name, value: key } = att;
              if (name.startsWith('v-')) {
                const dir = name.slice(2);
                this.updater(node, key, dir);

                if (name == 'v-model') {
                  node.addEventListener('input', () => {
                    this.$vm[key] = node.value;
                  })
                }
              }
            });
          }
        });
      }


      // 所有使用到数据的地方,统一使用updater更新,主要这样方便在这里统一收集所有watcher
      // 每一个使用到key值的节点node,都对应一个watcher,该watcher内记录了使用的节点,和被使用使用的key(值更新时,this.$vm[key]可以获取到最新的值)
      updater(node, key, dir) {
        let fn = this[dir + 'Updater'];
        Dep.target = new Watcher(() => {
          fn && fn(node, this.$vm[key]);
        });
        fn && fn(node, this.$vm[key]);
        Dep.target = null;
      }

      textUpdater(node, val) {
        node.textContent = JSON.stringify(val);
      }

      htmlUpdater(node, val) {
        node.innerHTML = JSON.stringify(val);
      }

      modelUpdater(node, val) {
        node.value = val;
      }

      complierText(node, key) {
        // node.nodeValue = this.$vm[key];
        this.updater(node, key, 'text')
      }

      isElementNode(node) {
        return node.nodeType == 1
      }

      isTextNode(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent)
      }

    };

Dep.js 观察者模式

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

      add(arg) {
        this.collectArr.push(arg)
      }

      notify() {
        this.collectArr.forEach(w => w.updater())
      }
    }

watcher.js  观察者


    class Watcher {
      constructor(callBack) {
        this.callBack = callBack;
      }

      updater() {
        this.callBack();
      }
    }

    let app = new Vue({
      el: '#app',
      data: {
        opt: {
          age: 10
        },
        arr: [1, 2, 3, 4],
        a: 150,
        des: '<em>em</em>'
      }
    });


    setTimeout(() => {
      app.arr.push(100);
    }, 1000)
    setTimeout(() => {
      app.arr.pop();
    }, 2000)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值