Vue3的响应式原理是如何通过Proxy实现长尾链式依赖追踪和响应式数据的?

2026-04-01 12:571阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1357个文字,预计阅读时间需要6分钟。

Vue3的响应式原理是如何通过Proxy实现长尾链式依赖追踪和响应式数据的?

目录 + Proxy + Reflect + 以一个例子展示 reactive + effect + track + trigger + Proxy + Vue3 的响应式原理依赖于 Proxy + 这个核心 API,通过 Proxy 可以持久化对对象的某些操作。例如:

const obj={ a: 1 };const p=new Proxy(obj, { // ...

目录
  • Proxy
  • Reflect
  • 举个例子
  • reactive
  • effect
  • track
  • trigger

Proxy

Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 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; // 输出 --> get p.a = 2; // 输出 --> set "a" in p; // 输出 --> has delete p.a; // 输出 --> deleteProperty

如上例子,我们用 Proxy 代理了 Obj 对象的属性访问、属性赋值、in 操作符、delete 的操作,并进行 console.log 输出。

Reflect

Reflect 是与 Proxy 搭配使用的一个 API,当我们劫持了某些操作时,如果需要再把这些操作反射回去,那么就需要 Reflect 这个 API。

Vue3的响应式原理是如何通过Proxy实现长尾链式依赖追踪和响应式数据的?

由于我们拦截了对象的操作,所以这些操作该有的功能都丧失了,例如,访问属性 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 副作用函数,然后重新执行。

reactive

reactive 会返回如下一个 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); // 依赖更新     },   }); };

effect

let 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; });

track

track 会建立一个 对象(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的响应式原理的详细内容,更多关于Vue3响应式原理的资料请关注易盾网络其它相关文章!

本文共计1357个文字,预计阅读时间需要6分钟。

Vue3的响应式原理是如何通过Proxy实现长尾链式依赖追踪和响应式数据的?

目录 + Proxy + Reflect + 以一个例子展示 reactive + effect + track + trigger + Proxy + Vue3 的响应式原理依赖于 Proxy + 这个核心 API,通过 Proxy 可以持久化对对象的某些操作。例如:

const obj={ a: 1 };const p=new Proxy(obj, { // ...

目录
  • Proxy
  • Reflect
  • 举个例子
  • reactive
  • effect
  • track
  • trigger

Proxy

Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 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; // 输出 --> get p.a = 2; // 输出 --> set "a" in p; // 输出 --> has delete p.a; // 输出 --> deleteProperty

如上例子,我们用 Proxy 代理了 Obj 对象的属性访问、属性赋值、in 操作符、delete 的操作,并进行 console.log 输出。

Reflect

Reflect 是与 Proxy 搭配使用的一个 API,当我们劫持了某些操作时,如果需要再把这些操作反射回去,那么就需要 Reflect 这个 API。

Vue3的响应式原理是如何通过Proxy实现长尾链式依赖追踪和响应式数据的?

由于我们拦截了对象的操作,所以这些操作该有的功能都丧失了,例如,访问属性 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 副作用函数,然后重新执行。

reactive

reactive 会返回如下一个 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); // 依赖更新     },   }); };

effect

let 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; });

track

track 会建立一个 对象(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的响应式原理的详细内容,更多关于Vue3响应式原理的资料请关注易盾网络其它相关文章!