美文网首页
Vue数据劫持原理-源码分析

Vue数据劫持原理-源码分析

作者: FConfidence | 来源:发表于2020-03-27 23:09 被阅读0次

响应式原理分析

  1. 原理流程图概览


    image.png
  1. 响应式相关的源码在(源码位于instance/index.js) stateMixin

  2. stateMixin中initData实现两个功能

    • 将data中的对象代理(proxy)到_data上

      export function proxy (target: Object, sourceKey: string, key: string) {
        sharedPropertyDefinition.get = function proxyGetter () {
          return this[sourceKey][key]
        }
        sharedPropertyDefinition.set = function proxySetter (val) {
          this[sourceKey][key] = val
        }
        Object.defineProperty(target, key, sharedPropertyDefinition)
      }
      // 也就是说vm._data.变量都是响应式数据(即vm.变量)。
      
    • 将data中的数据变为响应式数据,即

      // observe data
      observe(data, true /* asRootData */)
      
    • observe类 (上一步中的observer函数)

      export function observe (value: any, asRootData: ?boolean): Observer | void {
        if (!isObject(value) || value instanceof VNode) {
          return
        }
        let ob: Observer | void
        if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
          ob = value.__ob__
        } else if (
          shouldObserve &&
          !isServerRendering() &&
          (Array.isArray(value) || isPlainObject(value)) &&
          Object.isExtensible(value) &&
          !value._isVue
        ) {
          // 将value变成响应式数据
          ob = new Observer(value)
        }
        if (asRootData && ob) {
          ob.vmCount++
        }
        return ob
      }
      
    • Observer的构造函数&walk函数

      constructor (value: any) {
        this.value = value
        // 每一个响应式对象都有一个Dep去收集依赖watcher
        this.dep = new Dep()
        this.vmCount = 0
        def(value, '__ob__', this)
        if (Array.isArray(value)) {
          const augment = hasProto
            ? protoAugment
            : copyAugment
          augment(value, arrayMethods, arrayKeys)
          this.observeArray(value)
        } else {
          this.walk(value)
        }
      }
      
      /**
      * Walk through each property and convert them into
      * getter/setters. This method should only be called when
      * value type is Object.
      */
      walk (obj: Object) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
          defineReactive(obj, keys[i])
        }
      }
      
    • 上面的defineReactive方法将数据变为响应式,核心代码:

      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()
          }
          if (setter) {
            setter.call(obj, newVal)
          } else {
            val = newVal
          }
          childOb = !shallow && observe(newVal)
          // 当数据变化时触发更新。
          dep.notify()
      }
      
  3. Dep类

    // 可以理解为观察者模式中的发布者
    export default class Dep {
    
      static target: ?Watcher;
      id: number;
    
      // 理解为观察者
      subs: Array<Watcher>;
    
      constructor () {
        this.id = uid++
        this.subs = []
      }
    
      // 收集依赖
      addSub (sub: Watcher) {
        this.subs.push(sub)
      }
    
      // 移除依赖
      removeSub (sub: Watcher) {
        remove(this.subs, sub)
      }
    
      depend () {
        if (Dep.target) {
          // 在watcher中添加dep依赖
          Dep.target.addDep(this)
        }
      }
    
      // 提醒(data, computed, watch)各类型的watcher , 进行更新
      notify () {
        // stabilize the subscriber list first
        const subs = this.subs.slice()
        if (process.env.NODE_ENV !== 'production' && !config.async) {
          // subs aren't sorted in scheduler if not running async
          // we need to sort them now to make sure they fire in correct
          // order
          subs.sort((a, b) => a.id - b.id)
        }
        for (let i = 0, l = subs.length; i < l; i++) {
          subs[i].update()
        }
      }
    }
    

    Dep类的构造函数中的subs是Watcher(观察者)类。vue实例中data的一个值,可以添加多个Watcher,同时这个值变化的时候也是触发这多个Watcher的更新。

  1. Watcher类

  2. Observer、Dep和Watcher类关系


    image.png
Observer类是发布者(主题)(vue实例的data对象),里面有好多订阅者(Dep类),每本主题可以被订阅(Watcher类)。  

当某主题更新的时候,订阅的Watcher类会收到通知,进而更新订阅者就会收到消息然后进行自我更新(vue实例的data对象)。  

Observer类, 给data中的属性分配Dep, 一个Dep可以有多个类型的依赖watcher (如data,computed,watch)

相关文章

  • Vue数据劫持原理-源码分析

    响应式原理分析 原理流程图概览image.png 响应式相关的源码在(源码位于instance/index.js)...

  • Vue2.x 的双向绑定原理及实现

    Vue 数据双向绑定原理 Vue 是利用的 Object.defineProperty()方法进行的数据劫持,利用...

  • vue数据绑定原理

    实现原理:vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的.1)数据劫持、vue是通过Obje...

  • vue面试知识点

    vue 数据双向绑定原理 vue实现数据双向绑定原理主要是:采用数据劫持结合发布订阅设计模式的方式,通过对data...

  • vue实现双向绑定原理

    原理 vue数据双向绑定通过‘数据劫持’ + 订阅发布模式实现 数据劫持 指的是在访问或者修改对象的某个属性时,通...

  • Vue MVVM 原理实现

    核心原理 MVVM 双向数据绑定, 数据驱动视图 Vue 实现 MVVM 采用 数据劫持 + 发布订阅模式 : ...

  • vue面试常被问到的问题整理

    vue面试常被问到的问题整理 1、Vue的双向数据绑定原理是什么? 答 : vue是采用数据劫持,并且使用发布-订...

  • vue 数据劫持原理

  • vue数据双向绑定原理

    vue数据双向绑定原理 vue.js 采用数据劫持结合发布者-订阅者模式的方式,通过Object.definePr...

  • vue3.x 与 vue2.x 在写法上的区别

    1. 数据双向绑定的原理 1.1 原理 Vue2.0使用Object.defineProperty来劫持对象属性的...

网友评论

      本文标题:Vue数据劫持原理-源码分析

      本文链接:https://www.haomeiwen.com/subject/atdduhtx.html