[Vue源码系列-2]vue3响应式API实现原理

457 阅读3分钟

1. 响应式API

vue3中最核心的就是基于Proxy实现的响应式API(用于代理对象类型)

  • reactive:vue3中能够将对象变成响应式的API,不管对象有多少层
  • shallowReactive:vue3中能够将对象变成响应式的API,只代理最外层对象
  • readonly:将对象属性变为只读,且不管对象有多少层
  • shallowReadonly:将对象属性变为只读,但是只代理最外层

2. reactivity响应式原理

reactivity文件夹下的src中就是实现响应式逻辑的源码

  • index.ts:入口文件
  • reactive.ts:响应式API
  • baseHandler.tsgettersetter的处理逻辑
  • operators.ts:定义公共标识
  • effect.ts:依赖收集,副作用函数(依赖)effect、属性收集依赖track、通知依赖执行trigger
  • ref.tsreftoRef实现
  • computed.ts:计算属性

3. 实现响应式API

3.1 shared模块

shared模块是vue3中用于存储共享方法的包模块,相当于utils,shared中的方法都在srcindex.ts

// 这里我们先实现一些常用的
export const isObject = val => typeof val === 'object' && val !== null;
export const extend = Object.assign;
export const isArray = Array.isArray;
export const isFunction = val => typeof val === 'function';
export const isNumber = val => typeof val === 'number';
export const isString = val => typeof val === 'string';
export const isIntegerKey = key => parseInt(key) + '' === key;

let hasOwnProperty = Object.prototype.hasOwnProperty;
export const hasOwn = (target,key) => hasOwnProperty.call(target,key);

export const hasChanged = (oldValue,value) => oldValue !== value;

3.2 reactive.ts

入口文件reactvity/src/index.ts

// reactvity/src/index.ts
export {
    reactive,
    readonly,
    shallowReactive,
    shallowReadonly
} from './reactive'

响应式API

import {isObject} from '@vue/shared/src'
import {
    mutableHandlers,
    readonlyHandlers,
    shallowReactiveHandlers,
    shallowReadonlyHandlers
} from "./baseHandlers";  // 不同的拦截函数

// 是不是仅读,是不是深度,基于柯里化编程实现
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)
}

// createReactiveObject 创建响应式对象
function createReactiveObject(target, isReadonly, baseHandlers) {}

createReactiveObject 函数的实现

/**
 * createReactiveObject 创建响应式对象
 * @param target 拦截的目标
 * @param isReadonly 是不是仅读属性
 * @param baseHandlers 对应的拦截函数
 */

const reactiveMap = new WeakMap();  // 会自动垃圾回收,不会造成内存泄露,存储的key只能是对象
const readonlyMap = new WeakMap();
export function createReactiveObject(target,isReadonly,baseHandler){
    // 如果目标不是对象,没法拦截,reactive 这个 API 只能拦截对象类型
    if(!isObject(target)){
        return target;
    }
    // 如果某个对象已经被代理过了,就不要再代理了,也可能一个对象被代理是深度的,又被仅读代理了
    const proxyMap = isReadonly ? readonlyMap : reactiveMap;
    const existProxy = proxyMap.get(target);
    if(existProxy){
        return existProxy;  // 如果已经被代理了,直接返回即可
    }
	// 如果没有被代理,则基于 Proxy 实现对象代理
    const proxy = new Proxy(target,baseHandler);
    proxyMap.set(target,proxy);  // 将要代理的对象,和对应代理结果缓存起来
    return proxy;
}

3.3 baseHandler.ts

baseHandler.ts中主要实现了拦截器的逻辑(getter、setter)

import { isObject } from "@vue/shared";
import { reactive, readonly } from "./reactive";

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

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

/** createGetter 拦截获取功能
 * @param isReadonly 是不是仅读
 * @param shallow 是不是浅响应
 */
function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
        // 后续Object上的方法会被迁移到 Reflect上
        // 以前target[key] = value 方式设置值可能会失败,不会报异常,也没有返回标识
        // Reflect 方法是具备返回值的
        const res = Reflect.get(target, key, receiver); // target[key]

        if (!isReadonly) { // 如果是仅读的无需收集依赖,等数据变化后更新对应视图
            console.log('依赖收集')
        }

        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 result = Reflect.set(target, key, value, receiver);
        return result;
    }
}

export const mutableHandlers = {
    get,
    set
};
export const readonlyHandlers = {
    get: readonlyGet,
    set(target, key) {
        console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`)
        return true;
    }
};
export const shallowReactiveHandlers = {
    get: shallowGet,
    set: shallowSet
};
export const shallowReadonlyHandlers = {
    get: shallowReadonlyGet,
    set(target, key) {
        console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`)
        return true;
    }
};

以上就基本实现了vue3响应式原理