observer/index.js - 图1

    1. /* @flow */
    2. import Dep from './dep'
    3. import VNode from '../vdom/vnode'
    4. import { arrayMethods } from './array'
    5. import {
    6. def,
    7. hasOwn,
    8. hasProto,
    9. isObject,
    10. isPlainObject,
    11. isPrimitive,
    12. isServerRendering,
    13. isUndef,
    14. isValidArrayIndex,
    15. warn
    16. } from '../util/index'
    17. const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
    18. /**
    19. * In some cases we may want to disable observation inside a component's
    20. * update computation.
    21. */
    22. export let shouldObserve: boolean = true
    23. export function toggleObserving (value: boolean) {
    24. shouldObserve = value
    25. }
    26. /*
    27. * 针对Object使用defineReactive,
    28. * 数组使用的是劫持原型方法
    29. * */
    30. /**
    31. * Observer class that is attached to each observed
    32. * object. Once attached, the observer converts the target
    33. * object's property keys into getter/setters that
    34. * collect dependencies and dispatch updates.
    35. */
    36. /*
    37. * 每个被观察到对象被附加上观察者实例,
    38. * 一旦被添加,观察者将为目标对象加上getter\setter属性,
    39. * 进行依赖收集以及调度更新。
    40. */
    41. export class Observer {
    42. value: any
    43. dep: Dep
    44. vmCount: number // number of vms that have this object as root $data
    45. constructor (value: any) {
    46. this.value = value
    47. this.dep = new Dep()
    48. this.vmCount = 0
    49. /*
    50. * 将Observer实例绑定到data的__ob__属性上面去,
    51. * 之前说过observe的时候会先检测是否已经有__ob__对象存放Observer实例了,
    52. * def方法定义可以参考
    53. * https://github.com/vuejs/vue/blob/dev/src/core/util/lang.js#L16
    54. * def方法第四个参数为是否可以枚举
    55. * 这里的__ob__这样声明,是不可枚举的
    56. * 这里写法导致的结果: this.a.__ob__.value === this.a
    57. */
    58. // def (obj: Object, key: string, val: any, enumerable?: boolean)
    59. def(value, '__ob__', this)
    60. if (Array.isArray(value)) {
    61. // '__proto__' in {}
    62. // ie9、ie10 和 opera 没有 __proto__ 属性
    63. if (hasProto) {
    64. /*直接覆盖原型的方法来修改目标对象*/
    65. protoAugment(value, arrayMethods)
    66. } else {
    67. /*定义(覆盖)目标对象或数组的某一个方法*/
    68. copyAugment(value, arrayMethods, arrayKeys)
    69. }
    70. /*如果是数组则需要遍历数组的每一个成员进行observe*/
    71. this.observeArray(value)
    72. } else {
    73. /*如果是对象则直接walk进行绑定*/
    74. this.walk(value)
    75. }
    76. }
    77. /**
    78. * Walk through all properties and convert them into
    79. * getter/setters. This method should only be called when
    80. * value type is Object.
    81. */
    82. /*
    83. * 遍历每一个对象并且在它们上面绑定getter与setter。
    84. * 这个方法只有在value的类型是对象的时候才能被调用
    85. */
    86. walk (obj: Object) {
    87. const keys = Object.keys(obj)
    88. for (let i = 0; i < keys.length; i++) {
    89. defineReactive(obj, keys[i])
    90. }
    91. }
    92. /**
    93. * Observe a list of Array items.
    94. */
    95. /*
    96. * 对一个数组的每一个成员进行observe
    97. */
    98. observeArray (items: Array<any>) {
    99. for (let i = 0, l = items.length; i < l; i++) {
    100. observe(items[i])
    101. }
    102. }
    103. }
    104. // helpers
    105. /**
    106. * Augment a target Object or Array by intercepting
    107. * the prototype chain using __proto__
    108. */
    109. /**
    110. * 直接覆盖原型的方法来修改目标对象
    111. */
    112. function protoAugment (target, src: Object) {
    113. /* eslint-disable no-proto */
    114. target.__proto__ = src
    115. /* eslint-enable no-proto */
    116. }
    117. /**
    118. * Augment a target Object or Array by defining
    119. * hidden properties.
    120. */
    121. /**
    122. * 定义(覆盖)目标对象或数组的某一个方法
    123. */
    124. /* istanbul ignore next */
    125. function copyAugment (target: Object, src: Object, keys: Array<string>) {
    126. for (let i = 0, l = keys.length; i < l; i++) {
    127. const key = keys[i]
    128. def(target, key, src[key])
    129. }
    130. }
    131. /**
    132. * Attempt to create an observer instance for a value,
    133. * returns the new observer if successfully observed,
    134. * or the existing observer if the value already has one.
    135. */
    136. /*
    137. * 尝试创建一个Observer实例(__ob__),
    138. * 如果成功创建Observer实例则返回新的Observer实例,
    139. * 如果已有Observer实例则返回现有的Observer实例。
    140. */
    141. export function observe (value: any, asRootData: ?boolean): Observer | void {
    142. if (!isObject(value) || value instanceof VNode) {
    143. return
    144. }
    145. let ob: Observer | void
    146. /*
    147. * 这里用__ob__这个属性来判断是否已经有Observer实例,
    148. * 如果没有Observer实例则会新建一个Observer实例并赋值给__ob__这个属性,
    149. * 如果已有Observer实例则直接返回该Observer实例
    150. */
    151. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    152. ob = value.__ob__
    153. } else if (
    154. /*
    155. * 这里的判断是为了确保value是单纯的对象,而不是函数或者是Regexp等情况。
    156. * 而且该对象在shouldConvert的时候才会进行Observer。这是一个标识位,避免重复对value进行Observer
    157. */
    158. shouldObserve &&
    159. !isServerRendering() &&
    160. (Array.isArray(value) || isPlainObject(value)) &&
    161. Object.isExtensible(value) &&
    162. !value._isVue
    163. /*
    164. 这里的判断是为了确保value是单纯的对象,而不是函数或者是Regexp等情况。
    165. 而且该对象在shouldConvert的时候才会进行Observer。
    166. 这是一个标识位,避免重复对value进行Observer
    167. */
    168. ) {
    169. ob = new Observer(value)
    170. }
    171. if (asRootData && ob) {
    172. /*如果是根数据则计数,后面Observer中的observe的asRootData非true*/
    173. ob.vmCount++
    174. }
    175. return ob
    176. }
    177. /**
    178. * Define a reactive property on an Object.
    179. */
    180. /*为对象defineProperty上在变化时通知的属性*/
    181. export function defineReactive (
    182. obj: Object, //对象
    183. key: string, //对象的key
    184. val: any, //监听的数据 返回的数据
    185. customSetter?: ?Function, // 日志函数
    186. shallow?: boolean //是否要添加__ob__ 属性
    187. ) {
    188. /*在闭包中定义一个dep对象*/
    189. const dep = new Dep()
    190. const property = Object.getOwnPropertyDescriptor(obj, key)
    191. // configurable属性为true,对象的属性描述可以被改变或者属性可被删除
    192. if (property && property.configurable === false) {
    193. return
    194. }
    195. /*
    196. * 如果之前该对象已经预设了getter以及setter函数则将其取出来,
    197. * 新定义的getter/setter中会将其执行,
    198. * 保证不会覆盖之前已经定义的getter/setter。
    199. */
    200. // cater for pre-defined getter/setters
    201. const getter = property && property.get
    202. const setter = property && property.set
    203. // 只有在walk函数中才是args长度为2,args长度为2,即没有传入val
    204. // 当没有设置getter或者已有setter
    205. // TODO: 这个条件存疑
    206. if ((!getter || setter) && arguments.length === 2) {
    207. val = obj[key]
    208. }
    209. /*对象的子对象递归进行observe并返回子节点的Observer对象*/
    210. let childOb = !shallow && observe(val)
    211. Object.defineProperty(obj, key, {
    212. enumerable: true,
    213. configurable: true,
    214. get: function reactiveGetter () {
    215. /*如果原本对象拥有getter方法则执行*/
    216. const value = getter ? getter.call(obj) : val
    217. if (Dep.target) {
    218. /*进行依赖收集*/
    219. dep.depend()
    220. if (childOb) {
    221. /**
    222. * 子级dep也要收集依赖,这个就是为array准备的
    223. * 最开始分析,array数据的dep本身是不会收集依赖的
    224. * 但是可以让他强行进行一次收集,就是在这儿
    225. * */
    226. childOb.dep.depend()
    227. if (Array.isArray(value)) {
    228. /**
    229. 是数组则需要对每一个成员都进行依赖收集,
    230. 如果数组的成员还是数组,则递归。
    231. */
    232. dependArray(value)
    233. }
    234. }
    235. }
    236. return value
    237. },
    238. set: function reactiveSetter (newVal) {
    239. /*通过getter方法获取当前值,与新值进行比较,一致则不需要执行下面的操作*/
    240. const value = getter ? getter.call(obj) : val
    241. /* eslint-disable no-self-compare */
    242. if (newVal === value || (newVal !== newVal && value !== value)) {
    243. return
    244. }
    245. /* eslint-enable no-self-compare */
    246. if (process.env.NODE_ENV !== 'production' && customSetter) {
    247. customSetter()
    248. }
    249. // #7981: for accessor properties without setter
    250. if (getter && !setter) return
    251. if (setter) {
    252. /*如果原本对象拥有setter方法则执行setter*/
    253. setter.call(obj, newVal)
    254. } else {
    255. val = newVal
    256. }
    257. /*新的值需要重新进行observe,保证数据响应式*/
    258. childOb = !shallow && observe(newVal)
    259. /*dep对象通知所有的观察者*/
    260. dep.notify()
    261. }
    262. })
    263. }
    264. /**
    265. * Set a property on an object. Adds the new property and
    266. * triggers change notification if the property doesn't
    267. * already exist.
    268. */
    269. // Vue.set = set
    270. export function set (target: Array<any> | Object, key: any, val: any): any {
    271. if (process.env.NODE_ENV !== 'production' &&
    272. (isUndef(target) || isPrimitive(target))
    273. ) {
    274. warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
    275. }
    276. /*如果传入数组则在指定位置插入val*/
    277. if (Array.isArray(target) && isValidArrayIndex(key)) {
    278. target.length = Math.max(target.length, key)
    279. target.splice(key, 1, val)
    280. /*数组已经被改写了Array原型上的方法来进行响应式处理,这里直接调用就行*/
    281. return val
    282. }
    283. /*如果是一个对象,并且已经存在了这个key则直接返回*/
    284. if (key in target && !(key in Object.prototype)) {
    285. target[key] = val
    286. return val
    287. }
    288. /*获得target的Oberver实例*/
    289. const ob = (target: any).__ob__
    290. /*
    291. _isVue 一个防止vm实例自身被观察的标志位 ,_isVue为true则代表vm实例,也就是this
    292. vmCount判断是否为根节点,
    293. 存在则代表是data的根节点,
    294. Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property)
    295. */
    296. if (target._isVue || (ob && ob.vmCount)) {
    297. process.env.NODE_ENV !== 'production' && warn(
    298. 'Avoid adding reactive properties to a Vue instance or its root $data ' +
    299. 'at runtime - declare it upfront in the data option.'
    300. )
    301. return val
    302. }
    303. if (!ob) {
    304. /*
    305. * 用__ob__这个属性来判断是否已经有Observer实例
    306. * 如果连需要赋值的对象都不是响应式的,那就直接赋值
    307. */
    308. target[key] = val
    309. return val
    310. }
    311. /*
    312. *注意:set方法是有返回值的
    313. */
    314. defineReactive(ob.value, key, val)
    315. ob.dep.notify()
    316. return val
    317. }
    318. /**
    319. * Delete a property and trigger change if necessary.
    320. */
    321. // Vue.delete = del
    322. export function del (target: Array<any> | Object, key: any) {
    323. if (process.env.NODE_ENV !== 'production' &&
    324. (isUndef(target) || isPrimitive(target))
    325. ) {
    326. warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
    327. }
    328. if (Array.isArray(target) && isValidArrayIndex(key)) {
    329. target.splice(key, 1)
    330. return
    331. }
    332. const ob = (target: any).__ob__
    333. if (target._isVue || (ob && ob.vmCount)) {
    334. /*通过vmCount判断,提示你删掉的属性可能是根对象*/
    335. process.env.NODE_ENV !== 'production' && warn(
    336. 'Avoid deleting properties on a Vue instance or its root $data ' +
    337. '- just set it to null.'
    338. )
    339. return
    340. }
    341. if (!hasOwn(target, key)) {
    342. return
    343. }
    344. delete target[key]
    345. if (!ob) {
    346. return
    347. }
    348. ob.dep.notify()
    349. }
    350. /**
    351. * Collect dependencies on array elements when the array is touched, since
    352. * we cannot intercept array element access like property getters.
    353. */
    354. /*
    355. 递归数组,进行依赖收集
    356. */
    357. function dependArray (value: Array<any>) {
    358. for (let e, i = 0, l = value.length; i < l; i++) {
    359. e = value[i]
    360. e && e.__ob__ && e.__ob__.dep.depend()
    361. if (Array.isArray(e)) {
    362. dependArray(e)
    363. }
    364. }
    365. }