Vue3 reactive 响应式原理源码实现

news2024/11/24 14:22:30

学习小满的视频,更详细的讲解
Vue3响应式原理
视频

需要了解Proxy、Reflect函数

目录结构:
在这里插入图片描述
配置环境:

  • package.json
{
  "name": "vue-reactive",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev":"webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.9.0"
  }
}
// 安装ts
npm install -g typescript
// 初始化
npm init
  • tsconfig.json 关键的两个配置,target和module
{
  "compilerOptions": {
    "target": "es6",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "es2015",                                /* Specify what module code is generated. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}
  • reactive.ts
//@ts-nocheck
import {track,trigger} from './effect'
// 判断代理对象的值是否为空,不为空的话值是否是一个对象
const isObject = (target)=>target!=null&&typeof target==='object'

// 创建代理,proxy对象,使用其中的get和set方法
export const reactive = <T extends object>(targert : T)=>{
    
    
    let proxy =  new Proxy(targert,{
        get(target,key,receiver){
            // Reflect:与proxy搭配使用,不会在出错时抛出错误,如果没有该属性的值返回undefined
            let res = Reflect.get(target,key,receiver)
            track(target,key)
            // 代理对象的值是否为一个对象,如果是的话就进行代理,对深层的对象进行代理,使用递归的方法
            if(isObject(res)){
                return reactive(res)
            }
            return res
            
            
        },
        set(target,key,value,receiver){
             // Reflect:给对象target的key属性设置值为value,如果成功返回true,失败返回false
            let res = Reflect.set(target,key,value,receiver)
            trigger(target,key)
            return res
        },
    
        
    })
    console.log(proxy);
    return proxy
    
}
  • effect.ts

//@ts-nocheck
let activeEffect;
// 用来执行副作用函数的方法,至今不明白真正的作用,fn就是副作用函数,是html页面传过来的函数
export const effect = (fn:Function)=>{
    const _effect = function(){
        activeEffect = _effect
        fn()
    }
    _effect()
}
/* 
** 可能是因为这是个学习响应式的简略的程序,所以必须先通过get方法创建targetMap对象这个数据结构才能使用set方法

    1. 给user这个响应式对象创造另一种结构
    3. 先通过响应式对象的get方法,执行此方法中的track方法,建立一个Map结构
    4. 通过Map结构,将响应式对象和副作用函数建立联系,副作用函数是set结构

    targetMap:{
            {name: 'jack', age: 18, first: {…}} : {
                "name" : "副作用函数",
                "age" : "副作用函数",
                "first: {…}" : "副作用函数",
                "second: {…}" : "副作用函数",
                "third" : "副作用函数"
            }
    }
*/
const targetMap = new WeakMap()
export const track = (target,key)=>{
    console.log("执行了reactive.tarck");
    let depsMap = targetMap.get(target)
    if(!depsMap){
        depsMap = new Map()
        targetMap.set(target,depsMap)
    }
    let deps = depsMap.get(key)
    if(!deps){
        deps = new Set()
        depsMap.set(key,deps)
    }
    deps.add(activeEffect)
    console.log(targetMap);
    
}
/*
    1. 触发响应式对象的某一个key对应的value发生改变时,执行proxy的set方法,执行此方法中的trigger方法
    2. 从targetMap中通过key找到对应的value,value中储存着副作用函数,依次将副作用函数全部执行
    3. 假如在页面中有3处使用了响应式对象,则value中储存着3条副作用函数
*/
export const trigger = (target,key)=>{
    console.log("执行了reactive.trigger");
    
    const depsMap = targetMap.get(target)
    const deps = depsMap.get(key)
    deps.forEach(effect=>effect())
}
  • index.html
<html>

<body>
    <div id="app">

    </div>
    <script type="module">
        import { reactive} from './reactive.js'
        import { effect } from './effect.js'

        // 这里的user已经通过reactive成为了一个proxy对象,对user对象的操作,就是对proxy对象的操作
        const user = reactive({ 
            name: 'Tom',
            age: 18,
            first:{
                second:{
                    third:"第三层"
                }
            }
        })
       // 执行到这里的时候,只是做个proxy的代理
        effect(() => {
            // 在执行${user.name}时才执行proxy代理中相应的get方法
           // 每个对象对应一个targetMap,但是结构中并不是储存所有的属性的map结构,只有在执行到对应属性的get方法时才在targetMap中添加该属性对应的结构
            document.querySelector('#app').innerText = `${user.name}---${user.first.second.third}`
            // document.querySelector('#app').innerText = `${user.name} -- ${user.age}`
        })
        user.name="jack"//执行赋值的操作时才会执行proxy的set方法
        // setTimeout(() => {
        //     user.name="jack",//执行赋值的操作时才会执行proxy的set方法
        //     setTimeout(()=>{
        //         user.first.second.third="第三层修改"
        //     },1000)
        // },1000)
    </script>
</body>

</html>
  • 执行操作:
  1. 在VScode中安装插件:Live Server
  2. 在终端中运行tsc,将ts文件编译为js文件
  3. 修改reactive.js文件,import { track, trigger } from './effect.js';
  4. 右键index.html,使用Open with live server

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2045921.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Kubernetes】Service 类型

Service 类型 1.NodePort2.ClusterlP3.LoadBalance4.ExternalName 在《Service 概念与实战》一文中&#xff0c;Service 的发布使用的是 NodePort 类型。除此之外&#xff0c;Service 的发布还支持 ClusterlP、LoadBalancer 和 ExternalName 这 3 种类型。 1.NodePort 在把 Se…

基于STM32开发的智能门铃系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化按钮与蜂鸣器控制显示与远程通知Wi-Fi通信应用场景 家庭智能门铃办公室访客通知常见问题及解决方案 常见问题解决方案结论 1. 引言 智能门铃系统通过集成按钮、蜂鸣器、显示屏、W…

HTML补充——表格表单

一、表格 1、在现实生活中&#xff0c;我们经常需要使用表格来表示一些格式化数据&#xff1a;课程表、人名表、成绩单 同样在网页中我们也需要使用表格&#xff0c;我们通过table标签创建表格。 2、在table标签中使用tr表示表格中的一行&#xff0c;有几个tr就有几行&#xff…

prometheus数据如何清理

1. 停止prometheus服务 2. 进到prometheus数据目录 3. 删除数据 3.1 删除持久化的数据块 Prometheus 将数据分块存储&#xff0c;每个块对应一个时间段。你可以通过查看目录中的时间戳来找到需要删除的数据块。 每个块的目录名是一个时间戳范围&#xff0c;例如 16094592000…

单片机在线升级架构(bootloader+app)

1、架构&#xff08;bootloaderapp) 在一定的时间内如果没有程序需要更新则自动跳转到app地址执行用户程序 内部flash 512K bootloader 跑裸机 48k 主要实现USB升级和eeprom标志位升级 app 跑freeRtos 464K 程序的基本功能&#xff0c;升级时软件复位开始执行bootloader升级…

互斥锁以及进程间通信

写线程 ---写资源 可以写数据 的条件 1.开始时 &#xff0c;buf空的 2.读线程 读完了 读线程 //buf充当读资源 //1.一开始&#xff0c;buf中没有数据可读的 1.写线程结束 信号量的机制 1.信号量 ----来描述 可使用的资源的个数 2.p操作 表示 使用这个资源 资…

毕业生实习与就业管理系统的设计与实现

TOC springboot297毕业生实习与就业管理系统的设计与实现 绪论 1.1 研究背景 现在大家正处于互联网加的时代&#xff0c;这个时代它就是一个信息内容无比丰富&#xff0c;信息处理与管理变得越加高效的网络化的时代&#xff0c;这个时代让大家的生活不仅变得更加地便利化&a…

保存数据至后台表

保存数据至后台表-供大数据平台使用-JOB程序 *&---------------------------------------------------------------------* *&程序名称 &#xff1a;ZBD_JOB_001 *&程序描述 : 保存数据至后台表-供大数据平台使用-JOB程序 *…

[Linux] 什么是 Shell?

一、什么是 shell ? shell在英语中的意思就是外壳&#xff0c;所以我们习惯称shell程序为壳程序。那为什么又会被叫做壳程序呢&#xff1f;那是因为shell程序是在内核上面的&#xff0c;属于操作系统的外壳部分&#xff0c;因此我们就称之为壳程序(shell)。 在 Linux 中&#…

增材制造正在加速赋能模具产业转型升级

模具&#xff0c;作为制造业的基石&#xff0c;正随着经济的蓬勃发展与产业的深度转型而面临更高要求。特别是注塑模具的冷却系统&#xff0c;传统工艺在面对随形冷却水路时显得力不从心&#xff0c;导致冷却效率无法进一步提升。而3D打印技术的崛起&#xff0c;则为模具领域开…

财务会计与管理会计(七)

文章目录 电商快递费用计算IF、VLOOKUP函数的应用 交费分布统计表SUMPRODUCT函数的应用 考勤签到统计系统OFFSET、MATCH函数的应用 出入库余额自动核算系统SUMPRODUCT、LOOKUP函数的应用 分段收费的典型案例VLOOKUP函数、MIN函数、MAX函数的应用 全额累进与超额累进计算提成全额…

《走走停停》,观后感

他这辈子看起来&#xff0c;好像就不是很成功。但是我们都很怀念这个人。 我们的文化太过强调永远&#xff0c;并把“永远”和“成功”牢牢捆绑了起来。 比如你开了一家咖啡店&#xff0c;这家店给你带来了很多快乐。但后来成本变高了&#xff0c;经营压力也变大了&#xff0…

求个位数(c语言)

1./描述 //给你一个数&#xff0c;让他进行巴啦啦能量&#xff0c;沙鲁沙鲁&#xff0c;小魔仙大变身&#xff0c;如果进行变身的数不满足条件的话&#xff0c;就继续让他变身。。。直到满足条件为止。 //巴啦啦能量&#xff0c;沙鲁沙鲁&#xff0c;小魔仙大变身&#xff1a;对…

2024/8/15 英语每日一段

A new Google update will make it simpler to request the removal of fake explicit images, as public figures, teachers and ordinary people increasingly contend with targeted abuse in the form of “deepfakes,” or realistic-looking images made with AI. While …

C# 学习笔记17:上位机助手_页面生成多控件滚动效果_保存与加载控件文本到文件_多字符串发送界面

今日继续完善更新我的上位机助手&#xff0c;这次完善多字符串发送的部分&#xff1a; 目前上位机助手支持以下功能&#xff1a; 1、 普通的16进制\ASCLL显示收发 2、 全页更新HEX显示&#xff08;会自动断串口&#xff09; 3、 日志辅助显示报错 4、 必要的清除日志区、接…

Hbase图形化界面

分享一个好用的hbase图形化界面 安装包&#xff1a;链接: https://pan.baidu.com/s/11Y2cDlme-P2xe--pYqy6MQ?pwdguag 提取码: guag 1、上传项目到linux 2、修改数据库配置信息 application-druid.yml 修改url、username、password为数据库连接信息 3、创建数据库(注意字符集…

display:flex布局,最简单的案例

1. 左右贴边 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>#parent{width: 800px;background: red;height: 200px;display: flex;justify-content: space-between…

vscode 远程免密登录

Windows R 输入 cmd在命令行终端中输入 ssh-keygen 一直回车、确定 生成秘钥 3. C:\用户\xxx.ssh 拷贝公钥内容 id_rsa.pub 4. 在虚拟机~/.ssh/ 下创建文件touch authorized_keys,拷贝公钥内容 id_rsa.pub粘贴到authorized_keys里即可。

某市-2024【网安·理论】初赛-web1-扫雷-wp

进来是个简单的扫雷 看源码是纯js写的 看了下主要格子之类的生成逻辑在jms.js里 其中flag的输出条件也包含在jms.js 格式化了一下 看特征是base64了&#xff0c;然后又经过了别的操作&#xff0c;不过他混淆了一下就懒得看了。 知道的是每过一个难度的都可以拿到1/3个fl…

【网络】UDP回显服务器和客户端的构造,以及连接流程

回显服务器&#xff08;Echo Server&#xff09; 最简单的客户端服务器程序&#xff0c;不涉及到业务流程&#xff0c;只是对与 API 的用法做演示 客户端发送什么样的请求&#xff0c;服务器就返回什么样的响应&#xff0c;没有任何业务逻辑&#xff0c;没有进行任何计算或者…