vue3源码-一、响应式原理reactive的实现

reactive的实现

  1. 使用:

    使用reactive()函数创建一个响应式对象。

    import { reactive } from 'vue'
    
    export default {
      // `setup` 是一个专门用于组合式 API 的特殊钩子函数
      setup() {
        const state = reactive({ count: 0 })
    
        // 暴露 state 到模板
        return {
          state
        }
      }
    }
    
    <div>{{ state.count }}</div>
    
  2. 手写源码

    这里有四个方法需要实现包括reactive, shallowReactive, readonly, shallowReadonly

    这个四个方法的实现:

    export function reactive(target) {
      return createReactiveObject(target, false, mutabelHandlers);
    }
    
    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);
    }
    

    这四个方法同一调用一个创建函数,不同的是根据功能给出相应的参数。

    其中target是劫持的对象,第二个参数是是否是仅读的,第三个是处理的handler

    四个handler实现:

    import {
      extend,
      hasChanged,
      hasOwn,
      isAarray,
      isIntegerKey,
      isObject,
    } from "@vue/shared";
    import { reactive, readonly } from "./reactive";
    import { track, trigger } from "./effect";
    // 实现new Proxy(target,handler)
    // 是不是仅读的,仅读属性set时会报异常
    // 是不是深度的
    function createGetters(isReadonly = false, shallow = false) {
      return function get(target, key, receiver) {
        // proxy + reflect
        // 后续object方法会被迁移到Reflect Reflect.getPropertyeof()
        // 以前target[key] = value 方法设置值可能会失败,并不会报异常,也没有返回值标识
        // Reflect方法具备返回值
        //   reflect使用可以不使用 proxy es6
        const res = Reflect.get(target, key, receiver);
    
        if (!isReadonly) {
          //   收集依赖
          track(target, TrackOpTypes.GET, key);
        }
        if (shallow) {
          return res;
        }
        if (isObject(res)) {
          // vue2是一上来就递归,vue3是当取值的时候会进行代理,vue3的代理模式是懒代理
          return isReadonly ? readonly(res) : reactive(res);
        }
        return res;
      };
    } // 拦截获取功能
    function createSetter(shallow = false) {
      return function set(target, key, value, receiver) {
        const oldValue = target[key];
        let hadKey =
          isAarray(target) && isIntegerKey(key)
            ? Number(key) < target.length
            : hasOwn(target, key);
        const res = Reflect.set(target, key, value, receiver);
        // s数据更新时视图更新, 通知对应属性的effect重新执行
    
        if (!hadKey) {
          // 新增
          trigger(target, TriggerOrTypes.ADD, key, value);
        } else if (hasChanged(oldValue, value)) {
          // 修改
          trigger(target, TriggerOrTypes.SET, key, value, oldValue);
        }
    
        return res;
      };
    } // 拦截设置功能
    
    const get = createGetters();
    const shallowGet = createGetters(false, true);
    const readonlyGet = createGetters(true);
    const shallowReadonlyGet = createGetters(true, true);
    
    const set = createSetter();
    const shallowSet = createSetter(true);
    // reactive的
    export const mutabelHandlers = {
      get, // get只是获取,无特殊参数
      set: set, // set需要,一样的
    };
    export const shallowReactiveHandlers = {
      get: shallowGet, // 浅的的需要给浅的参数
      set: shallowSet, // 需要给一个浅的的参数
    };
    
    let readonlyObj = {
      // 只读如果取值,就报错
      set: (target, key) => {
        console.warn(`set on key ${key} failed`);
      },
    };
    
    export const readonlyHandlers = extend(
      // 只读get访问只读的,set报错
      {
        get: readonlyGet,
      },
      readonlyObj
    );
    export const shallowReadonlyHandlers = extend(
      //同上
      {
        get: shallowReadonlyGet,
      },
      readonlyGet
    );
    

    目的是重写相应的getset方法。其实使用的都是工厂函数。

    然后就要创建响应式数据,就要根据不同的参数调用createReactiveObject方法

    const reactiveMap = new WeakMap(); // 会自动垃圾回收,存储的key只能是对象
    const readonlyMap = new WeakMap(); //
    export function createReactiveObject(target, isReadonly, baseHandlers) {
      // 如果目标不是对象,没法拦截了,reactive这个api只能拦截对象
      if (!isObject(target)) {
        return target;
      }
      // 如果某个对象被代理过了,就不要再次代理
      const proxyMap = isReadonly ? readonlyMap : reactiveMap;
      const existProxy = proxyMap.get(target);
      if (existProxy) {
        return existProxy;
      }
      const proxy = new Proxy(target, baseHandlers);
      proxyMap.set(target, proxy);
      return proxy;
    }
    

    使用的也是工厂函数实现代理的,数据代理的。

    因此这里的数据响应式处理使用的是Proxy,其中这里还对对象是否代理进行了处理,使用的是WeakMap。这样就可以做到了响应式原理。

posted @ 2023-04-29 21:01  楸枰~  阅读(40)  评论(0)    收藏  举报