// initData
const data = { msg: 'msg' }
observe(data)
// observe
function observe(data) {
if (!isObject(value)) return;
return new Observer(data)
}
// Class Observe
class Observe {
value: any;
dep: Dep;
vmCount: number;
constructor (data) {
this.value = data
this.dep = new Dep()
this.vmCount = 0
data._ob_ = this
this.walk(data)
}
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
}
// 这一步完成之后会将我们data定义一个_ob_属性 说明以及做过响应式处理
class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this) // watcher 添加对应的dep
}
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 调用对应的watcher更新
}
}
}
function defineReactive(obj, key) {
const dep = new Dep()
let childOb = observe(val) // 子对象 { msgData: {msg: ''} } => observe({ msg: ''})
Object.defineProperty(obj, key,{
get() {
if (Dep.target) {
dep.depend() // watcher实例 添加对应的dep
}
if (childOb) {
childOb.dep.depend()
}
retun obj[key]
},
set(newVal) {
val = newVal
childOb = observe(newVal)
dep.notify()
}
})
}
// 假设一个data为以下数据
const data = {
msg: 'msg',
formData: {
name: 'zyh'
}
};
// 进行过响应式过后为:
const data = {
msg: 'msg',
formData: {
name: 'zyh',
_ob_: Observe{ value: formData, dep: Dep }
},
_ob_: Observe{ value: data, dep: Dep }
};
何时触发get呢?
接着手动调用$mount(‘#app’)
Vue.prototype.$mount = function(el) {
const options = this.$options
const { render, staticRenderFns } = compileToFunctions(template) // 生成render函数
return mount.call(this, el, hydrating)
}
Vue.prototype.$mount = function(el) {
return mountComponent(this, el)
}
function mountComponent(vm, el) {
vm.$el = el
if (!vm.$options.render) vm.$options.render = createEmptyVNode // 生成一个空节点
let updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, ()=>{}, {}, true /* isRenderWatcher */)
}
class Watcher {
constructor(
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {
vm._watcher = this
}
vm._watchers.push(this)
this.getter = expOrFn // updateComponent
this.value = this.get()
}
get() {
const vm = this.vm
value = this.getter.call(vm, vm) // 开始执行 vm._update(vm._render(), hydrating)
}
}
vm._update 在哪定义呢 lifecycleMixin
Vue.prototype._update
梳理下流程:
- 定义响应式数据
- 执行挂载
- 生成render函数
- 执行mountComponent函数
- new Wather
- wather 内部调用get Dep.target = this(watcher)
- wather 内部调用vm._update(vm._render(), hydrating)
- 调用render函数生成vnode,这一步触发 getter 依赖收集Dep.targe(wather)
- set触发dep.notify ```typescript const data = { obj: { foo: { bar: ‘bar’ } }, name: ‘kobe’ }
// 1. this.data.obj = this.obj this.data.name = this.name // 2. this.obj getter 触发getter 的 依赖收集 // 3. this.obj.foo // // 4. 赋值触发setter
```typescript
dep.depend Dep.target:watcher
// class Dep
// 目的还是添加watcher
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
addSub (watcher) {
this.subs.push(sub)
}
// class Watcher
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
// 对数组的原型方法进行拦截扩展
protoAugment(value, arrayMethods) // value: []
const arrayMethods = Object.create(Array.prototype)
function protoAugment (target, src: Object) {
/* eslint-disable no-proto */
target.__proto__ = src
/* eslint-enable no-proto */
}
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})