vue2.x响应式原理

vue2.0响应式原理

三个核心 Observer,Dep,Watcher

Observer

  • data通过Observer转化成了getter/setter的形式来追踪变化

Dep

  • 收集依赖,删除依赖和向依赖发送消息

Watcher

  • Watcher就是依赖

下面来细细的说一下

  1. 我们在写vue代码的时候,希望那些数据将来是响应式的,会把他定义在data中
data() {
    return {
      active: "",
      account: "",
    };
  },

那我们的Observer类,就是将定义在data中的数据,转化为每个层级都是响应式的数据。

核心API就是Object.defineProperty,将属性转化为getter/setter的形式来追踪变化。

这两句话是一个意思🤓

至此数据的劫持已经完成,如果你是单纯的console.log()去验证这个数据的响应式,是完全ok了。但是我们Vue现在要做的是数据变化,就要驱动视图变化。那就引出了一个关键词 -> 依赖

  1. 何为依赖?

依赖,顾名思义,就是一种依靠关系。我们也说了vue就是数据驱动视图,所有用到数据的地方,就称之为依赖

在vue1.x中,是细粒度依赖,用到数据的DOM都是依赖

在Vue2.x中,是中等粒度依赖,用到数据的组件是依赖

  1. 在哪里收集依赖,又在哪里触发依赖?

在getter中收集依赖,在setter中触发依赖。getter/setter只是一个操作方法。具体肯定要将这些依赖存放在一个地方,那就是我们的另一个核心Dep

  1. 收集谁?谁是依赖就收集谁

换句话说,就是当属性变化后,通知谁。我们用到数据的地方有很多,而且类型还不一样,这时我们就需要抽出一个能集中处理这些情况的类。=》 Watcher

核心代码实现

// 对数组的处理
import { arrayMethods } from './arr'
function observe (data) {
  if (typeof data !== 'object' || data === null) return // 如果data中的数据不是对象,直接返回
  new Observer(data) // 在Observer类里,将数据进行响应式
}

class Observer {
  constructor(data){
  data.__ob__ = this // 给每一个监控过的对象都增加一个__ob__属性,作用就是描述这个对象已经被监测
  if (Array.isArray(data)) { // 如果是数组,通过代理原型的方式,进行
      data.__proto__ = arrayMethods // 通过重写数组原型方法来对数组的七种方法进行拦截
      this.observeArray(data)
    } else {
      this.walk(data) // 如果是对象,利用Object.definePrototype进行处理
    }
  }
  walk(data){ // vue如果数据层次过多,会递归解析对象中的属性,依次增加get和set方法
   // Object.keys(data) =》 [a,b,address]
    // defineReactive(data, key, key[data]) } 定义响应式数据。依次将[a,b,address]变为响应式
    Object.keys(data).forEach(key => defineReactive(data, key,key[data]))
  }
  observeArray(data){
    data.forEach(i=> observe(i))
  }
}

defineReactive (data,key,value){
  let childOb = observe(value) // 递归实现深度检测
  let dep = new Dep()
  Object.definePrototype(data,key,{
    get(){
      // 如果现在处于依赖的收集阶段
      if (Dep.target) {
        dep.depend()
        // 判断子元素
        if (childOb) {
          childOb.dep.depend()
        }
      }
      return value
    },
    set(newValue){
      if (newValue === value) return
      value = newValue 
       childOb = observe(newValue) // 继续劫持用户设置的值,因为用户可能设置的是一个对象
      // 发布订阅者模式,通知dep
      dep.notify() // 发布
    }
  })
}

let data = {
  a: 1,
  b: 2,
  address: {
    x: 'x'
  },
  arr: [1, 2, 3]
}
observe(data)
data.a = 33
data.address.x = 'XX'
data.address.x = { YY: "yy" }
data.arr.push([{ a: 1, b: 2 }, { c: 2 }, { d: 2 }])

arr.js 对数组观测

let oldArrayMethods = Arrar.prototype // 拿到数据的所有方法
export let arrayMethods = Object.create(oldArrayMethods) // arrayMethods通过__proto__拿到数组的方法
// Object.create()方法创建一个新对象,使现有的对象提供给新的对象的__proto__
let methods = ['push', 'shift', 'unshift', 'pop', 'splice', 'sort', 'reverse']
methods.forEach(method => {
 // arrayMethods[method],只能找这七个,但是上面Object.create,可以找到数组所有的方法,两手准备。
  arrayMethods[method] = function (...args) { // AOP切片编程。在不破坏封装的前提下,动态扩展功能
    let rest = oldArrayMethods[method].apply(this, args) // 调用原生的方法
    let inserted // 当前用户插入的数据
    let obj = this.__ob__ //this代表当前数据本身,因为this代表了当前Observer实例,所以包括了Observer里面的方法,所以可以通过this.__ob__来访问observeArray,实现继续观测新增属性 
    switch(methods) {
      case 'push',
      case 'unshift'
        inserted = args
        break;
      case 'splice'
        inserted = args.slice(2)
        break
    }
    if (inserted)  obj.observeArray(inderted) // 将新增的属性继续观测
    return rest
  } 
})

Dep

export default class Dep {
    constructor() {
        // 用数组存储自己的订阅者.subs存储的是watcher(订阅)的实例
        this.subs = []
    }
    //添加订阅
    addSub(sub) {
        this.subs.push(sub)
    }
    // 添加依赖
    depend() {
        if (Dep.target) { // 我们指定的全局的位置
            this.addSub(Dep.target)  // getter函数就会从全局唯一的地方,读取正在读取数据的watcher。并把这个watcger收集到dep中
        }
    }
    // 通知更新
    notify() {
        // 浅克隆一份
        const subs = this.subs.slice()
        subs.forEach((val => val.updata()))
    }
}
  • data.ob = this 次代码会导致监测过得数据,继续检测,我们做一个优化
    封装def
export function def (data, key, value) {
  Object.defineProperty(data, key, {
    enumerable: false, // 不可枚举,循环的时候循环不到
    configurable: false, // 不可配置
    value: value
  })
}
// data.__ob__ = this
def (data, '__ob__', this)
// 给每个响应式数据增加一个不可枚举的__ob__属性,并且指向了Observer实例。我们首先可以根据这个属性
// 防止已经被响应式观察的数据,反复被观测,其次响应式数据可以使用__ob__来获取Observer实例上相关的方法,这对数组很关键

WechatIMG442.png

未命名文件 (1)的副本.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值