初始化数据

  1. // TODO: 初始化数据
  2. function initData (vm: Component) {
  3. let data = vm.$options.data
  4. // data 是不是函数,是函数就让他执行获取数据 挂到 vm._data
  5. data = vm._data = typeof data === 'function'
  6. ? getData(data, vm)
  7. : data || {}
  8. // 观测数据
  9. observe(data, true /* asRootData */)
  10. }

观测数据

  1. /**
  2. * 响应式处理的真正入口
  3. * 为对象创建观察者实例,如果对象已经被观察过,则返回已有的观察者实例,否则创建新的观察者实例
  4. * @param {object} value
  5. * @param {boolean} asRootData
  6. * @returns
  7. */
  8. export function observe (value, asRootData){
  9. // 如果是对象并且不是 vnode 的时候才观测
  10. if (!isObject(value) || value instanceof VNode) {
  11. return
  12. }
  13. let ob
  14. // value 对象上存在 __ob__ 属性,则表示已经做过观察了,直接返回 __ob__ 属性
  15. if(hasOwn(value, '__ob__') && value.__ob__ instanceof Observer){
  16. ob = value.__ob__
  17. }
  18. if (
  19. shouldObserve &&
  20. !isServerRendering() && // 是否需要被观测
  21. (Array.isArray(value) || isPlainObject(value)) && // 是一个数组或对象
  22. Object.isExtensible(value) && // 支持 defineProperty
  23. !value._isVue // 不是vue 实例
  24. ) {
  25. // 创建观察者实例
  26. ob = new Observer(value)
  27. }
  28. // 记录观测数量
  29. if (asRootData && ob) {
  30. ob.vmCount++
  31. }
  32. return ob
  33. }

创建观察者实例

  1. /**
  2. * 2.观察者类,会被附加到每个被观察的对象上,value.__ob__ = this
  3. * 而对象的各个属性则会被转换成 getter/setter,并收集依赖和通知更新
  4. */
  5. export class Observer {
  6. constructor (value) {
  7. this.value = value // ob.value
  8. this.dep = new Dep() // 给对象和数组实例化一个 dep 属性 $set 之后就会触发他们自己的 update 方法
  9. this.vmCount = 0
  10. // data.__ob__ = this
  11. // 将当前 Observer 实例挂载到 data 上 __ob__ 主要是用来取一些方法,这样被劫持过的所有属性都有 __ob__
  12. def(value, '__ob__', this) // 不可枚举 TODO:
  13. /**
  14. * value 为数组
  15. * hasProto = '__proto__' in {}
  16. * 用于判断对象是否存在 __proto__ 属性,通过 obj.__proto__ 可以访问对象的原型链
  17. * 但由于 __proto__ 不是标准属性,所以有些浏览器不支持,比如 IE6-10,Opera10.1
  18. * 为什么要判断,是因为一会儿要通过 __proto__ 操作数据的原型链
  19. * 覆盖数组默认的七个原型方法,以实现数组响应式
  20. */
  21. if (Array.isArray(value)) { // 数组劫持 切片编程
  22. // 如果支持原型链
  23. if (hasProto) {
  24. protoAugment(value, arrayMethods)
  25. } else {
  26. // 不支持就拷贝一份
  27. copyAugment(value, arrayMethods, arrayKeys)
  28. }
  29. // 观测数组 如果数组中的数据是对象类型,需要监控对象的变化 arr: [{name: 'dabao'}, {age: 3}]
  30. this.observeArray(value)
  31. } else { // 对象劫持
  32. this.walk(value)
  33. }
  34. }
  35. /**
  36. * 遍历对象上的每个 key,为每个 key 设置响应式
  37. * 仅当值为对象时才会走这里
  38. */
  39. walk (obj) {
  40. const keys = Object.keys(obj)
  41. for (let i = 0; i < keys.length; i++) {
  42. defineReactive(obj, keys[i])
  43. }
  44. }
  45. /**
  46. * 遍历数组的每一项.目的是对数组中的数组和数组中的对象再次劫持,递归
  47. */
  48. observeArray (items) {
  49. for (let i = 0, l = items.length; i < l; i++) {
  50. observe(items[i])
  51. }
  52. }
  53. }
  54. /**
  55. * 设置 target.__proto__ 的原型对象为 src
  56. * 比如 数组对象,arr.__proto__ = arrayMethods
  57. * @param {Array} target value
  58. * @param {object} src arrayMethods
  59. */
  60. function protoAugment (target, src) {
  61. target.__proto__ = src
  62. }
  63. /**
  64. * 在目标对象上定义指定属性
  65. * 比如数组:为数组对象定义那七个方法
  66. */
  67. function copyAugment (target: Object, src: Object, keys: Array<string>) {
  68. for (let i = 0, l = keys.length; i < l; i++) {
  69. const key = keys[i]
  70. def(target, key, src[key])
  71. }
  72. }
  73. /**
  74. * Define a property.
  75. */
  76. export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  77. Object.defineProperty(obj, key, {
  78. value: val,
  79. enumerable: !!enumerable,
  80. writable: true,
  81. configurable: true
  82. })
  83. }

拦截 obj[key] 的读取和设置操作

  1. /**
  2. * 拦截 obj[key] 的读取和设置操作:
  3. * 1、在第一次读取时收集依赖,比如执行 render 函数生成虚拟 DOM 时会有读取操作
  4. * 2、在更新时设置新值并通知依赖更新
  5. */
  6. export function defineReactive (
  7. obj,
  8. key,
  9. val,
  10. customSetter,
  11. shallow
  12. ) {
  13. // 实例化 dep,一个 key 一个 dep
  14. const dep = new Dep()
  15. // 获取 obj[key] 的属性描述符,发现它是不可配置对象的话直接 return
  16. const property = Object.getOwnPropertyDescriptor(obj, key)
  17. // 数据必须支持 可配置 object.freeze 包装的对象 configurable 为 false
  18. if (property && property.configurable === false) {
  19. return
  20. }
  21. /**
  22. * getter 和 setter 是为了兼容这种情况
  23. * a: {
  24. * get(){},
  25. * set(){},
  26. * }
  27. */
  28. const getter = property && property.get
  29. const setter = property && property.set
  30. // 不存在 getter 只有 setter
  31. if ((!getter || setter) && arguments.length === 2) {
  32. val = obj[key]
  33. }
  34. // 递归调用,处理 val 即 obj[key] 的值为对象的情况,保证对象中的所有 key 都被观察
  35. let childOb = !shallow && observe(val)
  36. Object.defineProperty(obj, key, {
  37. enumerable: true,
  38. configurable: true,
  39. get: function reactiveGetter () {
  40. // 取值
  41. const value = getter ? getter.call(obj) : val
  42. /**
  43. * Dep.target 为 Dep 类的一个静态属性,值为 watcher,在实例化 Watcher 时会被设置
  44. * 实例化 Watcher 时会执行 new Watcher 时传递的回调函数(computed 除外,因为它懒执行)
  45. * 而回调函数中如果有 vm.key 的读取行为,则会触发这里的 读取 拦截,进行依赖收集
  46. * 回调函数执行完以后又会将 Dep.target 设置为 null,避免这里重复收集依赖
  47. */
  48. if (Dep.target) {
  49. // 依赖收集,在 dep 中添加 watcher,也在 watcher 中添加 dep
  50. dep.depend()
  51. // 有子元素 也对其进行依赖收集
  52. if (childOb) {
  53. // 这就是 this.key.chidlKey 被更新时能触发响应式更新的原因
  54. childOb.dep.depend()
  55. // 如果这个 obj[key] 是 数组,则触发数组响应式
  56. if (Array.isArray(value)) { // 数组嵌套数组 [[]] 递归收集 arr:[[[]]] 改最里面的一层
  57. dependArray(value) // 嵌套数组递归收集
  58. }
  59. }
  60. }
  61. return value
  62. },
  63. // set 拦截对 obj[key] 的设置操作
  64. set: function reactiveSetter (newVal) {
  65. // 旧的 obj[key]
  66. const value = getter ? getter.call(obj) : val
  67. // 新旧值一样直接return
  68. if (newVal === value || (newVal !== newVal && value !== value)) {
  69. return
  70. }
  71. // setter 不存在说明该属性是一个只读属性,直接 return
  72. // #7981: for accessor properties without setter
  73. if (getter && !setter) return
  74. // 设置新值
  75. if (setter) {
  76. setter.call(obj, newVal)
  77. } else {
  78. val = newVal
  79. }
  80. // 观测这个新值, 如果赋值的是一个新对象,需要对这个新对象进行劫持
  81. childOb = !shallow && observe(newVal)
  82. // 依赖通知更新
  83. dep.notify()
  84. }
  85. })
  86. }

创建渲染watcher

  1. // lifecycle.js
  2. /**
  3. * TODO:
  4. * 实例化 渲染 Watcher
  5. */
  6. new Watcher(vm, updateComponent, noop, {
  7. before () {
  8. if (vm._isMounted && !vm._isDestroyed) {
  9. callHook(vm, 'beforeUpdate')
  10. }
  11. }
  12. }, true /* isRenderWatcher */)

watcher源码

  1. import Dep, { pushTarget, popTarget } from './dep'
  2. //
  3. let uid = 0
  4. /**
  5. *
  6. * 渲染watcher 每个组件是一个watcher
  7. * Vue1 是每个差值表达式都有一个 wacher,这就导致粒度太细,追踪的依赖就太多了,很消耗内存,
  8. * Vue2.x 和 Vue3.x 都是以整个组件为一个 Watcher,这样状态发生变化后会通知到组件,组件内部在使用虚拟dom 进行对比,降低消耗
  9. * 计算属性、
  10. * vm.$watch
  11. *
  12. * expOrFn 只能是一个 以点分割的路径或者是个函数 对于渲染watcher来说这里的 expOrFn 其实就是 vm._render 生成虚拟 dom、执行 dom-diff、更新真实 dom。
  13. * vm.$watch('a.b.c',function(newValue, oldValue) {console.log('读数据')})
  14. * vm.$watch(function(){return data.a + data.b)},function(newValue, oldValue) {console.log('读数据')})
  15. *
  16. */
  17. export default class Watcher {
  18. // 希望在第一个参数发生变化时触发第二个参数中的函数
  19. constructor (
  20. vm,
  21. expOrFn,
  22. cb,
  23. options,
  24. isRenderWatcher
  25. ) {
  26. this.vm = vm // 当前组件实例
  27. if (isRenderWatcher) { // 渲染watcher
  28. vm._watcher = this
  29. }
  30. vm._watchers.push(this)
  31. // options
  32. if (options) {
  33. this.deep = !!options.deep
  34. this.user = !!options.user
  35. this.lazy = !!options.lazy
  36. this.sync = !!options.sync
  37. this.before = options.before
  38. } else {
  39. this.deep = this.user = this.lazy = this.sync = false
  40. }
  41. this.cb = cb // //用户传入的回调函数 vm.$watch('msg',cb)
  42. this.id = ++uid // watcher的唯一标识 每次产生一个watcher都要有一个唯一标识
  43. this.active = true
  44. this.dirty = this.lazy // for lazy watchers
  45. this.deps = [] // 存 watcher 的依赖收集器
  46. this.newDeps = [] // 存 dep 的
  47. this.depIds = new Set()
  48. this.newDepIds = new Set()
  49. this.expression = process.env.NODE_ENV !== 'production'
  50. ? expOrFn.toString()
  51. : ''
  52. // 判断传入的是表达式还是函数 赋值给 this.getter
  53. if (typeof expOrFn === 'function') {
  54. this.getter = expOrFn
  55. } else {
  56. this.getter = parsePath(expOrFn) // 用parsePath 读取一个字符串的 keyPath
  57. }
  58. // 对于渲染watcher 会立马执行 this.get()进行页面渲染
  59. this.value = this.lazy
  60. ? undefined
  61. : this.get()
  62. }
  63. /**
  64. * 让getter执行,从实例上取值渲染页面 就会调用Object.defineProperty 的get方法,就会走依赖收集的过程
  65. * this.getter 是实例化 watcher 时传递的第二个参数,一个函数或者字符串,比如:updateComponent 或者 parsePath 返回的读取 this.xx 属性值的函数
  66. */
  67. get () {
  68. // 将 Dep.target 属性指向自己
  69. pushTarget(this) // Dep.target = target
  70. // value 为回调函数执行的结果 比如执行 updateComponent,进入 patch 阶段
  71. let value = this.getter.call(vm, vm)
  72. const vm = this.vm
  73. // popTarget()
  74. // 这里清空了Dep.target,为了防止notify触发时,不停的绑定Watcher与Dep,
  75. // 造成代码死循环
  76. Dep.target = null;
  77. this.cleanupDeps()
  78. return value
  79. }
  80. /** 两件事:
  81. * 1、添加 dep 给自己(watcher)
  82. * 2、添加自己(watcher)到 dep
  83. */
  84. addDep (dep) {
  85. // 判重,如果 dep 已经存在则不重复添加
  86. const id = dep.id
  87. if (!this.newDepIds.has(id)) {
  88. // 缓存 dep.id,用于判重
  89. this.newDepIds.add(id)
  90. // 将 dep 存入 watcher
  91. this.newDeps.push(dep)
  92. // 避免在 dep 中重复添加 watcher
  93. if (!this.depIds.has(id)) {
  94. // 将watcher 存入 dep
  95. dep.addSub(this)
  96. }
  97. }
  98. }
  99. /**
  100. * Clean up for dependency collection.
  101. */
  102. cleanupDeps () {
  103. let i = this.deps.length
  104. while (i--) {
  105. const dep = this.deps[i]
  106. if (!this.newDepIds.has(dep.id)) {
  107. dep.removeSub(this)
  108. }
  109. }
  110. let tmp = this.depIds
  111. this.depIds = this.newDepIds
  112. this.newDepIds = tmp
  113. this.newDepIds.clear()
  114. tmp = this.deps
  115. this.deps = this.newDeps
  116. this.newDeps = tmp
  117. this.newDeps.length = 0
  118. }
  119. /**
  120. * 更新方法
  121. * 根据 watcher 配置项,决定接下来怎么走,一般是 queueWatcher
  122. */
  123. update () {
  124. /* istanbul ignore else */
  125. if (this.lazy) {
  126. // 懒执行时走这里,比如 computed
  127. // 将 dirty 置为 true,可以让 computedGetter 执行时重新计算 computed 回调函数的执行结果
  128. this.dirty = true
  129. } else if (this.sync) {
  130. // 同步执行,在使用 vm.$watch 或者 watch 选项时可以传一个 sync 选项,
  131. // 当为 true 时在数据更新时该 watcher 就不走异步更新队列,直接执行 this.run
  132. // 方法进行更新
  133. this.run()
  134. } else {
  135. // 更新时一般都这里,将 watcher 放入 watcher 队列
  136. queueWatcher(this)
  137. }
  138. }
  139. /**
  140. * 由 刷新队列函数 flushSchedulerQueue 调用,完成如下几件事:
  141. * 1、执行实例化 watcher 传递的第二个参数,updateComponent 或者 获取 this.xx 的一个函数(parsePath 返回的函数)
  142. * 2、更新旧值为新值
  143. * 3、执行实例化 watcher 时传递的第三个参数,比如用户 watcher 的回调函数
  144. */
  145. run () {
  146. if (this.active) {
  147. // 调用 this.get 方法
  148. const value = this.get()
  149. if (
  150. value !== this.value ||
  151. isObject(value) ||
  152. this.deep
  153. ) {
  154. // 更新旧值为新值
  155. const oldValue = this.value
  156. this.value = value
  157. if (this.user) {
  158. // 如果是用户 watcher,则执行用户传递的第三个参数 —— 回调函数,参数为 val 和 oldVal
  159. try {
  160. this.cb.call(this.vm, value, oldValue)
  161. } catch (e) {
  162. handleError(e, this.vm, `callback for watcher "${this.expression}"`)
  163. }
  164. } else {
  165. // 渲染 watcher,this.cb = noop,一个空函数
  166. this.cb.call(this.vm, value, oldValue)
  167. }
  168. }
  169. }
  170. }
  171. /**
  172. * Evaluate the value of the watcher.
  173. * This only gets called for lazy watchers.
  174. */
  175. evaluate () {
  176. this.value = this.get()
  177. this.dirty = false
  178. }
  179. /**
  180. * 依赖于这个观察者收集的所有 deps.
  181. */
  182. depend () {
  183. let i = this.deps.length
  184. while (i--) {
  185. this.deps[i].depend()
  186. }
  187. }
  188. /**
  189. * Remove self from all dependencies' subscriber list.
  190. */
  191. teardown () {
  192. if (this.active) {
  193. // remove self from vm's watcher list
  194. // this is a somewhat expensive operation so we skip it
  195. // if the vm is being destroyed.
  196. if (!this.vm._isBeingDestroyed) {
  197. remove(this.vm._watchers, this)
  198. }
  199. let i = this.deps.length
  200. while (i--) {
  201. this.deps[i].removeSub(this)
  202. }
  203. this.active = false
  204. }
  205. }
  206. }

依赖收集器Dep

  1. /* @flow */
  2. import type Watcher from './watcher'
  3. import { remove } from '../util/index'
  4. import config from '../config'
  5. let uid = 0
  6. /**
  7. * 一个 dep 对应一个 obj.key []
  8. * 在读取响应式数据时,负责收集依赖,每个 dep(或者说 obj.key)依赖的 watcher 有哪些
  9. * 在响应式数据更新时,负责通知 dep 中那些 watcher 去执行 update 方法
  10. */
  11. export default class Dep {
  12. constructor () {
  13. this.id = uid++
  14. this.subs = []
  15. }
  16. addSub (sub) { // sub: Watcher
  17. this.subs.push(sub)
  18. }
  19. removeSub (sub) { // sub: Watcher
  20. remove(this.subs, sub)
  21. }
  22. // 向 watcher 中添加 dep
  23. depend () {
  24. if (Dep.target) {
  25. Dep.target.addDep(this)
  26. }
  27. }
  28. /**
  29. * 通知 dep 中的所有 watcher,
  30. * 执行 watcher.update() 方法
  31. */
  32. notify () {
  33. const subs = this.subs.slice() // 返回一个新数组
  34. // 遍历 dep 中存储的 watcher,执行 watcher.update()
  35. for (let i = 0, l = subs.length; i < l; i++) {
  36. subs[i].update()
  37. }
  38. }
  39. }
  40. Dep.target = null
  41. const targetStack = []
  42. // 设置 Dep.target = watche
  43. export function pushTarget (target) {
  44. targetStack.push(target)
  45. Dep.target = target
  46. }
  47. // 依赖收集结束调用,设置 Dep.target = null
  48. export function popTarget () {
  49. targetStack.pop()
  50. Dep.target = targetStack[targetStack.length - 1]
  51. }