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 值为旧数据。

observer/watcher.js - 图1

  1. //lifecycle.js
  2. new Watcher(vm, updateComponent, noop, {
  3. before () {
  4. if (vm._isMounted && !vm._isDestroyed) {
  5. callHook(vm, 'beforeUpdate')
  6. }
  7. }
  8. }, true /* isRenderWatcher */)
  1. /* @flow */
  2. import {
  3. warn,
  4. remove,
  5. isObject,
  6. parsePath,
  7. _Set as Set,
  8. handleError,
  9. noop
  10. } from '../util/index'
  11. import { traverse } from './traverse'
  12. import { queueWatcher } from './scheduler'
  13. import Dep, { pushTarget, popTarget } from './dep'
  14. import type { SimpleSet } from '../util/index'
  15. let uid = 0
  16. /**
  17. * A watcher parses an expression, collects dependencies,
  18. * and fires callback when the expression value changes.
  19. * This is used for both the $watch() api and directives.
  20. */
  21. /*
  22. * 一个解析表达式,进行依赖收集的观察者,
  23. * 同时在表达式数据变更时触发回调函数。
  24. * 它被用于$watch() api以及指令
  25. */
  26. // 当是渲染watcher时,expOrFn是updateComponent,即重新渲染执行render(_update)
  27. // 当是计算watcher时,expOrFn是计算属性的计算方法
  28. // 当是侦听器watcher时,expOrFn是watch属性的名字,this.cb就是watch的handler属性
  29. // 对于渲染watcher和计算watcher来说,expOrFn的值是一个函数,可以直接设置getter
  30. // 对于侦听器watcher来说,expOrFn是watch属性的名字,会使用parsePath函数解析路径,获取组件上该属性的值(运行getter)
  31. /*
  32. * 需要特别注意的是,在一个组件中,也就是一个vue实例内,
  33. * 只有一个render watcher
  34. * 也就是说一个组件内的变量改变导致dom更新,触发的都是同一个render watcher
  35. * computed的watcher,则会在this._computedWatchers中
  36. * */
  37. export default class Watcher {
  38. vm: Component;
  39. expression: string;
  40. cb: Function;
  41. id: number;
  42. deep: boolean;
  43. user: boolean;
  44. lazy: boolean;
  45. sync: boolean;
  46. dirty: boolean;
  47. active: boolean;
  48. deps: Array<Dep>;
  49. newDeps: Array<Dep>;
  50. depIds: SimpleSet;
  51. newDepIds: SimpleSet;
  52. before: ?Function;
  53. getter: Function;
  54. value: any;
  55. constructor (
  56. vm: Component,
  57. expOrFn: string | Function,
  58. cb: Function,
  59. options?: ?Object,
  60. isRenderWatcher?: boolean
  61. ) {
  62. this.vm = vm
  63. if (isRenderWatcher) {
  64. vm._watcher = this
  65. }
  66. /*_watchers存放订阅者实例*/
  67. vm._watchers.push(this)
  68. // options
  69. if (options) {
  70. this.deep = !!options.deep //是否启用深度监听
  71. this.user = !!options.user //主要用于错误处理,侦听器 watcher的 user为true,其他基本为false
  72. this.lazy = !!options.lazy //惰性求值,当属于计算属性watcher时为true
  73. this.sync = !!options.sync //标记为同步计算,三大类型暂无
  74. this.before = options.before
  75. } else {
  76. this.deep = this.user = this.lazy = this.sync = false
  77. }
  78. this.cb = cb
  79. this.id = ++uid // uid for batching
  80. this.active = true
  81. this.dirty = this.lazy // for lazy watchers
  82. this.deps = []
  83. this.newDeps = []
  84. this.depIds = new Set()
  85. this.newDepIds = new Set()
  86. this.expression = process.env.NODE_ENV !== 'production'
  87. ? expOrFn.toString()
  88. : ''
  89. // parse expression for getter
  90. // 解析expOrFn,赋值给this.getter
  91. if (typeof expOrFn === 'function') {
  92. this.getter = expOrFn
  93. } else {
  94. /* 把表达式expOrFn解析成getter */
  95. this.getter = parsePath(expOrFn)
  96. if (!this.getter) {
  97. this.getter = noop
  98. process.env.NODE_ENV !== 'production' && warn(
  99. `Failed watching path: "${expOrFn}" ` +
  100. 'Watcher only accepts simple dot-delimited paths. ' +
  101. 'For full control, use a function instead.',
  102. vm
  103. )
  104. }
  105. }
  106. this.value = this.lazy ?
  107. undefined :
  108. this.get()
  109. // 当为computed属性时,lazy为true,
  110. // 这时不会在new Watcher()执行get函数,即为惰性求值
  111. // 其他的时候都会在实例化时执行get,进行依赖收集
  112. }
  113. /**
  114. * Evaluate the getter, and re-collect dependencies.
  115. */
  116. get() {
  117. pushTarget(this) // 将当前watcher实例放入Dep.target中
  118. let value
  119. const vm = this.vm
  120. try {
  121. value = this.getter.call(vm, vm)
  122. // 执行getter函数即是获取了一次observer中的值
  123. // 触发了observer的get中的dep.depend()
  124. // 将Dep.target的watcher实例push到dep.subs中
  125. } catch (e) {
  126. if (this.user) {
  127. // user为true,那即是侦听器watch
  128. // 如果是watch的值不存在或有问题
  129. handleError(e, vm, `getter for watcher "${this.expression}"`)
  130. } else {
  131. throw e
  132. }
  133. } finally {
  134. // "touch" every property so they are all tracked as
  135. // dependencies for deep watching
  136. /*如果存在deep,则触发每个深层对象的依赖,追踪其变化*/
  137. if (this.deep) {
  138. // 深度监听,就是watch的deep选项
  139. /*递归每一个对象或者数组,
  140. *触发它们的getter,
  141. *使得对象或数组的每一个成员都被依赖收集,
  142. *形成一个“深(deep)”依赖关系
  143. */
  144. traverse(value)
  145. }
  146. popTarget()
  147. this.cleanupDeps()
  148. }
  149. return value
  150. }
  151. /**
  152. * Add a dependency to this directive.
  153. */
  154. /*添加一个依赖关系到Deps集合中*/
  155. addDep(dep: Dep) {
  156. const id = dep.id
  157. if (!this.newDepIds.has(id)) {
  158. // 同时实例中也会保存添加过当前实例的dep和dep的id
  159. this.newDepIds.add(id)
  160. this.newDeps.push(dep)
  161. if (!this.depIds.has(id)) {
  162. dep.addSub(this)
  163. }
  164. }
  165. }
  166. /**
  167. * Clean up for dependency collection.
  168. */
  169. /*清理依赖收集*/
  170. cleanupDeps() {
  171. let i = this.deps.length
  172. while (i--) {
  173. const dep = this.deps[i]
  174. if (!this.newDepIds.has(dep.id)) {
  175. dep.removeSub(this)
  176. }
  177. }
  178. let tmp = this.depIds
  179. this.depIds = this.newDepIds
  180. this.newDepIds = tmp
  181. this.newDepIds.clear()
  182. tmp = this.deps
  183. this.deps = this.newDeps
  184. this.newDeps = tmp
  185. this.newDeps.length = 0
  186. }
  187. /**
  188. * Subscriber interface.
  189. * Will be called when a dependency changes.
  190. */
  191. /*
  192. 调度者接口,
  193. 当依赖发生改变的时候进行回调。
  194. */
  195. update() {
  196. /* istanbul ignore else */
  197. if (this.lazy) {
  198. // 脏值标记
  199. this.dirty = true
  200. } else if (this.sync) {
  201. /*同步则执行run直接渲染视图*/
  202. this.run()
  203. } else {
  204. /*异步推送到观察者队列中,由调度者调用。*/
  205. queueWatcher(this)
  206. }
  207. }
  208. /**
  209. * Scheduler job interface.
  210. * Will be called by the scheduler.
  211. */
  212. /*
  213. 调度者工作接口,将被调度者回调。
  214. */
  215. run() {
  216. if (this.active) {
  217. const value = this.get()
  218. if (
  219. value !== this.value ||
  220. // Deep watchers and watchers on Object/Arrays should fire even
  221. // when the value is the same, because the value may
  222. // have mutated.
  223. // 即便值相同,拥有Deep属性的观察者以及在对象/数组上的观察者应该被触发更新,
  224. // 因为它们的值可能发生改变。
  225. isObject(value) ||
  226. this.deep
  227. ) {
  228. // set new value
  229. const oldValue = this.value
  230. /*设置新的值*/
  231. this.value = value
  232. if (this.user) {
  233. // user 为真时,使用了侦听者watch
  234. try {
  235. this.cb.call(this.vm, value, oldValue)
  236. } catch (e) {
  237. handleError(e, this.vm, `callback for watcher "${this.expression}"`)
  238. }
  239. } else {
  240. /*触发回调渲染视图*/
  241. this.cb.call(this.vm, value, oldValue)
  242. }
  243. }
  244. }
  245. }
  246. /**
  247. * Evaluate the value of the watcher.
  248. * This only gets called for lazy watchers.
  249. */
  250. /*获取观察者的值
  251. * 只会被用于惰性求值的观察者
  252. * */
  253. evaluate() {
  254. this.value = this.get()
  255. // 用于标记脏数据的变量
  256. this.dirty = false
  257. }
  258. /**
  259. * Depend on all deps collected by this watcher.
  260. */
  261. /*收集该watcher的所有deps依赖*/
  262. depend() {
  263. let i = this.deps.length
  264. while (i--) {
  265. this.deps[i].depend()
  266. }
  267. }
  268. /**
  269. * Remove self from all dependencies' subscriber list.
  270. */
  271. /* 将自身从所有依赖收集订阅列表删除 */
  272. teardown() {
  273. if (this.active) {
  274. // remove self from vm's watcher list
  275. // this is a somewhat expensive operation so we skip it
  276. // if the vm is being destroyed.
  277. // 将self从vm的watcher列表中移除,
  278. // 这是一个有点耗时的操作,
  279. // 所以如果vm正在被销毁,我们就跳过它。
  280. // 从watcher列表移除自身
  281. if (!this.vm._isBeingDestroyed) {
  282. remove(this.vm._watchers, this)
  283. }
  284. let i = this.deps.length
  285. while (i--) {
  286. this.deps[i].removeSub(this)
  287. }
  288. this.active = false
  289. }
  290. }
  291. }