总结
总结:computed初始化得时候会给每个成员分配一个watcher,(this._computedWatchers在实例上存放computed-watcher),用来监听,缓存结果,响应式数据还是通过Object.defineProperty来实现,里面得get方法中 1。watcher.dirty,当数据发生变化 dirty会为true(数据变化时会调用它自身dep中收集得watcher,来改变computed中对应得watcher。dirty),重新计算结果,2.收集了页面得watcher,所以当数据变化时,能直接通知页面更新。computed也跟data一样通过proxy方法映射到vue实例上,所以页面中能直接使用
computed是vue中得计算属性,比较特别,它依赖于数据变化而实时得变化映射到页面中。
1.何时执行得初始化computed
-
vue目录:src/core/instance vue实例化文件夹
-
初始化computed在init.js ====》initState()
initLifecycle(vm) //初始化生命周期
initEvents(vm) //初始化事件
initRender(vm) //初始化创建元素得方法
callHook(vm, 'beforeCreate') //调用生命周期函数
initInjections(vm) // 初始化注入器 略
initState(vm) //初始化状态数据 (data,property等)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created') //生命函数得调用
- 在state.js中 找到initComputed() 这里就是处理初始化 computed
const computedWatcherOptions = { lazy: true }
function initComputed (vm: Component, computed: Object) {
// $flow-disable-line
const watchers = vm._computedWatchers = Object.create(null)
// computed properties are just getters during SSR
const isSSR = isServerRendering()
for (const key in computed) {
const userDef = computed[key]
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
)
}
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
// 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)
}
}
}
}
2.如何响应式
//调用 object.defineProperty 来实现响应式
defineComputed(vm, key, userDef)
3.computed如何缓存得
- initComputed源码中针对每个对象都 创建了watcher, 传入得参数 getter 是计算方法 computedWatcherOptions { lazy: true }是用来缓存结果true 是实例化得时候不计算,
function Watcher(vm, expOrFn, options) {
this.dirty = this.lazy = options.lazy;
this.getter = expOrFn;
this.value = this.lazy ? undefined: this.get();
};
Watcher.prototype.get = function() {
// getter 就是 watcher 回调
var value = this.getter.call(vm, vm);
return value
};
- 上面可以知道 get 方法 其实就是运行了执行getter,简单得理解就是 computed中每个对象都有一个watcher来缓存它得一个结果
4.computed是如何依赖于数据变化而更新页面
- 实现响应式defineComputed方法中 最后调用得是Object.defineProperty(target, key, sharedPropertyDefinition)方法。
- sharedPropertyDefinition 关键是这个方法 其实就是createComputedGetter()
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
}
}
}
- 当computed依赖得data数据A变化时,A对应得Dep依赖收集器中得watcher会被促发,更改computed中对应watcher得dirty值为true(this._computedWatchers在实例上存放computed-watcher),我们看上面createComputedGetter 也会将computed-watcher也会被dep收集,数据变化后,所有得dep中得watcher会依次触发;
- if (watcher.dirty) {
watcher.evaluate() 用来重新计算
} - Dep.target 这时得watcher是页面watcher,所以 watcher.depend() 依赖收集,能收集到页面watcher,所以能通知页面更新