removeSub(sub: DepTarget) { // #12696 deps with massive amount of subscribers are extremely slow to // clean up in Chromium // to workaround this, we unset the sub for now, and clear them on // next scheduler flush. this.subs[this.subs.indexOf(sub)] = null if (!this._pending) { this._pending = true pendingCleanupDeps.push(this) } }
depend(info?: DebuggerEventExtraInfo) { if (Dep.target) { // 调用watcher的addDep方法 Dep.target.addDep(this) if (__DEV__ && info && Dep.target.onTrack) { Dep.target.onTrack({ effect: Dep.target, ...info }) } } }
notify(info?: DebuggerEventExtraInfo) { // stabilize the subscriber list first const subs = this.subs.filter(s => s) asDepTarget[] if (__DEV__ && !config.async) { // subs aren't sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort((a, b) => a.id - b.id) } for (let i = 0, l = subs.length; i < l; i++) { const sub = subs[i] if (__DEV__ && info) { sub.onTrigger && sub.onTrigger({ effect: subs[i], ...info }) } sub.update() } } }
// The current target watcher being evaluated. // This is globally unique because only one watcher // can be evaluated at a time. Dep.target = null consttargetStack: Array<DepTarget | null | undefined> = []
/** * Evaluate the getter, and re-collect dependencies. */ get() { // Dep类中定义 pushTarget(this) let value const vm = this.vm try { // try-catch-finally => 重要的是try // 这里的调用getter,实际上是调用updateComponent方法 value = this.getter.call(vm, vm) } catch (e: any) { if (this.user) { handleError(e, vm, `getter for watcher "${this.expression}"`) } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value) } popTarget() this.cleanupDeps() } return value }
/** * Add a dependency to this directive. */ addDep(dep: Dep) { const id = dep.id // 判断newDepIds中没有没 if (!this.newDepIds.has(id)) { // 没有就添加id,并push dep到DepDes中 this.newDepIds.add(id) this.newDeps.push(dep) // 如果depIds中也没有 if (!this.depIds.has(id)) { // Dep类中的addSub方法,把这个watcher实例添加到Dep类的subs中 dep.addSub(this) } } }
/** * Clean up for dependency collection. */ cleanupDeps() { let i = this.deps.length while (i--) { const dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } } lettmp: any = this.depIds this.depIds = this.newDepIds this.newDepIds = tmp this.newDepIds.clear() tmp = this.deps this.deps = this.newDeps this.newDeps = tmp this.newDeps.length = 0 }
/** * Subscriber interface. * Will be called when a dependency changes. */ update() { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } elseif (this.sync) { this.run() } else { queueWatcher(this) } }
/** * Scheduler job interface. * Will be called by the scheduler. */ run() { if (this.active) { const value = this.get() if ( value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { // set new value const oldValue = this.value this.value = value if (this.user) { const info = `callback for watcher "${this.expression}"` invokeWithErrorHandling( this.cb, this.vm, [value, oldValue], this.vm, info ) } else { this.cb.call(this.vm, value, oldValue) } } } }
/** * Evaluate the value of the watcher. * This only gets called for lazy watchers. */ evaluate() { this.value = this.get() this.dirty = false }
/** * Depend on all deps collected by this watcher. */ depend() { let i = this.deps.length while (i--) { this.deps[i].depend() } }
/** * Remove self from all dependencies' subscriber list. */ teardown() { if (this.vm && !this.vm._isBeingDestroyed) { remove(this.vm._scope.effects, this) } if (this.active) { let i = this.deps.length while (i--) { this.deps[i].removeSub(this) } this.active = false if (this.onStop) { this.onStop() } } } }
exportclassObserver { dep: Dep vmCount: number // number of vms that have this object as root $data
constructor(public value: any, public shallow = false, public mock = false) { // this.value = value this.dep = mock ? mockDep : newDep() this.vmCount = 0 // 将Observer实例挂载到data的__ob__属性上, // 相当于是给对象是否做了响应式打了一个标记,在调用observe的时候,会判断是否有这个标记 // 如果对一个对象做过了响应式处理了,就不需要再做一遍 def(value, '__ob__', this) if (isArray(value)) { if (!mock) { if (hasProto) { /* eslint-disable no-proto */ ;(value as any).__proto__ = arrayMethods /* eslint-enable no-proto */ } else { for (let i = 0, l = arrayKeys.length; i < l; i++) { const key = arrayKeys[i] def(value, key, arrayMethods[key]) } } } if (!shallow) { this.observeArray(value) } } else { /** * 遍历对象上的每个key(属性),将它们转为带有getter和setter的属性, * 这个方法只有当value是对象的时候才会执行,(原来2.6的版本是写的walk方法) */ /** * Walk through all properties and convert them into * getter/setters. This method should only be called when * value type is Object. */ const keys = Object.keys(value) // data里面 可能有很多的属性 ,for循环,对每一个key都做响应式处理 for (let i = 0; i < keys.length; i++) { const key = keys[i] // 做响应式处理~~~ defineReactive defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock) } } }
/** * Observe a list of Array items. */ observeArray(value: any[]) { for (let i = 0, l = value.length; i < l; i++) { observe(value[i], false, this.mock) } } }
if (__DEV__) { watcherOptions.onTrack = e =>callHook(vm, 'renderTracked', [e]) watcherOptions.onTrigger = e =>callHook(vm, 'renderTriggered', [e]) }
// we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined newWatcher( vm, updateComponent, noop, watcherOptions, true/* isRenderWatcher */ ) hydrating = false
// flush buffer for flush: "pre" watchers queued in setup() const preWatchers = vm._preWatchers if (preWatchers) { for (let i = 0; i < preWatchers.length; i++) { preWatchers[i].run() } }
// manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') } return vm }
Vue.prototype._render = function (): VNode { constvm: Component = this const { render, _parentVnode } = vm.$options
if (_parentVnode && vm._isMounted) { vm.$scopedSlots = normalizeScopedSlots( vm.$parent!, _parentVnode.data!.scopedSlots, vm.$slots, vm.$scopedSlots ) if (vm._slotsProxy) { syncSetupSlots(vm._slotsProxy, vm.$scopedSlots) } }
// set parent vnode. this allows render functions to have access // to the data on the placeholder node. vm.$vnode = _parentVnode! // render self let vnode try { // There's no need to maintain a stack because all render fns are called // separately from one another. Nested component's render fns are called // when parent component is patched. setCurrentInstance(vm) currentRenderingInstance = vm // 执行render函数 vnode = render.call(vm._renderProxy, vm.$createElement) } catch (e: any) { handleError(e, vm, `render`) // return error render result, // or previous vnode to prevent render error causing blank component /* istanbul ignore else */ if (__DEV__ && vm.$options.renderError) { try { vnode = vm.$options.renderError.call( vm._renderProxy, vm.$createElement, e ) } catch (e: any) { handleError(e, vm, `renderError`) vnode = vm._vnode } } else { vnode = vm._vnode } } finally { currentRenderingInstance = null setCurrentInstance() } // if the returned array contains only a single node, allow it if (isArray(vnode) && vnode.length === 1) { vnode = vnode[0] } // return empty vnode in case the render function errored out if (!(vnode instanceofVNode)) { if (__DEV__ && isArray(vnode)) { warn( 'Multiple root nodes returned from render function. Render function ' + 'should return a single root node.', vm ) } vnode = createEmptyVNode() } // set parent vnode.parent = _parentVnode return vnode }