一个简易的响应式系统

是什么

副作用函数

function effect(){
	document.body.innerText = obj.text
}

函数中使用设置了document.body.innerText的值,而全局其他地方也能设置这个值,因此这个函数的执行会间接影响其他地方,便产生了副作用

响应式数据

当数据改变时,其它使用这个数据的函数自动执行,
则称这个数据是响应式数据

实现

  1. 当副作用函数effect执行,使用读取响应式数据,也就是触发数据的get操作--------进行effect 函数的收集
  2. 当响应式数据改变,也就是触发数据的set操作—取出effect数据重新执行

需要截取数据对象的get与set操作

vue2中使用Object.defineProperty()实现
vue3中使用Proxy实现


需求1: effct函数与响应式数据绑定

在这里插入图片描述

  //副作用函数

  function effect(){

    document.body.innerText = obj.test

}

   //存储副作用函数的桶

const bucket = new Set()

  

//原始数据

const data = {test:"hello world"}

//对原始数据的代理

const obj = new Proxy(data,{

    //拦截读取操作

    get(target,key){

        //将副作用函数添加到桶中

        bucket.add(effect)

        //返回属性值

        return target[key]

    },

    //拦截写入操作

    set(target,key,newValue){

        //设置属性值

        target[key] = newValue

        //把副作用函数从桶里取出执行

        bucket.forEach(fn => fn())

        return true;//返回true代表设置成功

    }

  

})

  
  
  

// 1. 触发副作用函数,触发读取

effect()

  
//2.修改数据,触发set
//1秒后修改数据

setTimeout(() => {

    obj.test = "hello vue3"

},1000)

需求2:副作用函数的依赖收集

未实现:

  • 副作用函数的依赖收集(如果副作用函数是匿名函数,或者不叫effect,那么桶中就不会有这个函数)

 //用一个全局变量来存储被注册的副作用函数,
  let activeEffect  = null
//存储副作用函数的桶
const bucket = new Set()
//effect 函数用来注册副作用函数
function effect(fn){
	//将副作用函数赋值给activeEffect
	 activeEffect = fn
	//执行副作用函数
	fn()
}
//原始数据
 const data = {test:"hello world"}
 //对原始数据的代理
 const obj = new Proxy(data,{
    get(target,key){
        //只有当activeEffect有值的时候,才将其添加到桶中
        if(activeEffect){
            //将activeEffect添加到桶中
            bucket.add(activeEffect)

        }
        return target[key]
    },

    set(target,key,newValue){

        target[key] = newValue

        //把副作用函数从桶里取出执行

        bucket.forEach(fn => fn())

        return true;//返回true代表设置成功

    }

 })

 //1.副作用函数执行 , 触发get
 effect(function(){
   document.body.innerText = obj.test;
 })

  
//2.修改数据 , 触发set
 setTimeout(() => {
     obj.test = "hello vue3"
 },1000)

需求3:字段与函数建立明确的联系

发现bug:

当给代理对象添加不存在的属性的时候,按理说不应该触发副作用函数,但是实际上触发了!!!
导致该问题的根本原因是,我们没有在副作用函数与被操作的目标字段之间建立明确的联系。

  • 当设置属性时,无论设置的是哪一个属性,也都会把“桶”里的副作用函数取出并执行
  • 当读取属性时,无论读取的是哪一个属性,都会把副作用函数收集到“桶”里;

解决方案:

  • 在副作用函数与被操作的目标字段之间建立明确的联系

  • 重新设计桶结构:树形结构

在这里插入图片描述

不难看出是树形结构

选择weakMap代替set

weakMap表示键值对对象数据结构,键只能是对象,是弱引用,当在WeakMap中使用的是某个对象的引用作为键,那么这个对象被回收后(即没有其他地方引用它),对应的键值对也会被自动删除。这种弱引用特性有助于避免内存泄漏。
在这里插入图片描述

//存储副作用函数的桶

const bucket = new WeakMap()

// 全局变量,存储被注册的副作用函数

let activeEffect  = null

 //effect 函数用来注册副作用函数

function effect(fn){

      //将副作用函数赋值给activeEffect

      activeEffect = fn

      //执行副作用函数

      fn()

 }

//将副作用函数添加到桶中
function track(target,key)

{

 //根据target从桶中获取 depsMap,它也是一个Map类型:key是目标对象,value还是个Map,存储着副作用函数

 let depsMap = bucket.get(target)

 if(!depsMap){

     depsMap = new Map()

     bucket.set(target,depsMap)

 }

 //根据key从depsMap中获取 deps,它是一个Set类型,存储着与当前key相关联的副作用函数

 let deps = depsMap.get(key)

 if(!deps){

     deps = new Set()

     depsMap.set(key,deps)

 }

 //将当前副作用函数添加到deps中

 deps.add(activeEffect)  

}

  

//从桶中根据target和key获取对应的副作用函数

function trigger(target,key)

{

 //根据target从桶中获取 depsMap,它也是一个Map类型:key是目标对象,value还是个Map,存储着副作用函数

 let depsMap = bucket.get(target)

 if(!depsMap){

     depsMap = new Map()

     bucket.set(target,depsMap)

 }

 //根据key从depsMap中获取 deps,它是一个Set类型,存储着与当前key相关联的副作用函数

 let deps = depsMap.get(key)

 if(!deps){

     deps = new Set()

     depsMap.set(key,deps)

 }

 //把副作用函数从桶里取出执行

 deps.forEach(fn => fn())

}

  
  
  

  //原始数据

 const data = {test:"hello world"}

 //对原始数据的代理

 const obj = new Proxy(data,{

    get(target,key){

        //只有当activeEffect有值的时候,才将其添加到桶中

        if(activeEffect){

          track(target,key)

        }

        return target[key]

    },

    set(target,key,newValue){

        target[key] = newValue

        //触发副作用函数重新执行

        trigger(target,key)

        return true;//返回true代表设置成功

    }

 })

  

  //测试

 effect(function(){

    console.log("effect run");

     document.body.innerText = obj.test;

 })

  

 setTimeout(() => {

     obj.test = "hello vue3"

     obj.say="hello vue3"//触发输出effect run

 },1000)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值