Vue3响应系统的实现(一)

前言

我们都知道Vue3响应系统采用的是Proxy代理的方式实现的,对比于Vue2这种方式更智能,解决了很多Vue2响应系统的一些小问题,比如增加属性或者删除属性无法触发响应。本篇文章主要针对Vue3如何用Proxy来实现响应,最后实现一个简单的响应式系统。

响应式数据的基本实现

在讨论响应式数据实现之前,我们需要先来讨论一下副作用函数。

1.什么是副作用函数

所谓副作用函数指的是会产生副作用的函数:

函数的执行会直接或间接影响其他函数的执行,换句话说就是当函数调用的时候会对外部产生影响

比如:

let val=1//全局变量
function effect(){
    val=2//修改全局变量,产生副作用
}

上面的代码中,在effect函数里对全局变量val进行了修改,如果之后有其他函数也用到了该全局变量,那么用的是修改后的值。通过这种方式就影响了其他函数执行的结果

比如,一个函数foo返回值是val的平方,那么foo函数在effect函数执行前执行得到的结果是1,而在effect函数执行后执行的结果为4

副作用函数很容易产生,由于JavaScript是动态语言,函数调用到底会不会产生副作用,只有代码真正运行的时候才能知道,因此在rollup这种打包工具就很难静态的分析哪些代码是dead code,所以我们经常看见代码中有/*#__PURE__#*/这种代码注释,其作用就是告诉打包器该函数调用不会产生副作用,可以放心进行Tree-Shaking 

2.什么是响应式数据

理解了什么是副作用函数,再来说说什么是响应式数据,例如:

let obj = {text:"hello world"}
function effect(){
  //effect函数执行会读取obj.text属性
  document.body.innerText=obj.text;
}

上面的代码中,副作用函数effect执行会设置body元素的innerText属性值为obj.text,在未来obj.text的值变化时我们希望副作用函数effect会重新执行,如:

obj.text = "你好 世界";

这段代码修改了obj.text的值,我们希望当值变化后副作用函数自动重新执行,实现了这个目标那么我们就可以说对象obj就是响应式数据。

3.响应式数据的简单实现

如何让obj变成响应式数据?

通过观察思考,我们可以发现了两条线索:

  1. 当副作用函数 effect 执行时,会触发字段 obj.text 的 读取 操作
  2. 当修改 obj.text 时,会触发字段 obj.text 的 设置 操作

我们可以通过拦截对象的读取和设置操作,如何进行相应的额外处理:存储副作用函数,并在需要的时候取出来执行,具体如下:

  1. 当读取字段 obj.text 时,我们把副作用函数effect存储到一个“桶”中;
  2. 当设置 obj.text 时,我们再把副作用函数从“桶”里取出来并执行

如图:

 那么我们怎么实现拦截一个对象属性的读取和设置操作呢?在ES2015之前我们只能通过 Object.defineProperty函数实现,这也是Vue2采用的方式;现在我们可以使用新特性代理对象Proxy来实现,这也是Vue3采用的方式

实现代码如下:

//存储副作用函数的桶
const bucket = new Set()

//原始数据
let data = {text:"hello world"}
//对原始数据的代理
let obj = new Proxy(data,{
  //拦截读取操作
  get(target,key){
    //将副作用函数effect添加到存储副作用函数的桶中
    bucket.add(effect)
    //返回属性值
    return target[key]
  },
  //拦截设置操作
  set(target,key,newVal){
    //设置属性值
    target[key]=newVal
    //把副作用函数从桶中取出来并执行
    bucket.forEach(fn=>fn())
    //返回true代表设置操作成功
    return true
  }
})

可以写一段简单的测试代码来测试一下,比如:

function effect(){
  document.body.innerText=obj.text
}
effect()
setTimeout(()=>{
  obj.text="你好 世界"
},1000)

在浏览器中运行上段代码会得到预期的结果

至此,简单的响应式数据就已经实现了,但是目前的实现还有很多缺陷,

比如我们直接通过名字(effect)来获取副作用函数,这种硬编码的方式很不灵活,副作用函数的名字应该可以任意取,甚至可以是一个匿名函数

关于如何解决这些缺陷以致于实现一个更完善的响应式系统,我们可以先思考思考然后继续读我的下一篇文章

猜你喜欢

转载自blog.csdn.net/m0_65335111/article/details/127729455