Vue-computed

Vue-computed

computed 与 data 类似,在 init 阶段的 initState 中初始化的:

1
2
3
4
5
6
7
// 遍历 computed,,为子项创建 watcher
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)

如果 key 不是 vm 的属性则会调用:

1
2
3
4
5
6
7
8
9
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)
}
}

defineComputed 中通过 defineProperty 给 vm 添加对应 computed key 的 getter 和 setter。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)
}

调用createComputedGetter 创建 getter。当 computed 被使用时将会调用该 getter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
}
}
}

所以:

  1. 页面更新时 Dep.target 设置为当前页面的 watcher
  2. 引用 computed 值的地方会触发他的 getter,createComputedGetter 包装的函数就会被调用。
  3. 此时若 dirty 则会使 computedWatcher.evaluate 调用,在计算 computed 的值时就会将这个 computed 的值依赖添加到 computedWatcher 中,同时依赖中也会将这个 computedWatcher 添加到它的 订阅者收集器 中。

    computedWatcher.evaluate 执行时会备份当前的 Dep.target(渲染watcher),在获取 computed 值的过程中 Dep.target 会设置为 computedWatcher,当获取到 computed 的值后会恢复为 Dep.target(渲染watcher)。

  4. 执行 computedWatcher.depend() 时,Dep.target 为 渲染watcher,作用是把当前 computedWatcher 的依赖全部添加进 渲染watcher,依赖也会将 渲染watcher 添加到它的 订阅者收集器 中。

    这步的作用是,当 computedWatcher 的依赖变化时直接通知页面更新(渲染watcher),而不是通过 computed 变化后再通知页面去更新,一步到位。

流程图

流程图

# vue

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×