1. let uid = 0
    2. /**
    3. * A watcher parses an expression, collects dependencies,
    4. * and fires callback when the expression value changes.
    5. * This is used for both the $watch() api and directives.
    6. */
    7. export default class Watcher {
    8. vm: Component; // 所处在的 Vue 实例
    9. expression: string; // 在非生产环境下该属性的值为表达式(expOrFn)的字符串表示,在生产环境下其值为空字符串
    10. cb: Function; // 被观察者发生变化后的响应回调
    11. id: number; // Watcher 的 id,根据实例化的时机按顺序递增
    12. deep: boolean; // 用来告诉当前观察者实例对象是否是深度观测,在编写 watch 就有此选项,用于决定是否深度观测这个值,默认为 false
    13. user: boolean; // 用来标识当前观察者实例对象是 开发者定义的 还是 内部定义的,前者是 options 中的 watch 和 $watch,后者是渲染函数的观察者、计算属性的观察者等
    14. lazy: boolean; // computed 时用到,用来标识当前观察者实例对象是否是计算属性的观察者
    15. sync: boolean; // 用来告诉观察者当数据变化时是否同步求值并执行回调,一般情况下不会选择同步,一般是选择异步来计算
    16. dirty: boolean; // 当 lazy 为 true 时才会有效,代表当前的值是否是同步的
    17. active: boolean; // 它标识着该观察者实例对象是否是激活状态
    18. deps: Array<Dep>; // 当前 Watcher 所在的 Dep 集合
    19. newDeps: Array<Dep>; // 用于缓存最新依赖收集过程中当前 Watch 所在的 Dep 集合,会与旧的 Dep 集合对比,进行相应的依赖增改,提高性能和一致性,在每次依赖收集结束后会清空
    20. depIds: SimpleSet; // 当前 Watcher 所在的 Dep 的 id 集合
    21. newDepIds: SimpleSet; // 用于缓存最新依赖收集过程中当前 Watch 所在的 Dep 集合,会与旧的 Dep 集合对比,进行相应的依赖增改,提高性能和一致性,在每次依赖收集结束后会清空
    22. before: ?Function; // 可以理解为 Watcher 实例的钩子,当数据变化之后,触发更新之前,调用在创建渲染函数的观察者实例对象时传递的 before 选项
    23. getter: Function; // 触发依赖收集的开启函数,如果是组件的 Watcher,存放的就是渲染函数,如果是 watch 或者 $watch,则存放的是获取监听的值的函数,如果是 computed,则放的是计算函数
    24. value: any; // watch 的值,或者 computed 的结果(可能非最新)
    25. constructor (
    26. vm: Component, // vue 实例
    27. expOrFn: string | Function, // 要观察的表达式或者函数
    28. cb: Function, // 响应的回调函数
    29. options?: ?Object, // 传给当前观察者对象的一些参数
    30. isRenderWatcher?: boolean // 标识该观察者实例是否是渲染函数的观察者
    31. ) {
    32. this.vm = vm // 观察者所在的实例
    33. if (isRenderWatcher) {
    34. vm._watcher = this // 如果是作为实例的渲染函数的观察者,则将当前观察者对象赋值到 _watcher
    35. }
    36. vm._watchers.push(this) // 将当前观察者对象添加所在实例的观察者队列里
    37. // 参数初始化
    38. if (options) {
    39. this.deep = !!options.deep
    40. this.user = !!options.user
    41. this.lazy = !!options.lazy
    42. this.sync = !!options.sync
    43. this.before = options.before
    44. } else {
    45. this.deep = this.user = this.lazy = this.sync = false
    46. }
    47. this.cb = cb
    48. this.id = ++uid // uid for batching
    49. this.active = true
    50. this.dirty = this.lazy // for lazy watchers
    51. this.deps = []
    52. this.newDeps = []
    53. this.depIds = new Set()
    54. this.newDepIds = new Set()
    55. this.expression = process.env.NODE_ENV !== 'production'
    56. ? expOrFn.toString()
    57. : ''
    58. // expOrFn 初始化,最终都会转成一个 function
    59. // 如果 expOrFn 是函数则不必多说,如果是 string,则返回一个用于取该键值的函数 function(obj) {return obj[keyPath]}
    60. if (typeof expOrFn === 'function') {
    61. this.getter = expOrFn
    62. } else {
    63. // 返回取键值的函数,同时支持 'a.b.c',这类键值,但如果 exp 不符合键值的规则,则会返回一个 undefined
    64. this.getter = parsePath(expOrFn)
    65. // 如果 exp 不符合键值的规则,则将 getter 赋一个空函数,同时抛出错误
    66. if (!this.getter) {
    67. this.getter = noop
    68. process.env.NODE_ENV !== 'production' && warn(
    69. `Failed watching path: "${expOrFn}" ` +
    70. 'Watcher only accepts simple dot-delimited paths. ' +
    71. 'For full control, use a function instead.',
    72. vm
    73. )
    74. }
    75. }
    76. // https://github.com/vuejs/vue/issues/7767 computed
    77. // https://github.com/vuejs/vue/issues/8446 back to lazy
    78. // 如果是惰性的则返回 undefined 反之直接获取一次,获取 exp 对应的值,或者执行以下传进来的 expOrFn
    79. this.value = this.lazy
    80. ? undefined
    81. : this.get()
    82. }
    83. /**
    84. * Evaluate the getter, and re-collect dependencies.
    85. */
    86. //
    87. // 获取对应的值,或者触发对应的函数来获取其中的值来触发依赖收集
    88. get () {
    89. // Dep.target = 当前的 Watcher
    90. pushTarget(this)
    91. let value
    92. const vm = this.vm
    93. try {
    94. value = this.getter.call(vm, vm)
    95. } catch (e) {
    96. if (this.user) {
    97. handleError(e, vm, `getter for watcher "${this.expression}"`)
    98. } else {
    99. throw e
    100. }
    101. } finally {
    102. // "touch" every property so they are all tracked as
    103. // dependencies for deep watching
    104. // 如果是深度的,怎深度遍历 value,触发其深层值的 get,收集依赖
    105. if (this.deep) {
    106. traverse(value)
    107. }
    108. // 将当前的 Watcher 推出 target 队列
    109. popTarget()
    110. // 清楚一次性的 Dep 收集
    111. this.cleanupDeps()
    112. }
    113. return value
    114. }
    115. /**
    116. * Add a dependency to this directive.
    117. */
    118. // 触发被监听值的 get 后,触发依赖收集之后会到此处开始收集
    119. addDep (dep: Dep) {
    120. // 获取对应 Dep 集合的id
    121. const id = dep.id
    122. // 如果在本次 get 中收集过了则不再收集,反之收集
    123. if (!this.newDepIds.has(id)) {
    124. // 收集 ID
    125. this.newDepIds.add(id)
    126. // 收集 Dep 实例
    127. this.newDeps.push(dep)
    128. // 相较于 newDepIds,depIds 是真正长期有效的
    129. // newDepIds, 仅在一次 get 中有效
    130. if (!this.depIds.has(id)) {
    131. dep.addSub(this)
    132. }
    133. }
    134. }
    135. /**
    136. * Clean up for dependency collection.
    137. */
    138. // 校准 Dep,使得依赖关系保持一致性
    139. cleanupDeps () {
    140. let i = this.deps.length
    141. while (i--) {
    142. const dep = this.deps[i]
    143. // 移除上一次收集到当前 Watcher,但本次没有收集到的 Dep
    144. if (!this.newDepIds.has(dep.id)) {
    145. dep.removeSub(this)
    146. }
    147. }
    148. // 将最新的依赖关系覆盖之前的依赖关系
    149. let tmp = this.depIds
    150. this.depIds = this.newDepIds
    151. this.newDepIds = tmp
    152. this.newDepIds.clear()
    153. tmp = this.deps
    154. this.deps = this.newDeps
    155. this.newDeps = tmp
    156. this.newDeps.length = 0
    157. }
    158. /**
    159. * Subscriber interface.
    160. * Will be called when a dependency changes.
    161. */
    162. // Dep.notify 将触发的更新函数
    163. update () {
    164. /* istanbul ignore else */
    165. // 如果获取值的惰性的(computed),则修改值的状态为非最新值
    166. // 初次之外如果是同步更新的条件下,则直接进行对应的响应更新
    167. // 如果是正常的异步更新的条件下,则将当前 Watcher 添加到异步更新的 Watcher 队列中
    168. if (this.lazy) {
    169. this.dirty = true
    170. } else if (this.sync) {
    171. this.run()
    172. } else {
    173. queueWatcher(this)
    174. }
    175. }
    176. /**
    177. * Scheduler job interface.
    178. * Will be called by the scheduler.
    179. */
    180. // 状态更新后,执行的响应式操作
    181. run () {
    182. // 如果当前 Watcher 还处于激活状态,才继续
    183. if (this.active) {
    184. // 获取更新后的值,get 会触发依赖收集
    185. // 如果是组件的 Watcher 则会触发渲染更新,返回空,
    186. // 如果是 普通的 watch,则会尝试获取值,返回对应的值
    187. // 如果是 computed,则会进行计算返回对应的值
    188. const value = this.get()
    189. // 如果值不相等,或者即使值相同,深度观察者和对象/数组上的观察者也应触发,因为该值可能已发生变化。
    190. if (
    191. value !== this.value ||
    192. // Deep watchers and watchers on Object/Arrays should fire even
    193. // when the value is the same, because the value may
    194. // have mutated.
    195. isObject(value) ||
    196. this.deep
    197. ) {
    198. // 设置新的值
    199. const oldValue = this.value
    200. this.value = value
    201. // 执行回调函数
    202. // 如果当前的 Watcher 是又开发者自定义的,比如 $watch, watch, computed,则加上 try catch 来捕获这可能的错误
    203. if (this.user) {
    204. try {
    205. this.cb.call(this.vm, value, oldValue)
    206. } catch (e) {
    207. handleError(e, this.vm, `callback for watcher "${this.expression}"`)
    208. }
    209. } else {
    210. this.cb.call(this.vm, value, oldValue)
    211. }
    212. }
    213. }
    214. }
    215. /**
    216. * Evaluate the value of the watcher.
    217. * This only gets called for lazy watchers.
    218. */
    219. evaluate () {
    220. this.value = this.get()
    221. this.dirty = false
    222. }
    223. /**
    224. * Depend on all deps collected by this watcher.
    225. */
    226. depend () {
    227. let i = this.deps.length
    228. while (i--) {
    229. this.deps[i].depend()
    230. }
    231. }
    232. /**
    233. * Remove self from all dependencies' subscriber list.
    234. */
    235. // 取消监听
    236. teardown () {
    237. // 如果当前 watcher 实例属于活跃状态才进行取消监听
    238. if (this.active) {
    239. // remove self from vm's watcher list
    240. // this is a somewhat expensive operation so we skip it
    241. // if the vm is being destroyed.
    242. // 如果当前 vue 实例已经被销毁了则跳过移除,因为移除操作十分消耗性能
    243. if (!this.vm._isBeingDestroyed) {
    244. remove(this.vm._watchers, this)
    245. }
    246. // 通知对应的 Deps 移除自身
    247. let i = this.deps.length
    248. while (i--) {
    249. this.deps[i].removeSub(this)
    250. }
    251. // 将当前 watcher 设置为不活跃状态
    252. this.active = false
    253. }
    254. }
    255. }