5-实现reactiveApi

55 阅读2分钟

实现reactiveApi

  • 实现proxy里面的数据劫持逻辑
// => baseHandler.js

// 代理操作对象逻辑处理: 实现proxy里面的数据劫持逻辑
// 1. 只读的,set报异常
// 2. 是不是深度的
// 拦截获取
function createGet(isReadonly = false, shallow = false) {
  return function get(target, key, receiver) {
    // proxy * reflect
    const res = Reflect.get(target, key, receiver);
    if(!isReadonly) {
      // Todo 收集依赖,等会数据变化后更新对应的视图
    }

    // 浅的
    if(shallow) {
      return res;
    }

    if(isObject(res)) {// 是对象是只读调用readonly 的方式 不是再去递归代理
      return isReadonly ? readonly(res) : reactive(res);
    }
    return res;
  }
}
// 拦截设置
function createSetter(shallow = false) {
  return function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);

    return result;
  }
}


const get = createGet();
const shallowGet = createGet(false, true);
const readonlyGet = createGet(true);
const shallowReadonlyGet = createGet(true, true);


const set = createSetter(); 
const shallowSet = createSetter(); 

export const mutableHandlers = {//reactive
  get,
  set
};
export const shallowReactiveHandlers = {//shallowReactive
  get: shallowGet,
  set: shallowSet
};

let readonlyObj = {
  set(target, key) {
    console.warn(`set ${key} falied`)
  }
}
export const readonlyHandlers = Object.assign({
  get: readonlyGet,
}, readonlyObj);

export const shallowReadonlyHandlers = Object.assign({
  get: shallowReadonlyGet
}, readonlyObj);
  • reactive.js
// => reactive.js

import { isObject } from "@vue/shared";
import {
  mutableHandlers,
  shallowReactiveHandlers,
  shallowReadonlyHandlers,
  readonlyHandlers
} from './baseHandler'

export function reactive(target) {
  return createReactiveObject(target, false, mutableHandlers);
}

export function shallowReactive(target) {
  return createReactiveObject(target, false, shallowReactiveHandlers);
}

export function readonly(target) {
  return createReactiveObject(target, true, readonlyHandlers);
}

export function shallowReadonly(target) {
  return createReactiveObject(target, true, shallowReadonlyHandlers);
}

// 是不是只读,是不是深度 -> 柯里化 new Proxy 对数据做劫持
const reactiveMap = new WeakMap();//桶 会自动垃圾回收,存储的key只能是对象
const readonlyMap = new WeakMap();
/**
 * 创建reactive对象函数
 * @param target 目标对象
 * @param isReadonly 是不是只读的
 * @param baseHandler 不同的api有不同劫持方式
 */
export function createReactiveObject(target, isReadonly, baseHandler) {
  // 目标是不是对象,proxy只拦截对象
  if(isObject(target)) {
    return target;
  }

  // proxy代理对象 -> 如果某个对象被代理了,就不要再次代理了(映射表)
  const proxyMap = isReadonly ? readonlyMap : reactiveMap;
  // 看看缓存中是否有代理过的对象了
  const exisitProxy = proxyMap.get(target)
  if(exisitProxy) {//如果存在代理的对象,直接返回即可
    return exisitProxy;
  }
  const proxy = new Proxy(target, baseHandler);
  // 将要代理的对象,和代理结果缓存起来
  proxyMap.set(target, proxy);

  return proxy;
}
// => shared.js
// 是否是一个对象
export const isObject = (value) => typeof value === 'object' && value !== null; 

总结

  1. proxy进行数据的代理
  2. proxy只代理对象,get,set抽离的方式,让每个api对应的逻辑更加清晰
  3. 代理的逻辑要考虑它们是不是 只读的 或者 是不是浅层代理的
  4. 代理的时候,代理过的对象,不用继续代理,我们就需要用 一个桶的概念去进行管理,WeakMap是最适合的,key是对象,会自动垃圾回收
  5. 通过 是否是只读的 指定对应的桶(类似于垃圾分类)