简单的看一下computed

2021-03-20  本文已影响0人  HelenYin

先看看computed的用法

var vm = new Vue({
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

这里我只考虑计算属性是function的情况,还有一种get,set的我暂时不考虑。
被设置为computed的这个值fullName,依赖两个变量:this. firstNamethis.lastName。这两个变量有其中一个变化,那么fullName就随之改变。
这很容易就联想到,当我们一个被响应式监听的data变量被赋值后,notify,然后更新依赖这个data变量的watcher。那么这里的computed也是这个原理,我们把每个在computed里声明过得方法,放到watcher里,每当有notify,那么就更新watcher。

export function initState (vm) {
+ const { data, props, computed } = vm.$options;
  props && initProps(vm, props);
  data && initData(vm);
+ computed && initComputed(vm, computed);
}
function initComputed (vm, computed) {
  for (const key in computed) {
    const getter = computed[key];
    new Watcher(vm, getter);
  }
}

当我们在访问计算属性的时候需要返回相应的值

function initComputed (vm, computed) {
  for (const key in computed) {
    const getter = computed[key];
    new Watcher(vm, getter);
+    if (!(key in vm)) {
+      defineComputed(vm, key, getter);
+    } else {
+      console.warn('计算属性与与data中的属性命名冲突');
+    }
  }
}
function defineComputed (vm, key, get) {
  Object.defineProperty(vm, key, { get });
}

验证一下:

export const Hello = {
  name: 'Hello',
  data() {
    return {
      firstName: 'Foo',
      lastName: 'Bar',
    }
  },
  computed: {
    fullName: function () {
      return `${this.firstName} ${this.lastName}`;
    },
  },
  mounted() {
    setTimeout(() => {
      this.firstName = 'helen';
    }, 1000);
  },
  render: function(h) {
    return h('div', {}, this.fullName);
  },
}

现在看来好像没啥问题
但是渲染watcher和computedwatcher有点差异
渲染watcher因为需要直接执行render函数,所以需要立即执行,computed在new Watcher的时候其实不需要直接执行,在真实执行渲染Watcher的时候会触发getter函数,这个时候的computed属性收集的依赖是渲染watcher,在执行computed的函数时,会触发data里的变量的getter函数,这个时候,data里变量收集的依赖就是computed watcher。
这里给watcher多加一个option,用来让watcher 知道是computed watcher还是渲染 watcher

function initComputed (vm, computed) {
  for (const key in computed) {
    const getter = computed[key];
+   const computedWatcherOptions = { lazy: true };
    new Watcher(vm, getter, computedWatcherOptions);
    if (!(key in vm)) {
      defineComputed(vm, key, getter);
    } else {
      console.warn('计算属性与与data中的属性命名冲突');
    }
  }
}
class Watcher {
  constructor (
    vm, 
    exp, 
+   options) {
  if (options.lazy) {
    // 这里暂时这样写
    console.log('========');
  } else {
    this.get()
  }
  }
}

官网上说

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

好像没啥问题
https://github.com/TingYinHelen/tempo/src/instance/state.js

上一篇下一篇

猜你喜欢

热点阅读