持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
流程
1.创建一个响应式对象
2 effect执行时,把当前执行的effect赋值给全局变量,执行时由于访问到proxy属性,可以在proxy里的get方法里面收集依赖,依赖格式:weakmap { 响应式对象 :{(map、属性} :{ set、effect } }
3 数据变化时,触发proxy的set,赋值后执行trigger,执行数据对应的effect依赖。
依赖收集样式
配合下面的简单版例子
vue2的缺点
1 使用 defineProperty 进行数据劫持,需要重改 数据的 getter和 setter。
2 新增和删除属性无法监控变化,需要 用 $set/delete
3 数组需要单独处理,map、set不能处理
vue3优点
采用WeakMap,当依赖清除时,能及时去掉不必要的内存占用
demo
3s后 state.age变化,通过state.age渲染的元素也跟着变化
<div id="app"></div>
<script src="../dist/reactivity.global.js"></script>
<script>
//打包的全局的名字 VueReactivity
// effect:副作用,开始会执行一次,依赖变动会执行
const { reactive, effect } = VueReactivity;
const state = reactive({ name: 'zf', age: 13 ,address:'xxx'});
// 1.effect执行的过程是默认执行一次,使用的数据更新了 会再次执行
let runner = effect(() => {
app.innerHTML = state.name + '今年' + state.age + '岁了' + state.age;
})
setTimeout(() => {
state.age = 20;
}, 3000)
</script>
简单版
可以自行调试,由于我用的是es6引入,所以需要开启静态服务器,不然浏览器会报错的。
我用的是http-server
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script type="module">
import { effect, reactivity } from "./index.js";
const obj = reactivity({ age: 1 });
effect(() => {
document.getElementById("app").innerHTML = `我${obj.age}岁`;
console.log(`我${obj.age}岁`);
});
setTimeout(() => {
obj.age = 2333;
}, 1000);
</script>
</body>
</html>
index.js
// 用于存储当前执行的effect
let targetEffect
// 用于存储依赖映射关系
let effectMap = new WeakMap()
// proxy
export const reactivity = function (target) {
return new Proxy(target, {
get(target, key, receiver) {
// track 收集依赖
track(target, key)
return target[key]
// return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
// trigger 这里因为我搞错顺序,导致一直调试不对哎,要先赋值再去trigger
target[key] = value
// return Reflect.set(target, key, value, receiver)
// 数据更新,执行这个数据对应的effect依赖们
trigger(target, key)
}
})
}
// effect 执行时,存到全局变量,准备依赖收集
export const effect = function (fn) {
targetEffect = fn
fn()
}
// weekmap:map:set
// 对象 : 属性 : effect
const track = function (target, key) {
let obj = effectMap.get(target)
if (!obj) {
effectMap.set(target, (obj = new Map()))
}
let att = obj.get(key)
if (!att) {
obj.set(key, (att = new Set()))
}
if (!att.has(targetEffect)) {
att.add(targetEffect)
}
}
const trigger = function (target, key) {
effectMap.get(target).get(key).forEach(i => i())
}