响应式对象,核心是利用 Object.defineProperty 给数据添加了 getter 和 setter,目的是为了在我们访问数据和写数据时自动执行一些逻辑:getter 用来依赖收集,setter 来派发更新。

The Object.defineProperty

  1. // obj 要在其上定义属性的对象
  2. // prop 定义或修改属性的名称
  3. // descriptor 将被定义或修改的属性描述符
  4. Object.defineProperty(obj, prop, descriptor);

descriptor是比较核心的,有很多可选值。其中getset是我们最关心的,
get是给obj一个属性提供的getter方法,当我们访问属性时就会触发getter方法,
set是給obj一个属性提供的setter方法,当我们对该属性做修改的时候就会触发setter方法。

一个对象拥有了 gettersetter 后,我们可以简单地把这个对象称为响应式对象。

initState

对 props、methdos、data、computed 和 watcher 等属性做初始化操作。

  • initProps,遍历 props 配置,做两件事:调用 defineReactive 方法,把每个 prop 对应的值变成响应式,可以通过 vm._props.xxx 访问到定义在 props 中的属性。另一个是通过 proxyvm._props.xxx 的访问代理到 vm.xxx 上。
  • initdata,data 的初始化也是做两件事,对定义 data 函数返回对象的遍历,通过 proxy 把每个 vm._data.xxx 都代理到 vm.xxx 上;另一个是调用 observe 方法观测整个 data 的变化,把 data 也变为响应式,可以用过 vm._data.xxx 访问到定义在 data 返回函数中对应的属性。

    proxy

    代理,将 propsdata 上的属性代理到 vm 实例上,下面这个例子
    1. let comP = {
    2. props: {
    3. msg: 'hello'
    4. },
    5. methods: {
    6. say() {
    7. console.log(this.msg)
    8. }
    9. }
    10. }
    为什么我们可以直接使用定义在 props 中的 msg 呢,因为如下 prox 方法做了一些事: ```javascript const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop }

// proxy(vm, _data, key) 使用方式 export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) }

  1. 通过 `Object.defineProperty` 将对于 `target[sourceKey][key]` 的读写变成为 `target[key]` 的读写。所以对于 `vm._props.xxx` 的读写变为 `vm.xxx` 读写,`vm._data.xxx` 同理。
  2. <a name="sNxUU"></a>
  3. ## observe
  4. `observe` 的功能是来检测数据变化
  5. ```javascript
  6. export function observe (value: any, asRootData: ?boolean): Observer | void {
  7. if (!isObject(value) || value instanceof VNode) {
  8. return
  9. }
  10. let ob: Observer | void
  11. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
  12. ob = value.__ob__
  13. } else if (
  14. shouldObserve &&
  15. !isServerRendering() &&
  16. (Array.isArray(value) || isPlainObject(value)) &&
  17. Object.isExtensible(value) &&
  18. !value._isVue
  19. ) {
  20. ob = new Observer(value)
  21. }
  22. if (asRootData && ob) {
  23. ob.vmCount++
  24. }
  25. return ob
  26. }

该方法的作用是给非 VNode 的对象类型数据添加一个 Observer,即实例化一个 Observer 对象实例。

Observer

注意写法上就差一个 rObserver 对象是一个类,作用是给对象的属性添加 gettersetter,用于依赖收集和派发更新:

  1. export class Observer {
  2. value: any;
  3. dep: Dep;
  4. vmCount: number; // number of vms that has this object as root $data
  5. constructor (value: any) {
  6. this.value = value
  7. this.dep = new Dep()
  8. this.vmCount = 0
  9. def(value, '__ob__', this)
  10. if (Array.isArray(value)) {
  11. const augment = hasProto
  12. ? protoAugment
  13. : copyAugment
  14. augment(value, arrayMethods, arrayKeys)
  15. this.observeArray(value)
  16. } else {
  17. this.walk(value)
  18. }
  19. }
  20. /**
  21. * Walk through each property and convert them into
  22. * getter/setters. This method should only be called when
  23. * value type is Object.
  24. */
  25. walk (obj: Object) {
  26. const keys = Object.keys(obj)
  27. for (let i = 0; i < keys.length; i++) {
  28. defineReactive(obj, keys[i])
  29. }
  30. }
  31. /**
  32. * Observe a list of Array items.
  33. */
  34. observeArray (items: Array<any>) {
  35. for (let i = 0, l = items.length; i < l; i++) {
  36. observe(items[i])
  37. }
  38. }
  39. }

defineReactive

  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. const dep = new Dep()
  12. const property = Object.getOwnPropertyDescriptor(obj, key)
  13. if (property && property.configurable === false) {
  14. return
  15. }
  16. // cater for pre-defined getter/setters
  17. const getter = property && property.get
  18. const setter = property && property.set
  19. if ((!getter || setter) && arguments.length === 2) {
  20. val = obj[key]
  21. }
  22. let childOb = !shallow && observe(val)
  23. Object.defineProperty(obj, key, {
  24. enumerable: true,
  25. configurable: true,
  26. get: function reactiveGetter () {
  27. const value = getter ? getter.call(obj) : val
  28. if (Dep.target) {
  29. dep.depend()
  30. if (childOb) {
  31. childOb.dep.depend()
  32. if (Array.isArray(value)) {
  33. dependArray(value)
  34. }
  35. }
  36. }
  37. return value
  38. },
  39. set: function reactiveSetter (newVal) {
  40. const value = getter ? getter.call(obj) : val
  41. /* eslint-disable no-self-compare */
  42. if (newVal === value || (newVal !== newVal && value !== value)) {
  43. return
  44. }
  45. /* eslint-enable no-self-compare */
  46. if (process.env.NODE_ENV !== 'production' && customSetter) {
  47. customSetter()
  48. }
  49. if (setter) {
  50. setter.call(obj, newVal)
  51. } else {
  52. val = newVal
  53. }
  54. childOb = !shallow && observe(newVal)
  55. dep.notify()
  56. }
  57. })
  58. }

Data、Observer、Dep 和 watcher 之间的关系。
响应式对象 - 图1