diff --git a/js/vue响应式原理/1-Reactivity.js b/js/vue响应式原理/1-Reactivity.js new file mode 100644 index 0000000..1b90472 --- /dev/null +++ b/js/vue响应式原理/1-Reactivity.js @@ -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也要发生变化,这个计算是由副作用函数负责计算的, +只要保存这个副作用,当与之关联的数据发生变化时执行副作用就行了。 +*/ \ No newline at end of file diff --git a/js/vue响应式原理/2-Proxy&Refelct.js b/js/vue响应式原理/2-Proxy&Refelct.js new file mode 100644 index 0000000..37317b6 --- /dev/null +++ b/js/vue响应式原理/2-Proxy&Refelct.js @@ -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); \ No newline at end of file diff --git a/js/vue响应式原理/3-activeEffect&ref.js b/js/vue响应式原理/3-activeEffect&ref.js new file mode 100644 index 0000000..4d055e3 --- /dev/null +++ b/js/vue响应式原理/3-activeEffect&ref.js @@ -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); diff --git a/js/vue响应式原理/4-counputed.js b/js/vue响应式原理/4-counputed.js new file mode 100644 index 0000000..0ddc8c1 --- /dev/null +++ b/js/vue响应式原理/4-counputed.js @@ -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); + diff --git a/js/vue响应式原理/5-watch.js b/js/vue响应式原理/5-watch.js new file mode 100644 index 0000000..2de1606 --- /dev/null +++ b/js/vue响应式原理/5-watch.js @@ -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 diff --git a/js/vue响应式原理/test.js b/js/vue响应式原理/test.js new file mode 100644 index 0000000..f5b9fe2 --- /dev/null +++ b/js/vue响应式原理/test.js @@ -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 diff --git a/js/vue响应式原理/vue-reactive.js b/js/vue响应式原理/vue-reactive.js new file mode 100644 index 0000000..e500b41 --- /dev/null +++ b/js/vue响应式原理/vue-reactive.js @@ -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); +}