Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
if (options) {
this.computed = !!options.computed
}
if (this.computed) {
this.value = undefined
this.dep = new Dep()
} else {
this.value = this.get()
}
}
get () {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e) {
} finally {
popTarget()
}
return value
}
update () {
if (this.computed) {
if (this.dep.subs.length === 0) {
this.dirty = true
} else {
this.getAndInvoke(() => {
this.dep.notify()
})
}
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
evaluate () {
if (this.dirty) {
this.value = this.get()
this.dirty = false
}
return this.value
}
depend () {
if (this.dep && Dep.target) {
this.dep.depend()
}
}
}
为了简洁突出重点,这里我手动去掉了我们暂时不需要关心的代码片段。
观察 Watcher
的 constructor
,结合刚才讲到的 new Watcher
传入的第四个参数 {computed:true}
知道,对于计算属性而言 watcher
会执行 if
条件成立的代码 this.dep = new Dep()
,而 dep
也就是创建了该属性的消息订阅器。
export default class Dep {
static target: ?Watcher;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
Dep.target = nullDep
同样精简了部分代码,我们观察 Watcher
和 Dep
的关系,用一句话总结
watcher
中实例化了 dep
并向 dep.subs
中添加了订阅者,dep
通过 notify
遍历了 dep.subs
通知每个 watcher
更新。
defineComputed
定义计算属性
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
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)
}
}因为 computed
属性是直接挂载到实例对象中的,所以在定义之前需要判断对象中是否已经存在重名的属性,defineComputed
传入了三个参数:vm
实例、计算属性的 key
以及 userDef
计算属性的定义(对象或函数)。
然后继续找到 defineComputed
定义处:
export function defineComputed (
target: any,
key: string,
userDef: Object 关键词:Vue中computed的完成原理是啥?