简单的看一下computed
先看看computed的用法
var vm = new Vue({
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
这里我只考虑计算属性是function的情况,还有一种get,set的我暂时不考虑。
被设置为computed的这个值fullName,依赖两个变量:this. firstName
,this.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