# 基础概念

在使用Vue的过程中,当我们改变数据的时候,页面也会同样的改变,这就是数据的响应式,也就是双向绑定。

2 秒后页面的内容会变成hello world

<template>
  <div>{{ msg }}</div>
</template>
<script>
export default {
  data() {
    return {
      msg: "hello",
    };
  },
  mounted() {
    setTimeout(() => {
      this.msg = "hello world";
    }, 2000);
  },
};
</script>

Vue.js主要通过Object.defineproperty(obj,key,desc)来定义响应式对象,其中最关键的属性是set,get

Object.defineProperty(obj, key, {
  get: function () {
    // 当访问obj.key执行
    return val;
  },
  set: function (newVal) {
    // 当设置obj.key = '111',执行
    val = newVal;
  },
});

Vue.js会把data,props,computed等都变成响应式的对象。

# proxy

proxy 主要是用来进行数据的代理,在core/instance/state.js

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop,
};
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);
}

在源码中,经常可以看到proxy(vm, _props, key),这里其实就是将_props内的数据作为sourceKey,每次通过this实例获取的时候,可以从_props内获取,其余datacomputed等都是类似。

// 等同于this._props.age
this.age;

# observe

observe对数据进行响应式的处理,位置在core/observer/index.js

export function observe(value: any, asRootData: ?boolean): Observer | void {
  // 不需要做响应式处理
  if (!isObject(value) || value instanceof VNode) {
    return;
  }
  // __ob__ 就是observer对象
  let ob: Observer | void;
  if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    ob = value.__ob__;
  } else if (
    // 是否可以进行响应式处理,isVue为vue实例
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value);
  }
  if (asRootData && ob) {
    ob.vmCount++;
  }
  return ob;
}
  • 首先判断是否是对象,并且不能是 VNode 实例,否则直接返回
  • 如果有__ob__属性并且是Observer的实例,就说明该对象就是响应式对象,直接返回即可。
  • 如果没有__ob__属性,同时满足一些条件的情况下,会new Observer(value),最后如果是根实例,vmCount =1

# OBserver

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor(value: any) {
    this.value = value;
    this.dep = new Dep();
    this.vmCount = 0;
    def(value, "__ob__", this);
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  }

  walk(obj: Object) {
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i]);
    }
  }

  observeArray(items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i]);
    }
  }
}
  • 定义__ob__存储实例
  • 区分数组和对象,如果是对象,则调用walk方法遍历defineReactive
  • 如果是数组,遍历递归调用所有的子元素,同时改写array处理元素的方法,使其具有响应式。

# defineReactive

export function defineReactive(
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep();

  const property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return;
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get;
  const setter = property && property.set;
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key];
  }

  let childOb = !shallow && observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    },
    set: function reactiveSetter(newVal) {
      const value = getter ? getter.call(obj) : val;
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return;
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== "production" && customSetter) {
        customSetter();
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return;
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal);
      dep.notify();
    },
  });
}
  • Object.defineProperty()进行一层封装,进行settergetter处理
  • 判断Object.getOwnPropertyDescriptor()获取属性描述,如果configurablefalse,就不能被定义为响应式对象。