feat: vue3响应式原理
This commit is contained in:
parent
61d989f779
commit
f3df94b51a
41
js/vue响应式原理/1-Reactivity.js
Normal file
41
js/vue响应式原理/1-Reactivity.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const targetMap = new WeakMap() // 用原始对象作为key,依赖映射作为value
|
||||||
|
|
||||||
|
function track(target, key) {
|
||||||
|
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(effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
function trigger(target, key) {
|
||||||
|
const depsMap = targetMap.get(target)
|
||||||
|
if(!depsMap) return
|
||||||
|
const deps = depsMap.get(key)
|
||||||
|
deps.forEach(effect => {
|
||||||
|
effect()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 测试
|
||||||
|
let product = {price: 5, quantity: 2}
|
||||||
|
let total = 0
|
||||||
|
let effect = ()=>{ // 副作用函数
|
||||||
|
total = product.price * product.quantity
|
||||||
|
}
|
||||||
|
track(product, 'quantity') // 跟踪这个key,并记录副作用函数
|
||||||
|
effect() // 首次执行副作用函数
|
||||||
|
|
||||||
|
console.log(total);
|
||||||
|
product.quantity = 3
|
||||||
|
trigger(product, 'quantity') // 重新触发所有副作用
|
||||||
|
console.log(total);
|
||||||
|
|
||||||
|
/* 当数量发生变化时,对应的total也要发生变化,这个计算是由副作用函数负责计算的,
|
||||||
|
只要保存这个副作用,当与之关联的数据发生变化时执行副作用就行了。
|
||||||
|
*/
|
59
js/vue响应式原理/2-Proxy&Refelct.js
Normal file
59
js/vue响应式原理/2-Proxy&Refelct.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const targetMap = new WeakMap() // 用原始对象作为key,依赖映射作为value
|
||||||
|
|
||||||
|
function track(target, key) {
|
||||||
|
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(effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
function trigger(target, key) {
|
||||||
|
const depsMap = targetMap.get(target)
|
||||||
|
if(!depsMap) return
|
||||||
|
const deps = depsMap.get(key)
|
||||||
|
deps.forEach(effect => {
|
||||||
|
effect()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
const product = new reactive({price: 5, quantity: 2})
|
||||||
|
|
||||||
|
let total = 0
|
||||||
|
let effect = ()=>{
|
||||||
|
total = product.price * product.quantity // price和quantity都被跟踪了
|
||||||
|
}
|
||||||
|
|
||||||
|
effect()
|
||||||
|
|
||||||
|
console.log(total);
|
||||||
|
product.quantity = 3
|
||||||
|
console.log(total);
|
||||||
|
product.price = 100
|
||||||
|
console.log(total);
|
87
js/vue响应式原理/3-activeEffect&ref.js
Normal file
87
js/vue响应式原理/3-activeEffect&ref.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
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()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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,为基本数据类型赋予响应式功能
|
||||||
|
|
||||||
|
function ref(raw) {
|
||||||
|
const r = {
|
||||||
|
get value(){
|
||||||
|
track(r, 'value')
|
||||||
|
return raw
|
||||||
|
},
|
||||||
|
|
||||||
|
set value(newVal){
|
||||||
|
raw = newVal
|
||||||
|
trigger(r, 'value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function effect(effect){
|
||||||
|
activeEffect = effect
|
||||||
|
activeEffect()
|
||||||
|
activeEffect = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const product = new reactive({price: 5, quantity: 2})
|
||||||
|
|
||||||
|
let tax = ref(5) // 固定的商品税
|
||||||
|
let total = 0
|
||||||
|
|
||||||
|
effect(()=>{
|
||||||
|
total = tax.value + product.price * product.quantity // price和quantity都被跟踪了
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(total);
|
||||||
|
product.quantity = 4
|
||||||
|
product.price = 5
|
||||||
|
console.log(total);
|
||||||
|
tax.value = 10 // 修改固定的商品税
|
||||||
|
console.log(total);
|
90
js/vue响应式原理/4-counputed.js
Normal file
90
js/vue响应式原理/4-counputed.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
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()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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,为基本数据类型赋予响应式功能
|
||||||
|
|
||||||
|
function ref(raw) {
|
||||||
|
const r = {
|
||||||
|
get value(){
|
||||||
|
track(r, 'value')
|
||||||
|
return raw
|
||||||
|
},
|
||||||
|
|
||||||
|
set value(newVal){
|
||||||
|
raw = newVal
|
||||||
|
trigger(r, 'value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function effect(effect){
|
||||||
|
activeEffect = effect
|
||||||
|
activeEffect()
|
||||||
|
activeEffect = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function computed(getter){
|
||||||
|
let result = ref()
|
||||||
|
effect(()=>(result.value = getter()))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const product = new reactive({price: 5, quantity: 2})
|
||||||
|
|
||||||
|
const total = computed(()=>{
|
||||||
|
return product.price * product.quantity
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(total.value);
|
||||||
|
product.price = 3
|
||||||
|
console.log(total.value);
|
||||||
|
product.quantity = 10
|
||||||
|
console.log(total.value);
|
||||||
|
|
117
js/vue响应式原理/5-watch.js
Normal file
117
js/vue响应式原理/5-watch.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
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()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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,为基本数据类型赋予响应式功能
|
||||||
|
|
||||||
|
function ref(raw) {
|
||||||
|
const r = {
|
||||||
|
get value(){
|
||||||
|
track(r, 'value')
|
||||||
|
return raw
|
||||||
|
},
|
||||||
|
|
||||||
|
set value(newVal){
|
||||||
|
raw = newVal
|
||||||
|
trigger(r, 'value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function effect(effect){
|
||||||
|
activeEffect = effect
|
||||||
|
activeEffect()
|
||||||
|
activeEffect = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function computed(getter){
|
||||||
|
let result = ref()
|
||||||
|
effect(()=>(result.value = getter()))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用例
|
||||||
|
const product = reactive({ price: 5, quantity: 2 });
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => product.price,
|
||||||
|
(newPrice, oldPrice) => {
|
||||||
|
console.log(`Price changed from ${oldPrice} to ${newPrice}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const total = ref(1)
|
||||||
|
|
||||||
|
watch(total, (newVal, oldVal)=>{
|
||||||
|
console.log(`Total changed from ${oldVal} to ${newVal}`);
|
||||||
|
})
|
||||||
|
|
||||||
|
product.price = 10; // 应触发 watch 回调并打印 "Price changed from 5 to 10"
|
||||||
|
product.price = 100
|
||||||
|
|
||||||
|
total.value = 2
|
||||||
|
total.value = 3
|
48
js/vue响应式原理/test.js
Normal file
48
js/vue响应式原理/test.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import {reactive, ref, computed, watch, effect} from './vue-reactive.js'
|
||||||
|
|
||||||
|
// 测试
|
||||||
|
const product = reactive({ price: 5, quantity: 2 });
|
||||||
|
const tax = ref(5);
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
console.log(`现在的总价是:${product.price * product.quantity}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 修改价格
|
||||||
|
product.price = 10;
|
||||||
|
|
||||||
|
// 修改数量
|
||||||
|
product.quantity = 3;
|
||||||
|
|
||||||
|
// 计算含税价格
|
||||||
|
const total = computed(() => {
|
||||||
|
return product.price * product.quantity + tax.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
console.log(`含税价格为:${total.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 修改税
|
||||||
|
tax.value = 10;
|
||||||
|
|
||||||
|
watch(total, (newVal, oldVal) => {
|
||||||
|
console.log(`老总价:${oldVal}---->新总价:${newVal}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => product.price,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
console.log(`老价格:${oldVal}---->新价格:${newVal}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => product.quantity,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
console.log(`老数量:${oldVal}---->新数量:${newVal}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
product.price = 20
|
||||||
|
product.quantity = 10
|
96
js/vue响应式原理/vue-reactive.js
Normal file
96
js/vue响应式原理/vue-reactive.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user