proxy代理机制和工作原理,reactive是怎么通过proxy实现响应式的
1. 什么是 Proxy
Proxy 是 JavaScript 中一个用于创建代理对象的构造函数,允许你定义基本操作(如属性查找、赋值、枚举、函数调用等)的自定义行为。通过 Proxy,你可以对一个对象进行拦截,并在该对象的操作上添加自定义逻辑。在 Vue 3 中,Proxy 被广泛用于实现响应式系统。
2. 代理机制
代理机制的核心在于以下几个方面:
目标对象:这是 Proxy 要代理的实际对象(即目标)。
处理器对象:这是一个对象,定义了拦截器函数,用于拦截对目标对象的操作。
当对 Proxy 代理的对象进行操作时,实际的操作会被发送到处理器对象中定义的拦截器函数,这些函数会处理相应的操作。
基本语法
const proxy = new Proxy(target, handler);
//target:要代理的对象。
//handler:定义拦截行为的处理器对象。
3.工作原理
Proxy 的工作原理主要依赖于内部方法和拦截机制。以下是一些重要的方面:
内部方法
Proxy 通过内部方法(如 [[Get]]、[[Set]] 等)来控制对象的行为。当你尝试对代理对象进行操作时,这些操作会被转换为对应的内部方法调用。
以下是一些常见的内部方法及其对应的拦截器函数:
[[Get]]:当获取对象的属性时调用,映射到 get 拦截器。
[[Set]]:当设置对象的属性时调用,映射到 set 拦截器。
[[Delete]]:当删除对象的属性时调用,映射到 deleteProperty 拦截器。
[[Has]]:当使用 in 操作符检查属性是否存在时调用,映射到 has 拦截器。
[[Apply]]:当函数被调用时调用,映射到 apply 拦截器。
[[Construct]]:当使用 new 操作符创建对象时调用,映射到 construct 拦截器。
3.1 拦截行为
通过实现处理器对象中的拦截器函数,你可以控制对目标对象的所有操作。例如:
const target = {
name: 'John',
age: 30
};
const handler = {
get(target, prop, receiver) {
console.log(`Getting ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`Setting ${prop} to ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Getting name -> "John"
proxy.age = 31; // Setting age to 31
console.log(proxy.age); // Getting age -> 31
proxy对于响应式对象的基本实现:reactive
reactive 对 Proxy 进行封装。它是 Vue 3 中实现响应式数据的一种方式,简化了用户的使用体验
reactive 函数用于将普通对象转换为响应式对象。通过 Proxy,reactive 可以拦截对对象的读取和写入操作,以实现依赖收集和自动更新的功能。
reactive 的实现原理
reactive 的内部实现主要依赖于 Proxy,它将目标对象包装成一个代理对象,并在 get 和 set 拦截器中定义逻辑。具体实现通常包含以下几个步骤:
创建代理对象:通过 new Proxy 创建一个代理对象,接收目标对象和处理器(handler)作为参数。
实现拦截器:
get 拦截器:用于拦截对对象属性的读取,可以在这里进行依赖收集。
set 拦截器:用于拦截对对象属性的写入,可以在这里进行更新通知。
简化版 reactive 实现示例,展示了如何使用 Proxy 创建响应式对象
function reactive(target) {
// 存储依赖的简单实现
const dependencies = new Map();
// 创建代理
return new Proxy(target, {
get(target, prop, receiver) {
// 收集依赖
if (!dependencies.has(prop)) {
dependencies.set(prop, new Set());
}
const deps = dependencies.get(prop);
// 假设有一个全局的 effect 函数,用于注册依赖
if (currentEffect) {
deps.add(currentEffect);
}
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
// 更新目标对象
const oldValue = target[prop];
if (oldValue !== value) {
Reflect.set(target, prop, value, receiver);
// 通知依赖
const deps = dependencies.get(prop);
if (deps) {
deps.forEach(effect => effect());
}
}
return true;
}
});
}
// 用于注册当前的 effect
let currentEffect = null;
function effect(fn) {
currentEffect = fn;
fn(); // 立即执行一次,以收集依赖
currentEffect = null; // 执行完成后清空
}
// 使用 reactive
const state = reactive({ count: 0 });
effect(() => {
console.log(`Count is: ${state.count}`);
});
// 修改 state.count 会自动触发 effect
state.count++; // Count is: 1
state.count++; // Count is: 2
当然实际上不会这么使用,而是采用下面的方式
例子:
import { reactive, effect } from 'vue';
// 创建响应式状态
const state = reactive({
count: 0,
message: 'Hello Vue!'
});
// 创建响应式副作用
effect(() => {
console.log(`Count is: ${state.count}`);
console.log(`Message is: ${state.message}`);
});
// 修改数据,触发更新
state.count++; // Count is: 1
state.message = 'Hello World!'; // Message is: Hello World!
state.count++; // Count is: 2