Watch
如果用户提供了watch方法,会去 initWatch
watch 用的用法挺多,1.值是对象、2.值是数组、3.值是字符串
最终都是通过 createWatcher
> vm.$watch
去创建watch
进一步拓展 class Watch
,
首先注册 $watch
大致如下,在 new Watch 的基础上补了属性,兼容了立即执行
stateMixin(Vue);
export function stateMixin(Vue) {
Vue.prototype.$watch = function(exprOrFn, cb, options = {}) {
options.user = true; // 标记为用户watcher
// 核心就是创建个watcher
const watcher = new Watcher(this, exprOrFn, cb, options);
if(options.immediate){
cb.call(vm,watcher.value)
}
}
}
Computed
为啥有缓存?
如果用户提供了 computed 选项,这意味着用到了计算属性:计算属性特点有缓存、响应式、不会主动触发
要理解计算属性,需要先了解watcher,要了解watcher需要先了解响应式原理,懂了计算属性必然理解了后两者,啧啧啧。
思路大纲:
- 用户提供了computed
- 为每个computed建立watcher
new Watcher(vm, userFn, () => {}, { lazy: true })
,这里不需要回调,设定了 lazy 标识和普通的watcher不一样 - watcher中如果lazy=true,默认this.get 时候不调用:不主动触发
- 要实现缓存,用到一个flag:
dirty
,这意味着符合条件时候dirty=false,就不执行运算,返回某个值;如果不符合条件 dirty=true,重新运算,并且存储起来 - 现在wather上需要几个属性:
- value存储值
- dirty 运算开关
- lazy表示是否计算属性
- evaluate 执行运算的方法
- 隐藏运算到 Object.defineProperty 的get,因为computed可以设定get和set
准备 defineComputed
供initComputed使用,这里通过调用 createComptedGetter
拿到封装后的 desc描述符
交给 Object.defineProperty
使用。这样用户get某个key,得到的是getter方法
function defineComputed(target, key, userDef) {
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = createComputedGetter(key)
} else {
sharedPropertyDefinition.get = createComputedGetter(userDef.get);
sharedPropertyDefinition.set = userDef.set;
}
// 使用defineProperty定义
Object.defineProperty(target, key, sharedPropertyDefinition)
}
createComputedGetter
需要做的是:
- 返回一个function,后续传递给 desc,也就是高阶函数
- 准备好和watcher联通
通过阅读发现大致如下:
function createComputedGetter(key) {
return function computedGetter() {
const watcher = this._computedWatchers[key];
if (watcher) {
if (watcher.dirty) { // 如果dirty为true
watcher.evaluate();// 计算出新值,并将dirty 更新为false
}
// 如果依赖的值不发生变化,则返回上次计算的结果
return watcher.value
}
}
}
解读:
- 高阶函数返回函数,这就是 读key时候的get过程
- 通过key找到watcher
- 如果dirty=true,意味着需要执行,那就
evaluate
,就watcher.get
并且watcher.dirty=false
- 否则不执行,返回上次的 value
- 如果依赖项发生变化
watcher.update
会修改 dirty=true,后续get就会重新计算了
注意 watcher 和 dep的联通:
- watcher中
watcher.depend()
- dep中
this.deps[i].depend()
让计算属性中依赖的数据也记录渲染watcher