Skip to content
On this page

数据、视图、Vue

vue 想必大家都应该很熟悉了,最常提起的特点便是:数据驱动视图,我们只需要改动数据,我们的页面就会随之变化,所以,我们可以得出以下结论:

View = Render ( Data )

  • View 代表页面

  • Render 代表 Vue

  • Data 即是数据

Object.defineProperty

看过一些文章的小伙伴可能对于这个方法或多或少知道一点,vue 就是使用这个方法对于数据进行观测的,对于每个数据,都会有其对应的 getter 和 setter ,这样我们就会知道数据何时发生了变化,并去更新相对应的视图。

看这段代码:

js
 let Ned = {
       money:"100"
   }

   Object.defineProperty(Ned ,'money',{
       get(){
           console.log('Ned 被读取了一次')
       },
       set(newVal){
           console.log('Ned 被设置了一次,新数据为:',newVal)
       }
   })
   Ned.money // Ned 被读取了一次
   Ned.money="1亿" //Ned 被设置了一次,新数据为:1亿

从上面的案例可以看出,我们读取数据时会进入 get 函数中;我们设置数据时会进入 set 函数中,这样数据就变得可观测了,读取和设置数据的时候我们都会知道。

看源码(目录:src/core/observer/index.js):

js
// Observer观察者类,对每个对象设置getter和setter,进行依赖收集和发送更新

export class Observer {
   value: any;

   constructor (value: any) {
       this.value = value

   /**
    * 给value增加一个属性'__ob__',值为该value的Observer的实例
    * 这样是相当于在value上打一个补丁,避免重复操作
    * 方法在util/lang.js
    */
       def(value, '__ob__', this)
       if (Array.isArray(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])
       }
   }

  //在对象上定义反应属性
 export function defineReactive (
   obj: Object, //要响应的对象
   key: string, // 响应对象的键
   val: any,    // 对象的值
   ) {

     const dep = new Dep() //创建一个依赖管理器
   // 递归,针对子对象设置geter和setter,并返回子对象的Observer实例
       let childOb = observe(val)

       Object.defineProperty(obj, key, {
           enumerable: true, //表示能否通过for in 循环属性
           configurable: true, //是否可以删除或重新定义属性

           // 在这里可以知道获取了值
           get: function reactiveGetter () {
           dep.depend()//收集依赖,往下面看会明白
           return val
           },

           // 在这里可以知道更改了值
           set: function reactiveSetter (newVal) {
            dep.notify() // 通知所有依赖这个对象观察者进行更新
            val=newVal
           }
       })
   }

   /*
   * 给值创建观察者实例
   * 如果观察成功就返回新的观察者实例
   * 如果已经观察过了,就返回现有的
   */
  function observe (value: any, asRootData: ?boolean): Observer | void {
   // 如果不是对象,就不必设置getter好和setter
   if (!isObject(value) {
       return
   }
   let ob: Observer | void
   //通过‘__ob__’,判断是否有Observer实例,如果已经打过标记了,就直接拿出Observer的实例对象
   if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
       ob = value.__ob__
   } else if (
       /**
        * 确保value纯对象,且没有被是否Observer过
        */
       shouldObserve && //是否Observer过,通过toggleObserving来修改
       !isServerRendering() && // 是否是服务端渲染
       (Array.isArray(value) || isPlainObject(value)) && //isPlainObject判断类型是否是object
       Object.isExtensible(value) && //isExtensible判断对象是否可以扩展
       !value._isVue  // 避免vue实例被观察
   ) {
       ob = new Observer(value)
   }
    return ob
   }

从上面的源码可以看到,通过new Observer ( obj )我们可以使对象变得可观测,那么下一步我们就要知道既然知道了数据什么时候变化,那该怎么去更新视图呢?该更新哪些视图呢,这就要先提到依赖收集了。

Released under the MIT License.