Moon系列之Methods、Computed、双向绑定

前言

上一篇文章中了解了整个Moon构造函数的逻辑处理,本篇文章主要是去详细分析Moon是如何处理Methods、computed以及它是如何实现双向绑定的,vue.js是通过ES5的defineProperty来实现的,看看二者有何不同。

具体分析

还是通过简单的实例来分析methods、computed等,具体实例如下:

  <div id="app">
    <p>{{getMsg}}</p>
  </div>
  <script src="./moon.js"></script>
  <script>
    new Moon({
      el: '#app',
      data: {
        msg: 'hello world'
      },
      // 计算属性
      computed: {
        getMsg: {
          get() {
	        let msg = this.get('msg');
	        或者
	        this.$data.msg直接获取
            return `js ${msg}`
          }
        }
      },
      methods: {
        outMsg() {
          console.log(this.$data.msg);
        }
      }
    });

首先明确几点:

  • 在Js中应用data中的数据必须是this.$data.变量的形式或者通过Moon的get方法
  • computed的使用必须写成get、set形式

methods

首先分析Methods,看看Moon是如何处理的,相关代码如下:

	var initMethods = function (instance, methods) {
      var initMethod = function (methodName, method) {
	    // 从此处可以看到2点信息:
	    // 1:methods中定义的方法都会被添加到$data(数据中心)
	    // 2:使用apply改变原先methods中的this
        instance.$data[methodName] = function () {
          return method.apply(instance, arguments);
        };
      };
      // 遍历methods
      for (var method in methods) {
        initMethod(method, methods[method]);
      }
    };

Moon中methods的调用形式有两种:

  1. 直接调用,this.$data.方法
  2. 使用Moon中提供的方式callMethod的形式:Moon实例.callMethod(方法,参数)

computed

计算属性的处理就不会像methods那么简单了,计算属性涉及到双向绑定以及缓存,具体代码如下:

  var initComputed = function (instance, computed) {
      var setComputedProperty = function (prop) {
        // 获取observer对象,观察者
        var observer = instance.$observer;
        // 调用observe方法,为每一个计算属性创建一个clear清除缓存的函数
        observer.observe(prop);
        // 使用Object.defineProperty将计算属性定义到$data中并监听变化
        Object.defineProperty(instance.$data, prop, {
          get: function () {
            var cache = null;
            // 查找缓存中是否存在指定名称的computed,存在就取缓存中的计算属性
            if (observer.cache[prop] === undefined) {
              // 将observer.target就是当前计算属性名称
              observer.target = prop;
              // 调用computed中指定的计算属性,并将结果者赋给cache
              cache = computed[prop].get.call(instance);
              observer.target = null;
              // 保存当前计算结果到oberver.cache对象中
              observer.cache[prop] = cache;
            } else {
              cache = observer.cache[prop];
            }
            return cache;
          },
          set: noop
        });
        var setter = null;
        // computed中的计算属性中set方法都在observer.setter中
        if ((setter = computed[prop].set) !== undefined) {
          observer.setters[prop] = setter;
        }
      };
      // 遍历computed
      for (var propName in computed) {
        setComputedProperty(propName);
      }
    };

从上面中可以得到计算属性也是被添加到$data中,计算属性的缓存是通过Observer中的cache属性来处理

双向绑定

Vue是基于数据劫持 + 发布者-订阅者模式实现双向绑定的,你可以使用this.name = name这样的简单形式实现视图更新。
Moon中并没有提供这种形式,它采用的是提供专门的set和get方法主动去触发$data中的数据的响应式变更。
具体看看set、get方法的实现:

  • set
    Moon.prototype.set = function (key, val) {
      // 获取observer观察者对象
      var observer = this.$observer;
      // resolveKeyPath处理获取指定的属性名,更新$data中的值
      var base = resolveKeyPath(this, this.$data, key, val);
      var setter = null;
      // 检查是否是计算属性,是则调用计算属性的set方法
      if ((setter = observer.setters[base]) !== undefined) {
        setter.call(this, val);
      }
      // 通知,清除包含指定属性的所有计算属性的cache(缓存)
      observer.notify(base, val);
      // 异步队列更新DOM视图
	  // 实际上是重新调用Moon实例的build方法并执行updated生命周期函数
      queueBuild(this);
    };

queueBuid

	var queueBuild = function (instance) {
      if (instance.$queued === false && 
	      instance.$destroyed === false) {
        instance.$queued = true;
        setTimeout(function () {
          // 实例的build方法,更新DOM
          instance.build();
          // updated生命周期函数
          callHook(instance, 'updated');
          instance.$queued = false;
        }, 0);
      }
    };
  • get
	Moon.prototype.get = function (key) {
	  // 获取
      var observer = this.$observer;
      var target = null;
      // observer.target表示计算属性的名称(这里主要是用于计算属性初始化过程中的处理逻辑)
      if ((target = observer.target) !== null) {
	    // 构建map,map中存储每一个属性所在的所有计算属性Map
        if (observer.map[key] === undefined) {
	      // key就是$data中的属性,target就是依赖key的计算属性
          observer.map[key] = [target];
        } else if (observer.map[key].indexOf(target) === -1) {
          observer.map[key].push(target);
        }
      }
      return this.$data[key];
    };

从上面的实现逻辑中可以知道Moon中双向绑定实际的处理如下:

  • 主动调用set方法,实现$data或计算属性的值变化
  • get方法获取 d a t a 中数据,而在该方法中核心点是建立计算属性中的 data中数据,而在该方法中核心点是建立计算属性中的 data中数据,而在该方法中核心点是建立计算属性中的data依赖

只要你在computed中调用 d a t a 中变量,就会在 g e t 中建立 data中变量,就会在get中建立 data中变量,就会在get中建立data与computed之间的Map联系,这就是Moon是如何收集computed的方式。

总结
  • Moon中对于Methods以及Computed的处理,都添加到data数据中心
  • Moon中计算属性采用了Object.defineProperty,普通data中的变量没有采用
  • d a t a 中变量通过 M o o n 实例提供的 s e t 来实现数据响应的,使用 g e t 来获取 data中变量通过Moon实例提供的set来实现数据响应的,使用get来获取 data中变量通过Moon实例提供的set来实现数据响应的,使用get来获取data中的数据。
  • 计算属性中通过get来建立$data中变量与其的联系,使用Observer观察者对象的map来保存
  • 通过set更新 d a t a 中数据之后会重新渲染,再次渲染时拿到的就是更新后的 data中数据之后会重新渲染,再次渲染时拿到的就是更新后的 data中数据之后会重新渲染,再次渲染时拿到的就是更新后的data,即双向绑定
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值