Skip to content

02响应式系统基本原理

响应式系统

Vue.js是一款MVVM框架,数据模型如果仅仅是普通的JS对象,但是对这些对象进行操作时,却能影响对应视图,它的核心实现就是[响应式系统]。尽管我们在使用Vue开发时不会直接修改响应式系统,但是理解它的实现有助于是避开一些常见的坑。也有助于预见一些琢磨不透的问题时可以深入其原理来解决它。

Object.defineProperty

首先来介绍一下 Object.defineProperty,Vue就是基于它实现了响应式系统的。

更多参考mdn文档

实现 observer

我们用它来使对象变成可观察的。 这一部分的内容之前提到过,在_init阶段会进行初始化,对数据进行响应式化。

为便于理解,我们不考虑数组等复杂情况,只对对象进行处理。首先定义一个cb函数,该函数用于模拟视图更新,调用它即代表更新视图,内部可以是一些更新视图的方法。

js
function cb(val) {
  /* 渲染视图 */
}

然后定义一个defineReactive,该方法通过Object.defineProperty来实现对对象的响应式化。入参是一个obj(需要绑定的对象)、key(obj的某一属性)和val(具体值)。经过defineReactive处理后,我们的obj的key在读取时会触发reactiveGetter方法,而该属性被写入时会触发reactiveSetter方法。

js
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    enumerable: true,   // 属性可枚举
    configurable: true, // 属性可被修改或删除
    get: function reactiveGetter() {
      return val        // 会进行依赖收集
    },
    set: function reactiveSetter(newVal) {
      if (newVal === val) return
      cb(newVal)
    }
  })
}

当然这是不够的,我们需要在上面再封装一层observer。该函数传入一个value(需要响应化的对象),通过遍历所有属性的方式对该对象的每一个属性都通过defineReactive来处理。

js
function observer(needReactiveObj) {
  if (!needReactiveObj || typeof needReactiveObj !== 'object') return
  Object.keys(needReactiveObj).forEach(key => {
    defineReactive(needReactiveObj, key, needReactiveObj[key])
  })
}

最后,让我们用observer来封装一个Vue对象吧

在Vue的构造函数中,对optionsdata进行处理,这里的data就是我们平时在组件里所写的data属性(实际上就是一个函数,这里当作一个对象来简单处理)

js
class Vue {// Vue构造类
  constructor(options) {
    this._data = options.data
    observer(this._data)
  }
}

这样我们只需要new一个Vue对象,就会将data中的数据进行响应式化,如果我们对data的属性进行下面操作,就会触发cb方法更新视图

js
let vm = new Vue({
  data: {
    text: 'hello vue'
  }
})
vm._data.text = 'vue is wonderful!' // 视图自动更新

至此,Vue的响应式原理介绍完毕。接下来我们继续学习响应式系统的另一部分——依赖收集.

注:本节代码参考《响应式系统的基本原理》。

共 20 个模块,1301 篇 Markdown 文档。