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