Vue3 reactive 响应式原理源码实现

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

  1. reactiv响应式:简单的说就是个proxy的代理
  2. 响应式巧妙的地方就在于将要代理的数据重新做了个map数据结构
  3. 将修改数据的代码放在了map中,只要数据发生set,就会执行map中对应的代码来修改页面中对应数据

需要了解Proxy、Reflect函数

目录结构:
在这里插入图片描述

// 需要node环境
// 安装ts
npm install -g typescript
// 初始化,创建tsconfig.json文件
tsc --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. 在终端中运行tsc,将ts文件编译为js文件
  2. 修改reactive.js文件,import { track, trigger } from './effect.js';
  3. 在VScode中安装插件:Live Server
  4. 右键index.html,使用Open with live server
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值