Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue中是如何检测数组变化的? #95

Open
Cosen95 opened this issue Apr 25, 2020 · 1 comment
Open

Vue中是如何检测数组变化的? #95

Cosen95 opened this issue Apr 25, 2020 · 1 comment
Labels

Comments

@Cosen95
Copy link
Owner

Cosen95 commented Apr 25, 2020

No description provided.

@Cosen95 Cosen95 added the vue label Apr 25, 2020
@Cosen95
Copy link
Owner Author

Cosen95 commented Apr 25, 2020

Vue中检测数组变化核心有两点:

  • 首先,使用函数劫持的方式,重写了数组的方法
  • Vuedata 中的数组,进行了原型链重写。指向了自己定义的数组原型方法,这样当调用数组 api 时,就可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次进行观测。

这里用一张流程图来说明:

这里第一步和第二步和上题请说一下响应式数据的原理?是相同的,就不展开说明了。

  • 第一步同样是初始化用户传入的data数据。对应源码src/core/instance/state.js的112行的initData函数。
  • 第二步是对数据进行观测。对应源码src/core/observer/index.js的124行。
  • 第三步是将数组的原型方法指向重写的原型。对应源码src/core/observer/index.js的49行。
if (hasProto) {
  protoAugment(value, arrayMethods)
} else {
  // ...
}

也就是protoAugment方法:

/**
 * Augment a target Object or Array by intercepting
 * the prototype chain using __proto__
 */
function protoAugment (target, src: Object) {
  /* eslint-disable no-proto */
  target.__proto__ = src
  /* eslint-enable no-proto */
}
  • 第四步进行了两步操作。首先是对数组的原型方法进行重写,对应源码src/core/observer/array.js
/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [  // 这里列举的数组的方法是调用后能改变原数组的
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {  // 重写原型方法
  // cache original method
  const original = arrayProto[method]  // 调用原数组方法
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)  // 进行深度监控
    // notify change
    ob.dep.notify()  // 调用数组方法后,手动通知视图更新
    return result
  })
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant