watcher
Watcher类的实现比较复杂,因为他的实例分为渲染 watcher(render-watcher)、计算属性 watcher(computed-watcher)、侦听器 watcher(normal-watcher)三种,
这三个实例分别是在三个函数中构建的:mountComponent 、initComputed和Vue.prototype.$watch。
normal-watcher
我们在组件钩子函数watch 中定义的,都属于这种类型,即只要监听的属性改变了,都会触发定义好的回调函数,这类watch的expression是我们写的回调函数的字符串形式。
computed-watcher
我们在组件钩子函数computed中定义的,都属于这种类型,每一个 computed 属性,最后都会生成一个对应的 watcher 对象,但是这类 watcher 有个特点:当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。这类watch的expression是计算属性中的属性名。
render-watcher
每一个组件都会有一个 render-watcher, 当 data/computed 中的属性改变的时候,会调用该 render-watcher 来更新组件的视图。这类watch的expression是function () {vm._update(vm._render(), hydrating);}。
除了功能上的区别,这三种 watcher 也有固定的执行顺序,分别是:computed-render -> normal-watcher -> render-watcher。
这样安排是有原因的,这样就能尽可能的保证,在更新组件视图的时候,computed 属性已经是最新值了,如果 render-watcher 排在 computed-render 前面,就会导致页面更新的时候 computed 值为旧数据。
//lifecycle.jsnew Watcher(vm, updateComponent, noop, {before () {if (vm._isMounted && !vm._isDestroyed) {callHook(vm, 'beforeUpdate')}}}, true /* isRenderWatcher */)
/* @flow */import {warn,remove,isObject,parsePath,_Set as Set,handleError,noop} from '../util/index'import { traverse } from './traverse'import { queueWatcher } from './scheduler'import Dep, { pushTarget, popTarget } from './dep'import type { SimpleSet } from '../util/index'let uid = 0/*** A watcher parses an expression, collects dependencies,* and fires callback when the expression value changes.* This is used for both the $watch() api and directives.*//** 一个解析表达式,进行依赖收集的观察者,* 同时在表达式数据变更时触发回调函数。* 它被用于$watch() api以及指令*/// 当是渲染watcher时,expOrFn是updateComponent,即重新渲染执行render(_update)// 当是计算watcher时,expOrFn是计算属性的计算方法// 当是侦听器watcher时,expOrFn是watch属性的名字,this.cb就是watch的handler属性// 对于渲染watcher和计算watcher来说,expOrFn的值是一个函数,可以直接设置getter// 对于侦听器watcher来说,expOrFn是watch属性的名字,会使用parsePath函数解析路径,获取组件上该属性的值(运行getter)/** 需要特别注意的是,在一个组件中,也就是一个vue实例内,* 只有一个render watcher* 也就是说一个组件内的变量改变导致dom更新,触发的都是同一个render watcher* computed的watcher,则会在this._computedWatchers中* */export default class Watcher {vm: Component;expression: string;cb: Function;id: number;deep: boolean;user: boolean;lazy: boolean;sync: boolean;dirty: boolean;active: boolean;deps: Array<Dep>;newDeps: Array<Dep>;depIds: SimpleSet;newDepIds: SimpleSet;before: ?Function;getter: Function;value: any;constructor (vm: Component,expOrFn: string | Function,cb: Function,options?: ?Object,isRenderWatcher?: boolean) {this.vm = vmif (isRenderWatcher) {vm._watcher = this}/*_watchers存放订阅者实例*/vm._watchers.push(this)// optionsif (options) {this.deep = !!options.deep //是否启用深度监听this.user = !!options.user //主要用于错误处理,侦听器 watcher的 user为true,其他基本为falsethis.lazy = !!options.lazy //惰性求值,当属于计算属性watcher时为truethis.sync = !!options.sync //标记为同步计算,三大类型暂无this.before = options.before} else {this.deep = this.user = this.lazy = this.sync = false}this.cb = cbthis.id = ++uid // uid for batchingthis.active = truethis.dirty = this.lazy // for lazy watchersthis.deps = []this.newDeps = []this.depIds = new Set()this.newDepIds = new Set()this.expression = process.env.NODE_ENV !== 'production'? expOrFn.toString(): ''// parse expression for getter// 解析expOrFn,赋值给this.getterif (typeof expOrFn === 'function') {this.getter = expOrFn} else {/* 把表达式expOrFn解析成getter */this.getter = parsePath(expOrFn)if (!this.getter) {this.getter = noopprocess.env.NODE_ENV !== 'production' && warn(`Failed watching path: "${expOrFn}" ` +'Watcher only accepts simple dot-delimited paths. ' +'For full control, use a function instead.',vm)}}this.value = this.lazy ?undefined :this.get()// 当为computed属性时,lazy为true,// 这时不会在new Watcher()执行get函数,即为惰性求值// 其他的时候都会在实例化时执行get,进行依赖收集}/*** Evaluate the getter, and re-collect dependencies.*/get() {pushTarget(this) // 将当前watcher实例放入Dep.target中let valueconst vm = this.vmtry {value = this.getter.call(vm, vm)// 执行getter函数即是获取了一次observer中的值// 触发了observer的get中的dep.depend()// 将Dep.target的watcher实例push到dep.subs中} catch (e) {if (this.user) {// user为true,那即是侦听器watch// 如果是watch的值不存在或有问题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/*如果存在deep,则触发每个深层对象的依赖,追踪其变化*/if (this.deep) {// 深度监听,就是watch的deep选项/*递归每一个对象或者数组,*触发它们的getter,*使得对象或数组的每一个成员都被依赖收集,*形成一个“深(deep)”依赖关系*/traverse(value)}popTarget()this.cleanupDeps()}return value}/*** Add a dependency to this directive.*//*添加一个依赖关系到Deps集合中*/addDep(dep: Dep) {const id = dep.idif (!this.newDepIds.has(id)) {// 同时实例中也会保存添加过当前实例的dep和dep的idthis.newDepIds.add(id)this.newDeps.push(dep)if (!this.depIds.has(id)) {dep.addSub(this)}}}/*** Clean up for dependency collection.*//*清理依赖收集*/cleanupDeps() {let i = this.deps.lengthwhile (i--) {const dep = this.deps[i]if (!this.newDepIds.has(dep.id)) {dep.removeSub(this)}}let tmp = this.depIdsthis.depIds = this.newDepIdsthis.newDepIds = tmpthis.newDepIds.clear()tmp = this.depsthis.deps = this.newDepsthis.newDeps = tmpthis.newDeps.length = 0}/*** Subscriber interface.* Will be called when a dependency changes.*//*调度者接口,当依赖发生改变的时候进行回调。*/update() {/* istanbul ignore else */if (this.lazy) {// 脏值标记this.dirty = true} else if (this.sync) {/*同步则执行run直接渲染视图*/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.// 即便值相同,拥有Deep属性的观察者以及在对象/数组上的观察者应该被触发更新,// 因为它们的值可能发生改变。isObject(value) ||this.deep) {// set new valueconst oldValue = this.value/*设置新的值*/this.value = valueif (this.user) {// user 为真时,使用了侦听者watchtry {this.cb.call(this.vm, value, oldValue)} catch (e) {handleError(e, this.vm, `callback for watcher "${this.expression}"`)}} 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.*//*收集该watcher的所有deps依赖*/depend() {let i = this.deps.lengthwhile (i--) {this.deps[i].depend()}}/*** Remove self from all dependencies' subscriber list.*//* 将自身从所有依赖收集订阅列表删除 */teardown() {if (this.active) {// remove self from vm's watcher list// this is a somewhat expensive operation so we skip it// if the vm is being destroyed.// 将self从vm的watcher列表中移除,// 这是一个有点耗时的操作,// 所以如果vm正在被销毁,我们就跳过它。// 从watcher列表移除自身if (!this.vm._isBeingDestroyed) {remove(this.vm._watchers, this)}let i = this.deps.lengthwhile (i--) {this.deps[i].removeSub(this)}this.active = false}}}
