Vue.js实现响应式的核心是利用了ES5的Object.defineProperty,这也是Vue.js不能兼容IE8以下的原因

Object.defineProperty

在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象

  1. Object.defineProperty(obj, prop, descriptor)

obj 是要在其上定义属性的对象;prop 是要定义或修改的属性的名称;descriptor 是将被定义或修改的属性描述符

initState

在Vue的初始化阶段,_init方法执行的时候会执行initState(vm)方法
定义在src/core/instance/state.js中

  1. export function initState (vm: Component) {
  2. vm._watchers = []
  3. const opts = vm.$options
  4. // props
  5. if (opts.props) initProps(vm, opts.props)
  6. // methods
  7. if (opts.methods) initMethods(vm, opts.methods)
  8. // data
  9. if (opts.data) {
  10. initData(vm)
  11. } else {
  12. observe(vm._data = {}, true /* asRootData */)
  13. }
  14. // computed
  15. if (opts.computed) initComputed(vm, opts.computed)
  16. // watch
  17. if (opts.watch && opts.watch !== nativeWatch) {
  18. initWatch(vm, opts.watch)
  19. }
  20. }

initProps

  1. function initProps (vm: Component, propsOptions: Object) {
  2. const propsData = vm.$options.propsData || {}
  3. const props = vm._props = {}
  4. // cache prop keys so that future props updates can iterate using Array
  5. // instead of dynamic object key enumeration.
  6. const keys = vm.$options._propKeys = []
  7. const isRoot = !vm.$parent
  8. // root instance props should be converted
  9. if (!isRoot) {
  10. toggleObserving(false)
  11. }
  12. // 遍历定义的props配置
  13. for (const key in propsOptions) {
  14. keys.push(key)
  15. const value = validateProp(key, propsOptions, propsData, vm)
  16. /* istanbul ignore else */
  17. if (process.env.NODE_ENV !== 'production') {
  18. const hyphenatedKey = hyphenate(key)
  19. if (isReservedAttribute(hyphenatedKey) ||
  20. config.isReservedAttr(hyphenatedKey)) {
  21. warn(
  22. `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
  23. vm
  24. )
  25. }
  26. // 调用defineReactive方法把每个prop对应的值变成响应式,可以通过vm._props.xxx访问到定义props中对应的属性
  27. defineReactive(props, key, value, () => {
  28. if (!isRoot && !isUpdatingChildComponent) {
  29. warn(
  30. `Avoid mutating a prop directly since the value will be ` +
  31. `overwritten whenever the parent component re-renders. ` +
  32. `Instead, use a data or computed property based on the prop's ` +
  33. `value. Prop being mutated: "${key}"`,
  34. vm
  35. )
  36. }
  37. })
  38. } else {
  39. defineReactive(props, key, value)
  40. }
  41. // static props are already proxied on the component's prototype
  42. // during Vue.extend(). We only need to proxy props defined at
  43. // instantiation here.
  44. if (!(key in vm)) {
  45. // 通过proxy把vm._proxy.xxx的访问代理到vm.xxx上
  46. proxy(vm, `_props`, key)
  47. }
  48. }
  49. toggleObserving(true)
  50. }

initData

  1. function initData (vm: Component) {
  2. let data = vm.$options.data
  3. data = vm._data = typeof data === 'function'
  4. ? getData(data, vm)
  5. : data || {}
  6. if (!isPlainObject(data)) {
  7. data = {}
  8. process.env.NODE_ENV !== 'production' && warn(
  9. 'data functions should return an object:\n' +
  10. 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
  11. vm
  12. )
  13. }
  14. // proxy data on instance
  15. const keys = Object.keys(data)
  16. const props = vm.$options.props
  17. const methods = vm.$options.methods
  18. let i = keys.length
  19. while (i--) {
  20. const key = keys[i]
  21. if (process.env.NODE_ENV !== 'production') {
  22. if (methods && hasOwn(methods, key)) {
  23. warn(
  24. `Method "${key}" has already been defined as a data property.`,
  25. vm
  26. )
  27. }
  28. }
  29. if (props && hasOwn(props, key)) {
  30. process.env.NODE_ENV !== 'production' && warn(
  31. `The data property "${key}" is already declared as a prop. ` +
  32. `Use prop default value instead.`,
  33. vm
  34. )
  35. } else if (!isReserved(key)) {
  36. // 定义data函数返回对象的遍历,通过proxy把每一个值vm._data.xxx都代理到vm.xxx上
  37. proxy(vm, `_data`, key)
  38. }
  39. }
  40. // observe data
  41. // 调用observe方法观测整个data的变化,把data也变成响应式,可以通过vm._data.xxx访问到定义data返回函数中对应的属性
  42. observe(data, true /* asRootData */)
  43. }

无论是 props 或是 data 的初始化都是把它们变成响应式对象

proxy

代理的作用是把props和data上的属性代理到vm实例上

  1. // 定义这个props,可以通过vm实例访问到它
  2. let comP = {
  3. props: {
  4. msg: 'hello'
  5. },
  6. methods: {
  7. say() {
  8. console.log(this.msg)
  9. }
  10. }
  11. }

在say函数中通过this.msg访问到定义在props中的msg,这个过程发生在proxy阶段

  1. const sharedPropertyDefinition = {
  2. enumerable: true,
  3. configurable: true,
  4. get: noop,
  5. set: noop
  6. }
  7. export function proxy (target: Object, sourceKey: string, key: string) {
  8. sharedPropertyDefinition.get = function proxyGetter () {
  9. return this[sourceKey][key]
  10. }
  11. sharedPropertyDefinition.set = function proxySetter (val) {
  12. this[sourceKey][key] = val
  13. }
  14. // 把target[sourceKey][key]的读写变成了对target[key]的读写
  15. Object.defineProperty(target, key, sharedPropertyDefinition)
  16. }

对于 props 而言,对 vm._props.xxx 的读写变成了 vm.xxx 的读写,而对于 vm._props.xxx 可以访问到定义在 props 中的属性,就可以通过 vm.xxx 访问到定义在 props 中的 xxx 属性了
对于 data 而言,对 vm._data.xxxx 的读写变成了对 vm.xxxx 的读写,而对于 vm._data.xxxx 可以访问到定义在 data 函数返回对象中的属性,就可以通过 vm.xxxx 访问到定义在 data 函数返回对象中的 xxxx 属性了

observe

observe 的功能就是用来监测数据的变化
定义在 src/core/observer/index.js 中

  1. /**
  2. * Attempt to create an observer instance for a value,
  3. * returns the new observer if successfully observed,
  4. * or the existing observer if the value already has one.
  5. */
  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. // 给非VNode的对象类型数据添加一个Observer
  12. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { // 已经添加过
  13. ob = value.__ob__
  14. } else if ( // 没添加过
  15. shouldObserve &&
  16. !isServerRendering() &&
  17. (Array.isArray(value) || isPlainObject(value)) &&
  18. Object.isExtensible(value) &&
  19. !value._isVue
  20. ) {
  21. ob = new Observer(value) // 实例化一个Observer对象实例
  22. }
  23. if (asRootData && ob) {
  24. ob.vmCount++
  25. }
  26. return ob
  27. }

Observer

Observer是一个类,作用是给对象的属性添加getter和setter,用于依赖收集和派发更新

  1. /**
  2. * Observer class that is attached to each observed
  3. * object. Once attached, the observer converts the target
  4. * object's property keys into getter/setters that
  5. * collect dependencies and dispatch updates.
  6. */
  7. export class Observer {
  8. value: any;
  9. dep: Dep;
  10. vmCount: number; // number of vms that have this object as root $data
  11. constructor (value: any) {
  12. this.value = value
  13. this.dep = new Dep() // 实例化Dep对象
  14. this.vmCount = 0
  15. def(value, '__ob__', this) //把自身实例添加到数据对象value的_ob_属性上
  16. if (Array.isArray(value)) {
  17. if (hasProto) {
  18. protoAugment(value, arrayMethods)
  19. } else {
  20. copyAugment(value, arrayMethods, arrayKeys)
  21. }
  22. this.observeArray(value) // 数组调用方法
  23. } else {
  24. this.walk(value) // 纯对象调用方法
  25. }
  26. }
  27. /**
  28. * Walk through all properties and convert them into
  29. * getter/setters. This method should only be called when
  30. * value type is Object.
  31. */
  32. walk (obj: Object) {
  33. // 遍历对象的key 调用defineReactive方法
  34. const keys = Object.keys(obj)
  35. for (let i = 0; i < keys.length; i++) {
  36. defineReactive(obj, keys[i])
  37. }
  38. }
  39. /**
  40. * Observe a list of Array items.
  41. */
  42. observeArray (items: Array<any>) {
  43. // 遍历数组再次调用observe方法
  44. for (let i = 0, l = items.length; i < l; i++) {
  45. observe(items[i])
  46. }
  47. }
  48. }

def方法定义在src/core/util/lang.js中

  1. /**
  2. * Define a property.
  3. * 对Object.defineProperty的封装
  4. */
  5. export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  6. Object.defineProperty(obj, key, {
  7. value: val,
  8. enumerable: !!enumerable,
  9. writable: true,
  10. configurable: true
  11. })
  12. }

在开发中输出data上对象类型的数据会发现该对象多了一个ob的属性

defineReactive

defineReactive的功能就是定义一个响应式对象,给对象动态添加getter和setter
定义在src/core/observer/index,js中

  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. // 初始化Dep对象实例
  12. const dep = new Dep()
  13. // 拿到obj的属性描述符
  14. const property = Object.getOwnPropertyDescriptor(obj, key)
  15. if (property && property.configurable === false) {
  16. return
  17. }
  18. // cater for pre-defined getter/setters
  19. const getter = property && property.get
  20. const setter = property && property.set
  21. if ((!getter || setter) && arguments.length === 2) {
  22. val = obj[key]
  23. }
  24. // 对子对象递归调用observe方法,这样就保证了无论obj的结构多复杂,它的子属性也能变成响应式对象,这样访问和修改obj中一个嵌套较深的属性也能触发getter和setter
  25. let childOb = !shallow && observe(val)
  26. // 利用Object.defineProperty去给obj的属性key添加getter和setter
  27. Object.defineProperty(obj, key, {
  28. enumerable: true,
  29. configurable: true,
  30. get: function reactiveGetter () {
  31. const value = getter ? getter.call(obj) : val
  32. if (Dep.target) {
  33. dep.depend()
  34. if (childOb) {
  35. childOb.dep.depend()
  36. if (Array.isArray(value)) {
  37. dependArray(value)
  38. }
  39. }
  40. }
  41. return value
  42. },
  43. set: function reactiveSetter (newVal) {
  44. const value = getter ? getter.call(obj) : val
  45. /* eslint-disable no-self-compare */
  46. if (newVal === value || (newVal !== newVal && value !== value)) {
  47. return
  48. }
  49. /* eslint-enable no-self-compare */
  50. if (process.env.NODE_ENV !== 'production' && customSetter) {
  51. customSetter()
  52. }
  53. // #7981: for accessor properties without setter
  54. if (getter && !setter) return
  55. if (setter) {
  56. setter.call(obj, newVal)
  57. } else {
  58. val = newVal
  59. }
  60. childOb = !shallow && observe(newVal)
  61. dep.notify()
  62. }
  63. })
  64. }