JS高级——vue2和vue3响应式原理

一、对象的监听方式

1.1 vue2中监听对象:Object.defineProperty(obj,key,descriptor)

        vue2中使用Object.defineProperty来进行对象的监听:

//先定义一个对象
const obj = {
  age:16,
  height:1.88,
  grade:99
}

//遍历对象,对每个属性进行监听
Object.keys(obj).forEach(key => {
  let value = obj[key]
  Object.defineProperty(obj,key,{
    //通过存取属性描述符进行操作
    get() {
      console.log(`${key}属性被访问了`);
      return value
    },
    set(newValue) {
      console.log(`${key}属性被赋值了`);
      if (value === newValue) return
      value = newValue
    }
  })
})

obj.age
obj.height = 2.0

        但是通过这种方法会有一些缺点:首先,Object.defineProperty设计的初衷不是为了去监听一个对象中 所有的属性的;其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么 Object.defineProperty是无能为力的。

        那么我们怎样能正确监听对象呢?在ES6中,新增了proxy类,如果我们监听一个对象的相关操作,那么我们可以先创建一个代理对象(proxy对象),之后对该对象的所有操作,都通过代理对象来完成。

1.2 vue3中监听对象:const proxyObj = new Proxy(target, handler)

        vue3中通过Proxy类创建一个代理对象,之后的操作都是直接对代理对象的操作,而不是原有对象:

//先定义一个对象
const obj = {
  name: 'why',
  age: 20,
  height:1.98
}

//创建代理对象
//参数:
//target(侦听的对象obj)、key(将被设置的属性)、value(新属性的值)、receiver(调用的代理对象objProxy )

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    console.log(`对象的${key}属性被访问`,target);
    // return target[key]
    return Reflect.get(target,key,receiver)
  },
  set(target, key, value, receiver) {
    if (target[key] !== value) {
      // target[key] = value
      Reflect.set(target,key,value,receiver)
    console.log(`对象的${key}属性被设置值`,target);
    }
  },
  has(target, key) {
    // return key in target
    return Reflect.has(target,key)
  },
  deleteProperty(target, key) {
    // delete target[key]
    Reflect.deleteProperty(target,key)
  }
})

console.log(objProxy.name);
console.log(objProxy.age);

objProxy.name = 'kobe'
objProxy.age = 20

console.log('name' in objProxy);
delete objProxy.height
console.log(objProxy);

        上面代码中使用了Reflect对象,如果不了解Reflect对象的,点击了解Reflect 

二、实现响应式原理

2.1 vue2实现响应式

//1.定义一个全局函数对象,用来保存依赖函数
let activeReactiveFn = null

//2.定义一个监听函数
function watchFn(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

//3.定义一个类,用来给对象的每个属性添加对应的依赖函数,还有执行函数
class Depend {
  constructor() {
    this.reactiveFns = new Set()
  }
  //添加依赖函数
  addDepend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }
   //执行依赖函数
  notify() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
}

//4.创建一个weakMap,用来储存多个对象的多个属性对应的依赖
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
}

//注:vue2和vue3监听对象主要区别
// 5、对对象进行监听,并返回监听后的对象
function reactive(obj) {
  Object.keys(obj).forEach(key => {
    const value = obj[key]
    Object.defineProperty(obj, key, {
      get() {
        const depend = getDepend(obj, key)
        depend.addDepend()
        return value
      },
      set(newValue) {
        value = newValue
        const depend = getDepend(obj, key)
        depend.notify()
      }
    })
  })
  return obj
}

//6、定义一个对象
const obj = {
  name: "why",
  age: 18 
}
// 7、调用函数对对象进行监听
reactive(obj)

//8、收集对象的属性监听时所触发的函数
watchFn(() => {
  console.log(obj.name, '监听对象obj的name属性');
})
watchFn(() => {
  console.log(obj.age, '监听对象obj的age属性');
})

//9、对对象进行操作
obj.name = 'code'
obj.age = 22

2.2 vue3实现响应式

//1.定义一个全局函数对象,用来保存依赖函数
let activeReactiveFn = null

//2.定义一个监听函数
function watchFn(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

//3.定义一个类,用来给对象的每个属性添加对应的依赖函数,还有执行函数
class Depend {
  constructor() {
    this.reactiveFns = new Set()
  }
  //添加依赖函数
  addDepend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }
   //执行依赖函数
  notify() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
}

//4.创建一个weakMap,用来储存多个对象的多个属性对应的依赖
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
}

//注:vue2和vue3监听对象主要区别
// 5、创建代理对象函数
//参数:
//target(侦听的对象obj)、key(将被设置的属性)、value(新属性的值)、receiver(调用的代理对象objProxy )
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      // 根据target.key获取对应的depend
      const depend = getDepend(target, key)
      // 给depend对象中添加响应函数
      depend.addDepend()
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver)
      const depend = getDepend(target, key)
      depend.notify()
    }
  })
}

//6、定义一个对象
const obj = {
  name: "why",
  age: 18 
}
// 7、通过函数创建代理对象
const objProxy = reactive(obj)

//8、监听代理对象的属性时所触发的函数
watchFn(() => {
  console.log(objProxy.name, '监听代理对象objProxy的name属性');
})
watchFn(() => {
  console.log(objProxy.age, '监听代理对象objProxy的age属性');
})

//9、对代理对象进行操作
objProxy.name = 'code'
objProxy.age = 22

        上面代码中,使用了Set()WeakMap()Map()等ES6中新增的数据结构,分别点击可了解详细信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值