js-review/js/vue响应式原理/vue-reactive.js
2024-06-01 04:29:31 +08:00

97 lines
2.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const targetMap = new WeakMap(); // 用原始对象作为key依赖映射作为value
let activeEffect = null; // 保存当前的需要订阅通知的effect
function track(target, key) {
if (activeEffect) {
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect); // 将efect添加进通知set
}
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const deps = depsMap.get(key);
deps.forEach((effect) => {
effect();
});
}
export function reactive(target) {
const hander = {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
track(target, key);
return result;
},
set(target, key, value, receiver) {
let oldValue = target[key];
let result = Reflect.set(target, key, value, receiver);
if (oldValue !== value) {
trigger(target, key);
}
return result;
},
};
return new Proxy(target, hander);
}
// 创建ref为基本数据类型赋予响应式功能
export function ref(raw) {
const r = {
get value() {
track(r, "value");
return raw;
},
set value(newVal) {
raw = newVal;
trigger(r, "value");
},
};
return r;
}
// 绑定副作用
export function effect(effect) {
activeEffect = effect;
activeEffect();
activeEffect = null;
}
// 计算属性
export function computed(getter) {
let result = ref();
effect(() => (result.value = getter()));
return result;
}
// watch
export function watch(source, callback) {
let getter;
if (typeof source === "function") {
getter = source;
} else {
getter = () => source.value;
}
let oldValue, newValue;
const effectFn = () => {
newValue = getter();
if (oldValue !== undefined) callback(newValue, oldValue); // watch第一次不需要执行
oldValue = newValue;
};
effect(effectFn);
}