• Vue3响应系统的实现(一)


    前言

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

    响应式数据的基本实现

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

    1.什么是副作用函数

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

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

    比如:

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

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

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

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

    2.什么是响应式数据

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

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

    上面的代码中,副作用函数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采用的方式

    实现代码如下:

    1. //存储副作用函数的桶
    2. const bucket = new Set()
    3. //原始数据
    4. let data = {text:"hello world"}
    5. //对原始数据的代理
    6. let obj = new Proxy(data,{
    7. //拦截读取操作
    8. get(target,key){
    9. //将副作用函数effect添加到存储副作用函数的桶中
    10. bucket.add(effect)
    11. //返回属性值
    12. return target[key]
    13. },
    14. //拦截设置操作
    15. set(target,key,newVal){
    16. //设置属性值
    17. target[key]=newVal
    18. //把副作用函数从桶中取出来并执行
    19. bucket.forEach(fn=>fn())
    20. //返回true代表设置操作成功
    21. return true
    22. }
    23. })

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

    1. function effect(){
    2. document.body.innerText=obj.text
    3. }
    4. effect()
    5. setTimeout(()=>{
    6. obj.text="你好 世界"
    7. },1000)

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

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

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

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

  • 相关阅读:
    【C++笔记】多态的原理、单继承和多继承关系的虚函数表、 override 和 final、抽象类、重载、覆盖(重写)、隐藏(重定义)的对比
    【ESP32】 OTA 升级简述与 Flash 分区介绍
    第二十篇-推荐-纯CPU(E5-2680)推理-llama.cpp-qwen1_5-72b-chat-q4_k_m.gguf
    16. 机器学习——决策树
    【华为OD题库-033】经典屏保-java
    目前市面上最好的AI对话APP是哪个?
    如何在局域网外SSH远程访问连接到家里的树莓派?
    在mac上安装node.js及使用npm,yarn相关命令教程
    SQALE 是什么
    【HMS Core】游戏初始化
  • 原文地址:https://blog.csdn.net/m0_65335111/article/details/127729455