reactive
用法
import { reactive } from 'vue';
<script setup>
const obj = reactive({
name: 'lhm',
});
console.log(obj); // proxy
</script>
思路分析
-
reactive的入参是一个
object,并返回一个proxy对象 -
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
-
get 该方法用于拦截对象的读取属性操作,比如我们要读取某个对象的属性,就可以使用该方法进行拦截。
- target 被代理的对象名
- property 获取的属性名
- receiver proxy或者proxy继承的对象
-
set 当我们给对象设置属性值时,将会触发该拦截。
- target 被代理的对象名
- property 获取的属性名
- value 新的属性值
- receiver proxy或者proxy继承的对象
-
实现
- 目录结构如下
reactivity
├── copy
├── dist
├── package.json
├── src
│ ├── index.ts
│ └── reactive.ts
└── types
- 第一步先实现reactive,直接通过proxy进行代理
// reactive.ts
export function reactive(target) {
// 不对非对象类型的数据进行处理
if (!isObject(target)) return;
const proxy = new Proxy(target, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
}
});
return proxy;
}
- 在此之前还会有以下三个问题
- 重复代理
import { reactive } from 'vue';
<script setup>
const obj = {
name: 'lhm',
};
const o1 = reactive(obj);
const o2 = reactive(obj);
- 多次代理
import { reactive } from 'vue';
<script setup>
const o1 = reactive({
name: 'lhm',
});
const o2 = reactive(o1);
- 深层对象没有被代理
import { reactive } from 'vue';
<script setup>
const o1 = reactive({
name: 'lhm',
a: {
age: 3
}
});
const o2 = reactive(o1);
- 为了防止上述两种情况的发上,需要进行优化
- 在vue中利用
WeakMap对象作为key值的特性,创建一个存放过已经代理的对象 - 自定义
__v_isReactive值,在二次代理时,通过get方法进行拦截判断 - 对象为深层对象时,vue2相比vue3做了一些优化,vue3中仅当对象该深层对象被访问时才会进行代理,性能优化。
- 在vue中利用
// reactive.ts
const reactiveMap = new WeakMap();
const const enumn ReactiveFlags {
IS_REACTIVE: '__v_isReactive',
}
export function reactive(target) {
// 不对非对象类型的数据进行处理
if (!isObject(target)) return;
const exisitsProxy = reactiveMap.get(target); // 获取当前是否有代理过此对象
if (exisitsProxy) return exisitsProxy; // 如果代理过,直接返回该对象
if (target[ReactiveFlags.IS_REACTIVE]) return target; // 如果已经是proxy对象,直接返回
const proxy = new Proxy(target, {
get(target, key, receiver) {
if (key === ReactiveFlags.IS_REACTIVE) return true; // 被访问的key为ReactiveFlags.IS_REACTIVE,则返回true
const r = Reflect.get(target, key, receiver);
if (r !== null && typeof r === 'object') {
// 如果还是一个对象,则在此进行代理
reactive(r);
}
return r;
},
set(target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
}
});
reactiveMap.set(target, proxy); // 首次代理,缓存该对象
return proxy;
}