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响应式原理的理解? #81

Open
GGXXMM opened this issue Jan 3, 2021 · 0 comments
Open

谈谈你对vue响应式原理的理解? #81

GGXXMM opened this issue Jan 3, 2021 · 0 comments
Labels

Comments

@GGXXMM
Copy link
Owner

GGXXMM commented Jan 3, 2021

什么是响应式?

数据响应式就是能够使数据变化可以被检测并对这种变化做出响应的机制。

为什么需要响应式?

mvvm框架(vue/react/angluar)需要解决的一个核心问题是连接数据层和视图层,通过数据驱动应用,数据变化,视图立即更新,要做到这点就需要对数据做响应式处理。

响应式能给我们带来什么好处?

以vue为例,通过数据响应式、虚拟DOM和patch算法,可以使我们只需要操作数据,完全不用接触繁琐的dom操作,从而大大提升开发效率,降低开发难度。

vue2的响应式是怎么实现的?有哪些优缺点?

vue2.0中的响应式会根据数据类型来做不同处理,如果是对象采用Object.defineProperty()拦截对象,当数据被访问或发生变化时,可以感应做出响应。源码实现如下:
src/core/observer/index.js

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()
    }
  })
}

如果是数组则通过覆盖该数组原型的方法,扩展它的7个变更方法,使这些方法可以额外的做更新通知,从而做出响应。源码实现如下:
src/core/observer/array.js

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
  })
})

优缺点

Object.defineProperty()虽然很好的解决数据响应式的问题,但实际使用中也存在一些缺点,比如:

  • 初始化时递归遍历数据的所有属性会造成性能损失;
  • 新增/删除响应式属性时,需要用Vue.set/delete特殊的api才能生效;
  • 对于ES6中产生的Map、Set数据结构不支持。

vue3中响应式的新变化

利用proxy代理要响应化的数据,它有很多好处,比如:

  • 编程是一致的,不需要使用特殊的api;
  • 初始化性能和内存消耗得到大幅度改善,因为proxy是代理整个对象,不需要遍历属性;
  • 另外响应化的实现代码抽取独立为reactivity包,可以灵活使用,甚至不需要引入vue都可以体验。
@GGXXMM GGXXMM added the vue label Dec 6, 2021
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