依赖收集的目的就是为了当修改数据时可以对相关的依赖派发更新

  1. /**
  2. * Define a reactive property on an Object.
  3. */
  4. export function defineReactive (
  5. obj: Object,
  6. key: string,
  7. val: any,
  8. customSetter?: ?Function,
  9. shallow?: boolean
  10. ) {
  11. // ...
  12. // 当shallow为false时会把新设置的值变成一个响应式对象
  13. let childOb = !shallow && observe(val)
  14. Object.defineProperty(obj, key, {
  15. enumerable: true,
  16. configurable: true,
  17. get: function reactiveGetter () {
  18. // ...
  19. },
  20. set: function reactiveSetter (newVal) {
  21. const value = getter ? getter.call(obj) : val
  22. /* eslint-disable no-self-compare */
  23. if (newVal === value || (newVal !== newVal && value !== value)) {
  24. return
  25. }
  26. /* eslint-enable no-self-compare */
  27. if (process.env.NODE_ENV !== 'production' && customSetter) {
  28. customSetter()
  29. }
  30. // #7981: for accessor properties without setter
  31. if (getter && !setter) return
  32. if (setter) {
  33. setter.call(obj, newVal)
  34. } else {
  35. val = newVal
  36. }
  37. childOb = !shallow && observe(newVal)
  38. dep.notify() // 通知所有的订阅者
  39. }
  40. })
  41. }

过程分析

当在组件中对响应的数据做了修改就会触发setter,调用dep.notify()方法
定义在src/core/observer/dep.js

  1. /**
  2. * A dep is an observable that can have multiple
  3. * directives subscribing to it.
  4. */
  5. export default class Dep {
  6. // ...
  7. notify () {
  8. // stabilize the subscriber list first
  9. const subs = this.subs.slice()
  10. if (process.env.NODE_ENV !== 'production' && !config.async) {
  11. // subs aren't sorted in scheduler if not running async
  12. // we need to sort them now to make sure they fire in correct
  13. // order
  14. subs.sort((a, b) => a.id - b.id)
  15. }
  16. // 遍历所有的subs,也就是Watcher的实例数组,然后调用每一个watcher的update方法
  17. for (let i = 0, l = subs.length; i < l; i++) {
  18. subs[i].update()
  19. }
  20. }
  21. }

update方法定义在src/core/observer/watcher.js中

  1. /**
  2. * Subscriber interface.
  3. * Will be called when a dependency changes.
  4. */
  5. update () {
  6. /* istanbul ignore else */
  7. if (this.lazy) {
  8. this.dirty = true
  9. } else if (this.sync) {
  10. this.run()
  11. } else {
  12. queueWatcher(this)
  13. }
  14. }

queueWatcher方法定义在src/core/observer/scheduler.js中

  1. // 引入队列的概念
  2. // 不是每次数据改变都触发watcher的回调,而是把这些watcher先添加到一个队列里,然后在nextTick后执行flushSchedulerQueue
  3. const queue: Array<Watcher> = []
  4. let has: { [key: number]: ?true } = {} // 保证同一个Watcher只添加一次
  5. let waiting = false
  6. let flushing = false
  7. /**
  8. * Push a watcher into the watcher queue.
  9. * Jobs with duplicate IDs will be skipped unless it's
  10. * pushed when the queue is being flushed.
  11. */
  12. export function queueWatcher (watcher: Watcher) {
  13. const id = watcher.id
  14. if (has[id] == null) {
  15. has[id] = true
  16. if (!flushing) {
  17. queue.push(watcher)
  18. } else {
  19. // if already flushing, splice the watcher based on its id
  20. // if already past its id, it will be run next immediately.
  21. // flushing == true 用户可能添加了新的watcher
  22. // 从后往前找,找到第一个待插入watcher的id比当前队列中watcher的id大的位置,把watcher按照id插入到队列中,因此queue的长度发生了变化
  23. let i = queue.length - 1
  24. while (i > index && queue[i].id > watcher.id) {
  25. i--
  26. }
  27. queue.splice(i + 1, 0, watcher)
  28. }
  29. // queue the flush
  30. if (!waiting) { // 使用waiting保证对nextTick(flushSchedulerQueue)的调用逻辑只有一次
  31. waiting = true
  32. if (process.env.NODE_ENV !== 'production' && !config.async) {
  33. flushSchedulerQueue()
  34. return
  35. }
  36. nextTick(flushSchedulerQueue) // 异步执行flushSchedulerQueue
  37. }
  38. }
  39. }

flushSchedulerQueue定义在src/core/observer/scheduler.js中

  1. let flushing = false
  2. let index = 0
  3. /**
  4. * Flush both queues and run the watchers.
  5. */
  6. function flushSchedulerQueue () {
  7. currentFlushTimestamp = getNow()
  8. flushing = true
  9. let watcher, id
  10. // Sort queue before flush.
  11. // This ensures that:
  12. // 1. Components are updated from parent to child. (because parent is always
  13. // created before the child)
  14. // 2. A component's user watchers are run before its render watcher (because
  15. // user watchers are created before the render watcher)
  16. // 3. If a component is destroyed during a parent component's watcher run,
  17. // its watchers can be skipped.
  18. // 队列排序
  19. // 对队列做从小到大的排序,是为了确保以下内容
  20. // 1.组件的更新由父到子;因为父组件的创建过程是在子的前边,所以watcher的创建也是先父后子,执行顺序也应该保持先父后子
  21. // 2.用户自定义watcher要优先于渲染watcher执行;因为用户自定义watcher是在渲染watcher之前创建的
  22. // 3.如果一个组件在父组件的watcher执行期间被销毁,那么它对应的watcher执行都可以被跳过,所以父组件的watcher应该先执行
  23. queue.sort((a, b) => a.id - b.id)
  24. // do not cache length because more watchers might be pushed
  25. // as we run existing watchers
  26. // 队列遍历
  27. // 遍历时每次都会对queue.length求值,因为在watcher.run()时用户可能会再次添加新的watcher,然后再次执行到queueWatcher
  28. for (index = 0; index < queue.length; index++) {
  29. watcher = queue[index]
  30. if (watcher.before) {
  31. watcher.before()
  32. }
  33. id = watcher.id
  34. has[id] = null
  35. watcher.run()
  36. // in dev build, check and stop circular updates.
  37. if (process.env.NODE_ENV !== 'production' && has[id] != null) {
  38. circular[id] = (circular[id] || 0) + 1
  39. if (circular[id] > MAX_UPDATE_COUNT) {
  40. warn(
  41. 'You may have an infinite update loop ' + (
  42. watcher.user
  43. ? `in watcher with expression "${watcher.expression}"`
  44. : `in a component render function.`
  45. ),
  46. watcher.vm
  47. )
  48. break
  49. }
  50. }
  51. }
  52. // keep copies of post queues before resetting state
  53. const activatedQueue = activatedChildren.slice()
  54. const updatedQueue = queue.slice()
  55. // 状态恢复
  56. resetSchedulerState()
  57. // call component updated and activated hooks
  58. callActivatedHooks(activatedQueue)
  59. callUpdatedHooks(updatedQueue)
  60. // devtool hook
  61. /* istanbul ignore if */
  62. if (devtools && config.devtools) {
  63. devtools.emit('flush')
  64. }
  65. }

resetSchedulerState方法定义在src/core/observer/scheduler.js中

  1. const queue: Array<Watcher> = []
  2. const activatedChildren: Array<Component> = []
  3. let has: { [key: number]: ?true } = {}
  4. let circular: { [key: number]: number } = {}
  5. let waiting = false
  6. let flushing = false
  7. let index = 0
  8. /**
  9. * Reset the scheduler's state.
  10. * 把这些控制流程状态的一些变量恢复到初始值,把watcher队列清空
  11. */
  12. function resetSchedulerState () {
  13. index = queue.length = activatedChildren.length = 0
  14. has = {}
  15. if (process.env.NODE_ENV !== 'production') {
  16. circular = {}
  17. }
  18. waiting = flushing = false
  19. }

watcher.run()的逻辑定义在src/core/observer/watcher.js中

  1. class Watcher {
  2. // ...
  3. /**
  4. * Scheduler job interface.
  5. * Will be called by the scheduler.
  6. */
  7. run () {
  8. if (this.active) {
  9. // 获取当前值
  10. const value = this.get()
  11. // 满足新旧值不等、新值是对象类型、deep模式之一
  12. if (
  13. value !== this.value ||
  14. // Deep watchers and watchers on Object/Arrays should fire even
  15. // when the value is the same, because the value may
  16. // have mutated.
  17. isObject(value) ||
  18. this.deep
  19. ) {
  20. // set new value
  21. const oldValue = this.value
  22. this.value = value
  23. if (this.user) {
  24. const info = `callback for watcher "${this.expression}"`
  25. // 这就是当添加自定义watcher的时候能在回调函数的参数中拿到新旧值的原因
  26. invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info)
  27. } else {
  28. this.cb.call(this.vm, value, oldValue)
  29. }
  30. }
  31. }
  32. }
  33. // ...
  34. }

对于渲染watcher,它在执行this.get()方法求值的时候会执行getter方法
定义在src/core/instance/lifecycle.js中

  1. updateComponent = () => {
  2. vm._update(vm._render(), hydrating)
  3. }

这就是当去修改组件相关的响应式数据的时候会触发组件重新渲染的原因
接着会重新执行patch的过程,但和首次渲染有所不同

当数据发生变化的时候,触发 setter 逻辑,把在依赖过程中订阅的的所有观察者,也就是 watcher,都触发它们的 update 过程,这个过程又利用了队列做了进一步优化,在 nextTick 后执行所有 watcher 的 run,最后执行它们的回调函数