Vue响应式原理笔记

94 阅读6分钟
初步进行响应式函数封装
思路如下:当对象的属性进行改变时,手动通知需要执行的函数

// 把所有需要响应式的函数存到数组中
let reactiveFns = []

//把需要执行的函数放到数组中
function watchFn(fn) {
  reactiveFns.push(fn)
}

const obj = {
  name: "lhm",
  age: 18
}

function foo() {
  console.log('这是需要执行的代码');
}
watchFn(foo)

function bar() {
   console.log("这个函数不需要接收");
}

obj.name = 'kobe'

reactiveFns.forEach(fn => {
  fn()
})
第二步:依赖收集类的封装
思路如下:使用Depend类收集某个属性的所有依赖,每个属性都会对应一个类的对象,每个对象都会有一个reactiveFns数组,数组里存储每一个需要执行的函数,notyfy函数用来通知该执行的函数
// 这个类用来收集某个属性的所有依赖,
// 每个属性都会对应一个类的对象,每个对象都会有一个reactiveFns数组,数组里存储每一个需要执行的函数
class Depend {
  constructor () {
    this.reactiveFns = []
  }

  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn)
  }

  // notyfy用来通知每一个函数
  notyfy() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
}

const depend = new Depend()
function watchFn(fn) {
  depend.addDepend(fn)
}

const obj = {
  name: "lhm",
  age: 18
}

watchFn(function () {
  console.log(objProxy.name + '发生变化需要执行的代码');
})

watchFn(function () {
  console.log(objProxy.age + '发生变化需要执行的代码');
})
obj.name = 'kobe'

depend.notyfy()
第三步:自动监听属性的变化,然后执行depend的notify函数
思路如下:监听对象的属性变化有常用的两种方式:Proxy(Vue3的实现原理)和defineProepet(Vue2的实现原理),以下使用Proxy

// 这个类用来收集某个属性的所有依赖,
// 每个属性都会对应一个类的对象,每个对象都会有一个reactiveFns数组,数组里存储每一个需要执行的函数
class Depend {
  constructor () {
    this.reactiveFns = []
  }

  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn)
  }

  // notyfy用来通知每一个函数
  notyfy() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
}

const depend = new Depend()
function watchFn(fn) {
  depend.addDepend(fn)
}

const obj = {
  name: "lhm",
  age: 18
}

// 监听对象的属性变化有常用的两种方式:Proxy(Vue3的实现原理)和defineProepet(Vue2的实现原理)
const objProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
  set: function (target, key, newValue, receiver) {
    Reflect.set(target, key, newValue, receiver)
    depend.notyfy()
  }

}) 

watchFn(function () {
  console.log(objProxy.name + '发生变化需要执行的代码');
})

watchFn(function () {
  console.log(objProxy.age + '发生变化需要执行的代码');
})

objProxy.name = 'kobe'
objProxy.age = 30
第四步:依赖收集的管理
思路如下:在代理对象的get上收集函数,并把函数存入对应的depend对象中

// 这个类用来收集某个属性的所有依赖,
// 每个属性都会对应一个类的对象,每个对象都会有一个reactiveFns数组,数组里存储每一个需要执行的函数
class Depend {
  constructor () {
    this.reactiveFns = []
  }

  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn)
  }

  // notyfy用来通知每一个函数
  notyfy() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
}

let activeReactiveFn = null
function watchFn(fn) {
  // debugger;
  activeReactiveFn = fn
  fn()
  // activeReactiveFn = null
}
// 把每个属性和对应的函数以Map的方式存储起来
// const objMap = new Map()
// objMap.set("name", "nameDepend")
// objMap.set("age", "ageDepend")

// const infoMap = new Map()
// infoMap.set('address', 'addressDepend')

// 把每个对象和对应的Map存储到WeakMap中
// const targetMap = new WeakMap()
// targetMap.set(obj, objMap)
// targetMap.set(info, infoMap)

// 假设是obj的name属性发生了变化
// const dependObj = targetMap.get(obj).get(get)
// dependObj.notyfy()

// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
  // 根据target对象获取map的过程
  let map = targetMap.get(target)
  if(!map) {
    // 如果没有值,需要创建一个
    map = new Map()
    targetMap.set(target, map)
  }

  // 根据key获取depend对象,如果没有值就创建
  let depend = map.get(key)
  if(!depend) {
    depend = new Depend()
    map.set(key, depend)
  }
  return depend
}


const obj = {
  name: "lhm",
  age: 18
}
const info = {
  address: '广州市'
}

// 监听对象的属性变化有常用的两种方式:Proxy(Vue3的实现原理)和defineProepet(Vue2的实现原理)
const objProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    // console.log(activeReactiveFn);
    // 根据target.key获取对应的depend
    const depend = getDepend(target, key)
    // 给depend对象中添加响应式函数、
    depend.addDepend(activeReactiveFn)

    return Reflect.get(target, key, receiver)
  },
  set: function (target, key, newValue, receiver) {
    Reflect.set(target, key, newValue, receiver)
    // depend.notyfy()
    const depend = getDepend(target, key)
    depend.notyfy()
  }
}) 

watchFn(function () {
  console.log(objProxy.name + '发生变化需要执行的代码1');
})


watchFn(function () {
  console.log(objProxy.age + '发生变化需要执行的代码1');
})

watchFn(function () {
  console.log(objProxy.age + '新函数');
})


objProxy.age = 30
objProxy.name = '啦啦啦'

第五步:对depend类的优化和重构
(1) depend方法
(2) 使用Set来保存依赖函数而不是数组[]
let activeReactiveFn = null
// 这个类用来收集某个属性的所有依赖,
// 每个属性都会对应一个类的对象,每个对象都会有一个reactiveFns数组,数组里存储每一个需要执行的函数
class Depend {
  constructor () {
    // 为防止重复,把数组变成set存储
    this.reactiveFns = new Set()
  }

  addDepend(reactiveFn) {
    this.reactiveFns.add(reactiveFn)
  }

  depend() {
    if(activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }

  // notyfy用来通知每一个函数
  notyfy() {
    this.reactiveFns.forEach(fn => {
      fn()

    })
  }
}


function watchFn(fn) {
  // debugger;
  activeReactiveFn = fn
  fn()
  // activeReactiveFn = null
}
// 把每个属性和对应的函数以键值对的方式存储起来
// const objMap = new Map()
// objMap.set("name", "nameDepend")
// objMap.set("age", "ageDepend")

// const infoMap = new Map()
// infoMap.set('address', 'addressDepend')

// 把每个对象和对应的Map存储到WeakMap中
// const targetMap = new WeakMap()
// targetMap.set(obj, objMap)
// targetMap.set(info, infoMap)

// 假设是obj的name属性发生了变化
// const dependObj = targetMap.get(obj).get(get)
// dependObj.notyfy()

// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
  // 根据target对象获取map的过程
  let map = targetMap.get(target)
  if(!map) {
    // 如果没有值,需要创建一个
    map = new Map()
    targetMap.set(target, map)
  }

  // 根据key获取depend对象,如果没有值就创建
  let depend = map.get(key)
  if(!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend

}


const obj = {
  name: "lhm",
  age: 18
}
const info = {
  address: '广州市'
}

const objInfo = recative(info)
const objObj = recative(obj)

function recative(obj) {
  return new Proxy(obj, {
    get: function (target, key, receiver) {
      // console.log(activeReactiveFn);
      // 根据target.key获取对应的depend
      const depend = getDepend(target, key)
      // 给depend对象中添加响应式函数、
      depend.depend()
  
      return Reflect.get(target, key, receiver)
    },
    set: function (target, key, newValue, receiver) {
      Reflect.set(target, key, newValue, receiver)
      // depend.notyfy()
      const depend = getDepend(target, key)
      depend.notyfy()
    }
  
  }) 
}


watchFn(function () {
  console.log(objProxy.name + '----------------');
  console.log(objProxy.name + '++++++++++++++++');
})

objProxy.name = '啦啦啦'

vue2的响应式原理
let activeReactiveFn = null
// 这个类用来收集某个属性的所有依赖,
// 每个属性都会对应一个类的对象,每个对象都会有一个reactiveFns数组,数组里存储每一个需要执行的函数
class Depend {
  constructor () {
    // 为防止重复,把数组变成set存储
    this.reactiveFns = new Set()
  }

  addDepend(reactiveFn) {
    this.reactiveFns.add(reactiveFn)
  }

  depend() {
    if(activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }

  // notyfy用来通知每一个函数
  notyfy() {
    this.reactiveFns.forEach(fn => {
      fn()

    })
  }
}


function watchFn(fn) {
  // debugger;
  activeReactiveFn = fn
  fn()
  // activeReactiveFn = null
}
// 把每个属性和对应的函数以键值对的方式存储起来
// const objMap = new Map()
// objMap.set("name", "nameDepend")
// objMap.set("age", "ageDepend")

// const infoMap = new Map()
// infoMap.set('address', 'addressDepend')

// 把每个对象和对应的Map存储到WeakMap中
// const targetMap = new WeakMap()
// targetMap.set(obj, objMap)
// targetMap.set(info, infoMap)

// 假设是obj的name属性发生了变化
// const dependObj = targetMap.get(obj).get(get)
// dependObj.notyfy()

// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
  // 根据target对象获取map的过程
  let map = targetMap.get(target)
  if(!map) {
    // 如果没有值,需要创建一个
    map = new Map()
    targetMap.set(target, map)
  }

  // 根据key获取depend对象,如果没有值就创建
  let depend = map.get(key)
  if(!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend

}

const obj = {
  name: "lhm",
  age: 18
}
const info = {
  address: '广州市'
}

const objInfo = recative(info)
const objObj = recative(obj)

function recative(obj) {
  obj.keys.forEach(key => {
    let value = obj[key]
    Object.defineProperty(obj, key, {
      get: function() {
        const depend = getDepend(obj, key)
        depend.depend()
        return value
      },
      set: function (newVallue) {
        const depend = getDepend(obj, key)
        depend.notyfy()
        obj[key] = newVallue
      }
    })
  })
  return obj
}
watchFn(function () {
  console.log(objProxy.name + '----------------');
  console.log(objProxy.name + '++++++++++++++++');
})

objProxy.name = '啦啦啦'


总结:以weakMap => Map => depend对象的数据结构来保存每一个函数和对象的关系,在对象获取时进行收集,对象赋值时通知每一个函数执行