Vue 源码
你想要的 vue 源码分析 · 19
computed 示例 初始化 computed watcher 实例的构造函数 执行 watcher.depend() 执行 watcher.evaluate() 去求值 computed watcher 依赖的数据变化后会触发 setter 过程,通知所有订阅它变化的 watcher,执行 watcher.update() computed 是什么 computed 实际上是一个 comput…
computed 示例 初始化 computed watcher 实例的构造函数 执行 watcher.depend() 执行 watcher.evaluate() 去求值 computed watcher 依赖的数据变化后会触发 setter 过程,通知所有订阅它变化的 watcher,执行 watcher.update() computed 是什么 computed 实际上是一个 comput…
var vm = new Vue({
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function() {
return this.firstName + ' ' + this.lastName;
}
}
});
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
// 针对上例,如果computed存在,不会立即求值,而去建一个Dep实例
// 当render函数执行访问到this.firstName的时候,就触发的计算属性的getter
// 这使会拿到计算属性的watcher,然后执行 watcher.depend()
if (this.computed) {
this.value = undefined
this.dep = new Dep()
} else {
this.value = this.get()
}
}
// 这步中Dep.target是渲染watcher
depend () {
if (this.dep && Dep.target) {
// 所以this.dep.depend()相当于渲染watcher订阅了整个 computed watcher的变化
this.dep.depend()
}
}
// 该步执行完Dep.target就是computed watcher
evaluate () {
// 判断 this.dirty,如果为true
if (this.dirty) {
// 通过 this.get() 求值
// 求值过程中会执行 value = this.getter.call(vm, vm)
// this.getter.call(vm, vm)就会执行计算属性的getter函数
// 在该例子中,就是执行this.firstName + ' ' + this.lastName
// 因为this.firstNameh和this.lastName是响应式对象,进而就会触发他俩的getter
// 再进而就把自身持有的dep添加到正在计算的watcher中,这时候Dep.target就是这个 computed watcher
this.value = this.get()
// 并且把this.dirty置为false
this.dirty = false
}
// 拿到计算属性对应的值
return this.value
}
// w
if (this.computed) {
// 如果没有订阅计算属性变化的watcher,采用lazy模式
// 只有当下次访问这个计算属性的时候才会重新求值
if (this.dep.subs.length === 0) {
this.dirty = true;
} else {
// 针对上列,是有订阅计算属性变化的watcher的,所以会执行这步
this.getAndInvoke(() => {
this.dep.notify();
});
}
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);
}
this.getAndInvoke(() => {
this.dep.notify()
})
// getAndInvoke会对比新旧值,只有数值有变化,才会执行this.dep.notify(),触发渲染watcher重新渲染
getAndInvoke (cb: Function) {
const value = this.get()
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
const oldValue = this.value
this.value = value
this.dirty = false
if (this.user) {
try {
cb.call(this.vm, value, oldValue)
} catch (e) {
handleError(e, this.vm, `callback for watcher "${this.expression}"`)
}
} else {
cb.call(this.vm, value, oldValue)
}
}
}
computed 实际上是一个 computed watcher
在初始化 computed 的时候,会触发渲染 watcher 订阅 computed ,然后在求值的过程中,会执行计算属性的 getter 函数,又因为求值的两个变量是响应式对象,所以又触发它俩的 getter,把它俩自身持有 dep 添加到 watcher 中,如果计算属性依赖的变量有变化,会触发渲染 watcher 的重新渲染,如果依赖的变量有变化,但是新值和旧值相等,是不会触发重新渲染,只有最终计算的值发生变化,才会执行 this.dep.notify(),触发渲染 watcher 重新渲染
// src/core/instance/state.js
const computedWatcherOptions = { computed: true };
function initComputed(vm: Component, computed: Object) {
// 先创建vm.computedWatchers为空对象
const watchers = (vm._computedWatchers = Object.create(null));
// 判断是否是ssr
const isSSR = isServerRendering();
// 然后对computed属性进行遍历
for (const key in computed) {
// 拿到计算属性每一个userDef
const userDef = computed[key];
// 试着去获取每一个userDef的getter函数
const getter = typeof userDef === 'function' ? userDef : userDef.get;
// 拿不到报警告
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(`Getter is missing for computed property "${key}".`, vm);
}
// 如果不是ssr,就新建一个Watcher
if (!isSSR) {
// 为每一个getter创建一个Watcher
// 这个Watcher和渲染watcher不同,在代码开头定义了const computedWatcherOptions = { computed: true }
watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions);
}
// 对key进行判断
if (!(key in vm)) {
// 如果不是vm的属性,则用defineComputed
defineComputed(vm, key, userDef);
} else if (process.env.NODE_ENV !== 'production') {
// 如果已经在vm.$data或者在props中,则报出已经存在的警告
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);
}
}
}
}
// src/core/instance/state.js
// 利用Object.defineProperty给计算属性对应的key值添加getter和setter
// 只要计算属性的key有set,那么setter是计算属性的一个对象,否则就是一个空函数
export function defineComputed(target: any, key: string, userDef: Object | Function) {
const shouldCache = !isServerRendering();
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef;
sharedPropertyDefinition.set = noop;
} else {
sharedPropertyDefinition.get = userDef.get ? (shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get) : noop;
sharedPropertyDefinition.set = userDef.set ? userDef.set : noop;
}
if (process.env.NODE_ENV !== 'production' && sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function() {
warn(`Computed property "${key}" was assigned to but it has no setter.`, this);
};
}
Object.defineProperty(target, key, sharedPropertyDefinition);
}
// src/core/instance/state.js
function createComputedGetter(key) {
// 返回一个computedGetter函数,这个就是计算属性的getter
return function computedGetter() {
const watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
watcher.depend();
return watcher.evaluate();
}
};
}
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论