Vue3-reactive

96 阅读2分钟

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;
}
  • 在此之前还会有以下三个问题
  1. 重复代理
import { reactive } from 'vue';
<script setup>
const obj = {
  name: 'lhm',
};
const o1 = reactive(obj);
const o2 = reactive(obj);
  1. 多次代理
import { reactive } from 'vue';
<script setup>
const o1 = reactive({
  name: 'lhm',
});
const o2 = reactive(o1);
  1. 深层对象没有被代理
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中仅当对象该深层对象被访问时才会进行代理,性能优化。
// 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;
}