Open Been101 opened 4 years ago
function initComputed (vm: Component, computed: Object) {
// $flow-disable-line
const watchers = vm._computedWatchers = Object.create(null)
//...省略
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
//...省略
// 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)) {
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)
}
}
}
}
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const 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
}
//...省略
Object.defineProperty(target, key, sharedPropertyDefinition)
}
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
具体源码的位置是在 vue/src/core/instance/state.js
源码大致分析:
初始化的时候 vm 劫持了computed里面的key,而具体的赋值是在compile之后生成vnode之前 触发了computedGetter回调方法,
这里面会执行 watcher.evaluate()
evaluate () {
this.value = this.get()
this.dirty = false
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get () {
//..省略
try {
value = this.getter.call(vm, vm)
} catch (e) {
//..省略
}
return value
}
其中this.getter就是computed对应key的回调函数,执行了 return this.firstname + this.lastname 然后生成vnode, 因为是第一次渲染所以不涉及diff过程,之后页面就渲染完并展示fullname。
当 lastname 更新时,触发dep.notify(),此时就进入了update过程,update过程其实就是将初始化得到语法树转换成虚拟dom的过程,这个过程会读取fullname的value, 又会重新触发computedGetter过程,而这个过程会依次读取this.firstname 和 this.lastname的value 并返回最终的结果,得到结果然后生成新的虚拟dom,再进入diff流程,最终更新视图。
这里再插一句: computed 会对应一个内部watcher, data 对应一个更新watcher,data每一个key对应一个dep,如果key对应的value是object,会递归defineReactive,也就是说每一层obj的key都会对应一个dep。当更改data中的某一项数据时,会触发dep.notify, 继而触发dep依赖的所有watcher, 这里只说computed + data的情况,这时其实有两个watcher,data对应的watcher是和真实更新虚拟dom相关的,computed的更新受益与虚拟dom的更新。
简单理解,有问题欢迎沟通交流。
大佬, 能解答一下,为什么 Object.defineProperty 劫持一下 computed 中的属性,就可以实现 computed 的功能呢,
firstname 或 lastname 更新怎么触发的 fullname, 这一块不是很明白, 为什么 lastname 更新时 set 方法里的 dep.notify(); 这个 dep 是 跟 fullname 相关的。
大佬方便微信或其他聊天工具联系一下吗