vue3的响应式原理设计流程图
相关文件
index.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>
<script type="module">
import {reactive} from './reactive.js'
import {effect} from './effect.js'
const user = reactive({
name:'李白',
age:18,
foo:{
bar:'刘晓'
}
})
// 开始就执行一次,展示初始数据
effect(()=>{
document.querySelector('#app').innerText = `${user.name}-${user.age}-${user.foo.bar}`
})
// 修改数据
setTimeout(() => {
user.name = '杜甫'
setTimeout(() => {
user.foo.bar = '陈华'
}, 2000);
}, 2000);
</script>
</body>
</html>
effect.js
let activeEffect;
// effect为副作用函数,更新依赖的作用
export const effect = (fn) => {
const _effect = () => {
activeEffect = _effect;
fn();
};
_effect();
};
const targetMap = new WeakMap();
// track 收集依赖
export const track = (target, key) => {
// 获取Map对象
let depsMap = targetMap.get(target);
if (!depsMap) { // 如果没有,初始化一个Map对象,并添加进去
depsMap = new Map();
targetMap.set(target, depsMap);
}
// 获取一个Set对象(一个伪数组)
let deps = depsMap.get(key);
if (!deps) {// 如果没有,初始化一个Set对象,并添加进去
deps = new Set();
depsMap.set(key, deps);
}
// console.log('deps:', deps);
// 给相关的依赖添加副作用函数
deps.add(activeEffect);
};
// trigger 更新依赖
export const trigger = (target, key) => {
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);
}
// console.log('deps:', deps);
// 执行相关依赖内部的effect副作用函数,去更新依赖
deps.forEach((effect) => effect());
};
reactive.js
import { track, trigger } from './effect.js';
const isObject = (target) => {
return target !== null && typeof target === 'object';
};
export const reactive = (target) => {
// Proxy数据劫持
return new Proxy(target, {
// 访问了target对象源的属性就会触发get方法
get(target, key, receiver) {
let res = Reflect.get(target, key, receiver);
track(target, key); // 收集依赖
if (isObject(res)) { // 深层次数据监测 例如:{a:1,b:3,c:{k:3}}
return reactive(res);
}
return res;
},
// 修改了target对象源的属性值就会触发set方法
set(target, key, value, receiver) {
let res = Reflect.set(target, key, value, receiver);
trigger(target, key); // 更新依赖
return res;
}
});
};
测试:
- 安装下Live Server
- 在index.html里面右键选择Live Server打开