您好,欢迎访问一九零五行业门户网

Vue3的响应式原理是什么

proxyproxy这个核心api被vue3的响应式原理所依赖,利用proxy可以拦截一些对象操作。
const obj = { a: 1 };const p = new proxy(obj, { get(target, property, receiver) { console.log("get"); return reflect.get(target, property, receiver); }, set(target, property, value, receiver) { console.log("set"); return reflect.set(target, property, receiver); }, has(target, prop) { console.log("has"); return reflect.has(target, prop); }, deleteproperty(target, prop) { console.log("deleteproperty"); return reflect.deleteproperty(target, prop); },});p.a; // 输出 --> getp.a = 2; // 输出 --> set"a" in p; // 输出 --> hasdelete p.a; // 输出 --> deleteproperty
如上例子,我们用 proxy 代理了 obj 对象的属性访问、属性赋值、in 操作符、delete 的操作,并进行 console.log 输出。
reflectreflect 是与 proxy 搭配使用的一个 api,当我们劫持了某些操作时,如果需要再把这些操作反射回去,那么就需要 reflect 这个 api。
由于我们拦截了对象的操作,所以这些操作该有的功能都丧失了,例如,访问属性 p.a 应该得到 a 属性的值,但此时却不会有任何结果,如果我们还想拥有拦截之前的功能,那我们就需要用 reflect 反射回去。
const obj = { a: 1 };const p = new proxy(obj, { get(target, property, receiver) { console.log("get"); return reflect.get(target, property, receiver); }, set(target, property, value, receiver) { console.log("set"); return reflect.set(target, property, receiver); }, has(target, prop) { console.log("has"); return reflect.has(target, prop); }, deleteproperty(target, prop) { console.log("deleteproperty"); return reflect.deleteproperty(target, prop); },});
举个例子以下全文我们都会通过这个例子来讲述 vue3 响应式的原理。
<div id="app"></div><script> // 创建一个响应式对象 const state = reactive({ counter: 1 }); // 立即运行一个函数,当响应式对象的属性发生改变时重新执行。 effect(() => { document.queryselector("#app").innerhtml = state.counter; }); // 2s 后视图更新 settimeout(() => { state.counter += 1; }, 2000);</script>
我们用 reactive 创建了一个响应式对象 state,并调用了 effect 方法,该方法接受一个副作用函数,effect 的执行会立即调用副作用函数,并将 state.counter 赋值给 #app.innerhtml;两秒后,state.counter += 1,此时,effect 的副作用函数会重新执行,页面也会变成 2.
内部的执行过程大概如下图所示:
调用 reactive() 返回一个 proxy 代理对象,并劫持对象的 get 与 set 操作
调用 effect() 方法时,会访问属性 state.counter,此时会触发 proxy 的 get 操作。
get 方法会调用 track() 进行依赖收集;建立一个对象(state)、属性(counter)、effect 副作用函数的依赖关系;
set 方法会调用 trigger() 进行依赖更新;通过对象(state)与属性(coutner)找到对应的 effect 副作用函数,然后重新执行。
reactivereactive 会返回如下一个 proxy 对象
const reactive = (target) => { return new proxy(target, { get(target, key, receiver) { const res = reflect.get(target, key, receiver); track(target, key); // 收集依赖 if (isobject(res)) { // 如果当前获取的属性值是一个对象,则继续将为此对象创建 proxy 代理 return reactive(res); } return res; }, set(target, key, value, receiver) { reflect.set(target, key, value, receiver); trigger(target, key); // 依赖更新 }, });};
effectlet activeeffect;function effect(fn) { const _effect = function reactiveeffect() { activeeffect = _effect; fn(); }; _effect();}
定义一个全局的 activeeffect 变量,该变量指向当前正在执行的 effect 副作用函数,并将其一直保持更新。effect 为 fn 创建一个内部的副作用函数,然后立即执行,此时会触发对象的 get 操作,调用 track() 方法。
effect(() => { // effect 的立即执行会访问 state.counter,触发了对象的 get 操作。 document.queryselector("#app").innerhtml = state.counter;});
tracktrack 会建立一个 对象(state) => 属性(counter) => effect 的一个依赖关系
const targetmap = new weakmap();function track(target, key) { if (!activeeffect) { return; } 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())); } if (!dep.has(activeeffect)) { dep.add(activeeffect); }}
执行完成成后我们得到一个如下的数据结构:
[ // map 集合 { key: {counter: 1} // state 对象, value: [ // map 集合 { key: "counter", value: [ // set function reactiveeffect() {} // effect 副作用函数 ], } ], },];
注意:当我们调用 effect 时,会将当前的副作用函数赋值给全局的 activeeffect,所以此时我们可以正确关联其依赖。
trigger当我们给 state.counter 赋值的时候就会触发代理对象的 set 操作,从而调用 trigger 方法
settimeout(() => { // 给 counter 属性赋值会触发 set 操作 state.counter += 1;}, 2000);
function trigger(target, key) { const depsmap = targetmap.get(target); if (!depsmap) return; const effects = depsmap.get(key); effects && effects.foreach((effect) => effect());}
以上就是vue3的响应式原理是什么的详细内容。
其它类似信息

推荐信息