响应式的核心原理
按照对 vue
的基本理解,响应式的本质是数据驱动视图,也就是说界面会随着数据的变化而自动更新,但是这种理解趋于应用层面了,更接近本质的理解应该是 vue
中数据的变化会让依赖该数据的函数跟着变化,这个函数并不局限于 render
,同时可以是 watchEffect
也可以是 computed
; 比如如下的 vue
代码,随着 firstName
的变化,render
computer
watchEffect
函数都会重新运行
js
<template>
<div>{{firatName}}</div>
<div>{{ lastName }}</div>
<div>{{ name }}</div>
<div @click="handleClick"></div>
</template>
<script setup>
const firatName = ref("张");
const lastName = ref("三");
const handleClick = ()=>{
firatName.value = "赵"
}
const name = computed(()=>{
return firatName.value+lastName.value;
})
watchEffect(()=>{
// 日志答应
console.log(firatName.value)
})
</script>
graph TD
firstName --> effect
firstName --> computed
firstName --> render
核心设计
基本流程就是利用 proxy
对象,在 get
的时候收集依赖的函数,在 set
的时候触发的依赖的函数,同时提供一个 watchEffect
函数用于在 get
的时候收集依赖,那么基本的流程图如下

核心代码
effect
js
import {
effectStack,
} from "./index.js";
export function effect(fn) {
effectStack.push(fn);
fn();
effectStack.pop(fn)
}
track
js
import {
effectStack,
effectMap
} from "./index.js";
export function track(target, key) {
if (effectStack.length === 0) {
return;
}
let proxyMap = effectMap.get(target);
if (!effectMap.has(target)) {
proxyMap = new Map()
effectMap.set(target, proxyMap);
}
if (!proxyMap.has(key)) {
proxyMap.set(key, new Set())
}
const proxySet = proxyMap.get(key);
// 拿到调用栈的最后一个环境,进行调用
proxySet.add(effectStack[effectStack.length - 1]);
}
trigger
js
import {
effectStack,
effectMap
} from "./index.js";
export function trigger(target, key){
const proxyMap = effectMap.get(target);
if(!proxyMap||!proxyMap.has(key)){
console.error("未拿到对应的依赖")
return ;
}
const proxySet = proxyMap.get(key);
proxySet.forEach(fn=>fn())
}
reactive
js
import {track,trigger} from "../effect/index.js"
export default function reactive(target){
return new Proxy(target,{
get(target,key,recevier){
track(target,key);
return Reflect.get(target,key,recevier)
},
set(target,key,newValue,recevier){
trigger(target,key);
return Reflect.set(target,key,newValue,recevier);
}
})
}
示例&效果
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<button id="btn">点击修改</button>
<script type="module" src="./index.js"></script>
</body>
</html>
js
import reactive from "./reactive/index.js"
import {
effect
} from "./effect/index.js"
const a = reactive({
a: 1,
b: 2
})
function render() {
app.innerText = a.a;
btn.innerText = a.b
}
effect(render);
btn.addEventListener("click", () => {
a.a += 1;
a.b += '2'
})

总结
一个最基本的响应式系统就完成了,当然代码肯定还有别的问题,边界问题,依赖清理,自定义功能诸如此类的,后续根据使用接着补充即可(^^ゞ