代码
下面故事以这个代码为背景。
<template>
<div>
{{ magazineMan }}
<button @click="newEditionMan">男人帮出新品</button>
<button @click="newEditionWoman">女人帮出新品</button>
</div>
</template>
<script>
export default {
data() {
return {
magazineMan: '男人帮版本1',
magazineWoman: '女人帮版本1',
};
},
watch: {
magazineMan(newValue, oldValue) {
console.log(newValue);
console.log(oldValue);
},
},
computed: {
manAddWoman({ magazineMan, magazineWoman }) {
return magazineMan + magazineWoman;
},
},
methods: {
newEditionMan() {
this.magazineMan = '男人帮版本2';
},
newEditionWoman() {
this.magazineWoman = '女人帮版本2';
},
},
};
</script>
故事
公司: data
商品:magazineMan(男人帮)、magazineWoman(女人帮)
客户: watch、manAddWoman、template
watch、manAddWoman、template 他们需要一个特定的身份,所以每个建立一个身份 Watcher,就代表着 data 公司的客户 id号。下面代码是简化过的都是初始化 Watcher 知道 Watcher是什么,其实也没必要理解下面代码
。
// 2.6.14 (/src/core/instance/init.js)
function initComputed (vm: Component, computed: Object) {
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
// 配置项,computed 默认是懒执行
computedWatcherOptions
)
}
// 2.6.14 (/src/core/instance/init.js)
function initWatch (vm: Component, watch: Object) {
createWatcher(vm, key, handler[i])
}
function createWatcher () {
return vm.$watch(expOrFn, handler, options)
}
Vue.prototype.$watch = function (): Function {
const watcher = new Watcher(vm, expOrFn, cb, options)
}
// 每个一个组件都是一个 Watcher
// /src/core/instance/lifecycle.js
export function mountComponent (){
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
}
// /src/platforms/web/runtime/index.js
Vue.prototype.$mount = function (): Component {
return mountComponent(this, el, hydrating)
}
公司 data 拥有许多杂志,为了方便杂志的管理,所以每个杂志都有两个功能(get、set),get是来收集有哪些顾客订阅来此杂志,将顾客放到 dep 的盒子里,set用来通知订阅的顾客过来拿最新杂志。所以 magazineMan(男人帮) 和 magazineWoman(女人帮) 都有 get 与 set 功能。 下面代码是简化过的(2.6.14 (/src/core/observer/index.js))。
简洁: get -> dep.depend(), set -> dep.notify()
export function defineReactive () {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// get 拦截对 obj[key] 的读取操作
get: function reactiveGetter () {
if (Dep.target) {
// 依赖收集,在 dep 中添加 watcher,也在 watcher 中添加 dep
dep.depend()
}
return value
},
// set 拦截对 obj[key] 的设置操作
set: function reactiveSetter (newVal) {
// 依赖通知更新
dep.notify()
}
})
}
从上面的背景看到 watch、template 和 manAddWoman 都订阅了 magazineMan(男人帮),manAddWoman 还多订阅了一个 magazineWoman(女人帮)。
当 watch、template 和 manAddWoman 订阅 magazineMan(男人帮) 的时候,magazineMan(男人帮) 就执行 get 函数把这三个客户都放到自己的 dep 盒子里面,manAddWoman 多订阅了 magazineWoman(女人帮),也是一样的操作。
get: function reactiveGetter () {
if (Dep.target) {
// 依赖收集,在 dep 中添加 watcher,也在 watcher 中添加 dep
dep.depend()
}
return value
}
他们想看自己订阅了哪些杂志,在被杂志收集的时候他们也收集来此杂志放到自己的信息下面。
知道他们互相添加就行
export default class Dep {
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
},
addSub (sub: Watcher) {
this.subs.push(sub)
}
}
export default class Watcher {
addDep (dep: Dep) {
this.newDeps.push(dep)
dep.addSub(this)
}
}
当杂志出新版了就拿出自己的 dep 来通知订阅来自己的用户,让他们来拿最新的杂志版本。
set: function reactiveSetter (newVal) {
// 依赖通知更新
dep.notify()
}
循环通知客户
export default class Dep {
notify () {
// 遍历 dep 中存储的 watcher,执行 watcher.update()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
当点击 newEditionMan(男人帮版本升级) 就会去通知 watch、template 和 manAddWoman 来拿最新的男人帮版本。
newEditionWoman(女人帮版本升级) 就只通知 manAddWoman。