zoukankan      html  css  js  c++  java
  • vue源码分析(三)>>:computed

    看完watch在看看computed时怎么实现的:

    step1:用法

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>computed</title>
    </head>
    <body>
      <div id="app">
        <p>欢迎:{{userName}}</p>
        <p>欢迎:{{userNameM()}}</p>
        <button @click="handleClick">click</button>
      </div>
    
      <script src="../../dist/vue.js"></script>
      <script>
        let vm = new Vue({
          el:'#app',
          data:{
            user:{
              firstName:'',
              lastName:'三丰'
            },
          },
          methods: {
            handleClick(){
              this.user.firstName = "";
            },
            userNameM(){
              return this.user.firstName + this.user.lastName;
            }
          },
          computed: {
            userName(){
              return this.user.firstName + this.user.lastName;
            }
          },
        })
      </script>
    </body>
    </html>

    可以把计算属性看成一般的属性用,watch里边也能监听到userName的变化;另外用方法也能实现同样的效果,先写上。

    step2:源码实现

    还是从_init开始看起,到initState,在这里在initWatch之前,走initComputed方法,传参第一个参数是:vm,第二个参数是:vm.$options.computed;第一个参数就是当前示例对象,第二个参数就是我们所写的computed对象,看这个方法:

    // 初始化computed
      function initComputed (vm, computed) {
        debugger
        // $flow-disable-line
        var watchers = vm._computedWatchers = Object.create(null);
        // computed properties are just getters during SSR
        var isSSR = isServerRendering();// 看看是不是服务端渲染
    
        for (var key in computed) {
          var userDef = computed[key];
          var getter = typeof userDef === 'function' ? userDef : userDef.get;
          if (getter == null) {
            warn(
              ("Getter is missing for computed property "" + key + ""."),
              vm
            );
          }
    
          if (!isSSR) {
            // create internal watcher for the computed property. 
            // 定义watcher
            watchers[key] = new Watcher(
              vm,
              getter || noop,
              noop,
              computedWatcherOptions
            );
          }
    
          // component-defined computed properties are already defined on the
          // component prototype. We only need to define computed properties defined
          // at instantiation here.
          if (!(key in vm)) {// 如果key和其它方法或者属性冲突就抛异常
            defineComputed(vm, key, userDef);
          } else {
            if (key in vm.$data) {
              warn(("The computed property "" + key + "" is already defined in data."), vm);
            } else if (vm.$options.props && key in vm.$options.props) {
              warn(("The computed property "" + key + "" is already defined as a prop."), vm);
            }
          }
        }
      }

    然后创建Watcher,这和watch时候的一样了就,下边走defineComputed:就是创建它的getter

      function defineComputed (
        target,
        key,
        userDef
      ) {
        var shouldCache = !isServerRendering();
        if (typeof userDef === 'function') {
          sharedPropertyDefinition.get = shouldCache
            ? createComputedGetter(key)// 走这里
            : createGetterInvoker(userDef);
          sharedPropertyDefinition.set = noop;
        } else {
          sharedPropertyDefinition.get = userDef.get
            ? shouldCache && userDef.cache !== false
              ? createComputedGetter(key)
              : createGetterInvoker(userDef.get)
            : noop;
          sharedPropertyDefinition.set = userDef.set || noop;
        }
        if (sharedPropertyDefinition.set === noop) {
          sharedPropertyDefinition.set = function () {
            warn(
              ("Computed property "" + key + "" was assigned to but it has no setter."),
              this
            );
          };
        }
        Object.defineProperty(target, key, sharedPropertyDefinition);
      }
    
      function createComputedGetter (key) {
        debugger
        return function computedGetter () {
          var watcher = this._computedWatchers && this._computedWatchers[key];
          if (watcher) {
            if (watcher.dirty) {
              watcher.evaluate();
            }
            if (Dep.target) {
              watcher.depend();
            }
            return watcher.value
          }
        }
      }
    
      function createGetterInvoker(fn) {
        return function computedGetter () {
          return fn.call(this, this)
        }
      }

    至此:初始化工作就完成了。

    step3:触发逻辑

    当我们获取userName时候就会触发他的get方法就是上面 computedGetter方法,debugger已经打上了,debug走一下:

      function createComputedGetter (key) {
        return function computedGetter () {
          debugger
          var watcher = this._computedWatchers && this._computedWatchers[key];// 获取watcher对象
          if (watcher) {
            if (watcher.dirty) {// 如果dirty为true
              watcher.evaluate();//获取数据
            }// 否则就直接返回
            if (Dep.target) {
              watcher.depend();
            }
            return watcher.value
          }
        }
      }

    断点看到,界面上渲染时候获取也就是第一次获取,这个dirty是true就去获取了,我第二次获取时候,dirty是false直接返回的watcher.value,接下来看看watcher.evaluate()方法:

      /**
       * Evaluate the value of the watcher.
       * This only gets called for lazy watchers.
       */
      Watcher.prototype.evaluate = function evaluate () {
        console.log("Watcher evaluate");
        this.value = this.get();// 获取值 赋值给当前watcher
        this.dirty = false;// 把当前dirty设为false
      };
    
      /**
       * Evaluate the getter, and re-collect dependencies.
       */
      Watcher.prototype.get = function get () {
        debugger
        console.log("Watcher get");
        pushTarget(this);
        var value;
        var vm = this.vm;
        try {
          value = this.getter.call(vm, vm);// 走getter方法获取值
        } catch (e) {
          if (this.user) {
            handleError(e, vm, ("getter for watcher "" + (this.expression) + """));
          } else {
            throw e
          }
        } finally {
          // "touch" every property so they are all tracked as
          // dependencies for deep watching
          if (this.deep) {// 如果是深度监听
            traverse(value);
          }
          popTarget(); // 对应 pushTarget(this);
          this.cleanupDeps();
        }
        return value
      };

    现在分析下这个dirty,当user里边的firstName或lastName改变时候会触发watcher的update方法,会改变这个dirty为true,在随后获取时候就不会直接返回wather.value了;这里就体现出和用方法实现起来的区别了,区别就是这个computed会缓存,这个dirty逻辑就是缓存。

    暂时就这样

    over!

  • 相关阅读:
    P2910 [USACO08OPEN]寻宝之路Clear And Present Danger 洛谷
    P2212 [USACO14MAR]浇地Watering the Fields 洛谷
    Python字体颜色设置
    Python小游戏 -- 猜数字
    数据结构 -- 链表&双向链表
    数据结构 -- 队列 & 循环队列 -- 数组实现
    数据结构 -- 栈的数组实现法
    洛谷P1036 选数
    如何让c语言使用结构体近似模拟c++中的类
    对c语言回调函数的理解
  • 原文地址:https://www.cnblogs.com/rainbowLover/p/13574271.html
Copyright © 2011-2022 走看看