前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue3源码阅读笔记之数据响应式

Vue3源码阅读笔记之数据响应式

原创
作者头像
wanyicheng
修改2021-04-13 10:04:17
6990
修改2021-04-13 10:04:17
举报
文章被收录于专栏:纸上得来终觉浅
代码语言:javascript
复制
// 一些工具方法
function makeMap(str, expectsLowerCase) {
    const map = Object.create(null);
    const list = str.split(',');
    for (let i = 0; i < list.length; i++) {
        map[list[i]] = true;
    }
    return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val];
}

const EMPTY_OBJ = Object.freeze({})
        ;
const EMPTY_ARR = Object.freeze([]) ;
const isSymbol = (val) => typeof val === 'symbol';
const extend = Object.assign;
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
const isArray = Array.isArray;
// compare whether a value has changed, accounting for NaN.
const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue);
const isMap = (val) => toTypeString(val) === '[object Map]';
const isSet = (val) => toTypeString(val) === '[object Set]';
const isIntegerKey = (key) => isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key;
const isString = (val) => typeof val === 'string';
const toTypeString = (value) => objectToString.call(value);
const toRawType = (value) => {
    // extract "RawType" from strings like "[object RawType]"
    return toTypeString(value).slice(8, -1);
};

// effect 函数有关代码
// targetMap key: 值可能被修改的对象 target value: 一个set结构 depsMap
// depsMap key: target被观察的key value:一个effect方法 在值修改的时候触发
const targetMap = new WeakMap();
// 由于在某个effect调用过程中 可能再次调用effect去产生新的effect函数 所以会形成effect栈
const effectStack = [];
// 当前活跃的effect 栈的最顶层那一个
let activeEffect;
// 集合类型追踪遍历操作的key名
const ITERATE_KEY = Symbol('iterate' );
// Map类型追踪keys遍历key集合操作的key名
const MAP_KEY_ITERATE_KEY = Symbol('Map key iterate' );

function isEffect(fn) {
    return fn && fn._isEffect === true;
}
// 创建一个effect包装函数
function effect(fn, options = EMPTY_OBJ) {
    // 取原始函数
    if (isEffect(fn)) {
        fn = fn.raw;
    }
    const effect = createReactiveEffect(fn, options);
    // 立即执行一次
    if (!options.lazy) {
        effect();
    }
    return effect;
}
function stop(effect) {
    if (effect.active) {
        // 清除
        cleanup(effect);
        // stop钩子
        if (effect.options.onStop) {
            effect.options.onStop();
        }
        effect.active = false;
    }
}
let uid = 0;
// 内部创建响应式effect方法
function createReactiveEffect(fn, options) {
    const effect = function reactiveEffect() {
        // 实际effect被调用的时候 在业务方法 fn 执行之前会被下面的逻辑所控制
        if (!effect.active) {
            // 配置参数里面的 scheduler 选项 vue用的 这里我们忽略
            return options.scheduler ? undefined : fn();
        }
        // 只添加不同的effect
        if (!effectStack.includes(effect)) {
            // 总是先清除一次 后面的fn调用如果再次依赖 就重新添加即可
            cleanup(effect);
            try {
                // 执行业务函数 fn
                enableTracking();
                effectStack.push(effect);
                activeEffect = effect;
                return fn();
            }
            finally {
                // 恢复栈信息
                effectStack.pop();
                resetTracking();
                activeEffect = effectStack[effectStack.length - 1];
            }
        }
    };
    effect.id = uid++;
    // 允许递归调用 effect调用过程中可能再次调用同一个effect
    effect.allowRecurse = !!options.allowRecurse;
    effect._isEffect = true;
    effect.active = true;
    effect.raw = fn;
    effect.deps = [];
    effect.options = options;
    return effect;
}
// 这个effect可以被多个被观察者所依赖 所以需要取出这个依赖集合 逐一删除
function cleanup(effect) {
    // deps是一个set集合 其中每一项也是一个set集合(其中存放着多个effect 包含当前这个需要清除的effect在内) 
    const { deps } = effect;
    if (deps.length) {
        for (let i = 0; i < deps.length; i++) {
            deps[i].delete(effect);
        }
        deps.length = 0;
    }
}

// 追踪和触发有关代码
// 控制在任意逻辑段是否可以继续追踪 且 恢复
let shouldTrack = true;
const trackStack = [];
function pauseTracking() {
    trackStack.push(shouldTrack);
    shouldTrack = false;
}
function enableTracking() {
    trackStack.push(shouldTrack);
    shouldTrack = true;
}
function resetTracking() {
    const last = trackStack.pop();
    shouldTrack = last === undefined ? true : last;
}
// 追踪实现
function track(target, type, key) {
    // 控制逻辑
    if (!shouldTrack || activeEffect === undefined) {
        return;
    }
    // 根据target取出 key:key value:set 的map集合
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()));
    }
    // 取出对应key的 effect set集合
    let dep = depsMap.get(key);
    if (!dep) {
        depsMap.set(key, (dep = new Set()));
    }
    if (!dep.has(activeEffect)) {
        // 添加
        dep.add(activeEffect);
        // 相互添加
        activeEffect.deps.push(dep);
        // Track钩子
        if (activeEffect.options.onTrack) {
            activeEffect.options.onTrack({
                effect: activeEffect,
                target,
                type,
                key
            });
        }
    }
}
// 触发实现
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    // 取出响应effect集合
    const depsMap = targetMap.get(target);
    if (!depsMap) {
        // never been tracked
        return;
    }
    const effects = new Set();
    // 遍历一个set集合 逐个添加
    const add = (effectsToAdd) => {
        if (effectsToAdd) {
            effectsToAdd.forEach(effect => {
                // 允许递归的话 effect 可以等于自己 activeEffect
                if (effect !== activeEffect || effect.allowRecurse) {
                    effects.add(effect);
                }
            });
        }
    };
    // 清空类操作
    if (type === "clear" /* CLEAR */) {
        // collection being cleared
        // trigger all effects for target
        depsMap.forEach(add);
    }
    // 对于数组类型的来说 遍历的追踪体现在length的观察上
    else if (key === 'length' && isArray(target)) {
        depsMap.forEach((dep, key) => {
            if (key === 'length' || key >= newValue) {
                add(dep);
            }
        });
    }
    else {
        // 普通三类操作 修改/增加/删除 某一项key
        // schedule runs for SET | ADD | DELETE
        if (key !== void 0) {
            add(depsMap.get(key));
        }
        // also run for iteration key on ADD | DELETE | Map.SET
        switch (type) {
            case "add" /* ADD */:
                // 集合类独有的add操作 引发 迭代类型的effect需要更新
                if (!isArray(target)) {
                    add(depsMap.get(ITERATE_KEY));
                    // map的keys也会受到add操作的影响 所以也需要更新
                    if (isMap(target)) {
                        add(depsMap.get(MAP_KEY_ITERATE_KEY));
                    }
                }
                // 数组新增一项 引发长度发生改变
                else if (isIntegerKey(key)) {
                    // new index added to array -> length changes
                    add(depsMap.get('length'));
                }
                break;
            case "delete" /* DELETE */:
                // 删除操作 对应集合类 同上
                if (!isArray(target)) {
                    add(depsMap.get(ITERATE_KEY));
                    if (isMap(target)) {
                        add(depsMap.get(MAP_KEY_ITERATE_KEY));
                    }
                }
                break;
            case "set" /* SET */:
                // map独有的set 引发遍历操作的依赖需要更新
                if (isMap(target)) {
                    add(depsMap.get(ITERATE_KEY));
                }
                break;
        }
    }
    // 逐个执行effect
    const run = (effect) => {
        // Trigger 钩子
        if (effect.options.onTrigger) {
            effect.options.onTrigger({
                effect,
                target,
                key,
                type,
                newValue,
                oldValue,
                oldTarget
            });
        }
        // 有控制调度函数的以控制调度函数为准 vue中是加入更新事件队列 我们这直接执行就好了
        if (effect.options.scheduler) {
            effect.options.scheduler(effect);
        }
        else {
            effect();
        }
    };
    effects.forEach(run);
}

// 一些不被track的属性
const isNonTrackableKeys = /*#__PURE__*/ makeMap(`__proto__,__v_isRef,__isVue`);
const builtInSymbols = new Set(Object.getOwnPropertyNames(Symbol)
    .map(key => Symbol[key])
    .filter(isSymbol));

const get = /*#__PURE__*/ createGetter();
// 重写数组的几个遍历方法
const arrayInstrumentations = {};
['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
    // 原型链上的函数方法
    const method = Array.prototype[key];
    arrayInstrumentations[key] = function (...args) {
        // 我们是通过数组对象的实例调用.xxx方法的 所以this指向实例数组对象
        const arr = toRaw(this);
        // 遍历操作 触发数组的索引 key get
        for (let i = 0, l = this.length; i < l; i++) {
            track(arr, "get" /* GET */, i + '');
        }
        // we run the method using the original args first (which may be reactive)
        const res = method.apply(arr, args);
        // 返回执行结果
        if (res === -1 || res === false) {
            // if that didn't work, run it again using raw values.
            return method.apply(arr, args.map(toRaw));
        }
        else {
            return res;
        }
    };
});
// 重写数组的几个会改变数组长度的方法
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(key => {
    const method = Array.prototype[key];
    arrayInstrumentations[key] = function (...args) {
        // 无需再追踪  因为数组这个几个方法调用的时候会先取length 但是由于pauseTracking 不会触发length类型事件 然后接着触发数组对象key对应set操作
        pauseTracking();
        // 直接调用方法 this指向数组实例对象
        debugger
        const res = method.apply(this, args);
        resetTracking();
        return res;
    };
});
function createGetter() {
    return function get(target, key, receiver) {
        // 被代理劫持的对象 访问  __v_isReactive 这个key自然返回true 代表被代理过了
        if (key === "__v_isReactive" /* IS_REACTIVE */) {
            return true;
        }
        // 获取原始对象
        else if (key === "__v_raw" /* RAW */ &&
            receiver === reactiveMap.get(target)) {
            // 对目标target存在的key做get的时候 target是等于目标对象的 否则如果去原型链上get 会导致target等于原型对象 这时候 reactiveMap.get(target) 取到的值是不会等于目标对象对应的代理的
            // https://es6.ruanyifeng.com/#docs/proxy#Proxy-%E5%AE%9E%E4%BE%8B%E7%9A%84%E6%96%B9%E6%B3%95
            return target;
        }
        const targetIsArray = isArray(target);
        if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
            // 数组类型的对象 有几种规定的方法是被特殊处理的
            return Reflect.get(arrayInstrumentations, key, receiver);
        }
        const res = Reflect.get(target, key, receiver);
        // 一些特殊属性不追踪
        if (isSymbol(key)
            ? builtInSymbols.has(key)
            : isNonTrackableKeys(key)) {
            return res;
        }
        track(target, "get" /* GET */, key);
        
        if (isObject(res)) {
            // Convert returned value into a proxy as well. we do the isObject check
            // here to avoid invalid value warning. Also need to lazy access readonly
            // and reactive here to avoid circular dependency.
            // 所谓的延迟代理 只有取到某个属性 发现它也是个对象的时候 才给它设置代理
            // 其实我们只代理了一层
            return reactive(res);
        }
        return res;
    };
}
const set = /*#__PURE__*/ createSetter();
function createSetter() {
    return function set(target, key, value, receiver) {
        const oldValue = target[key];
        const hadKey = isArray(target) && isIntegerKey(key)
            ? Number(key) < target.length
            : hasOwn(target, key);
        const result = Reflect.set(target, key, value, receiver);
        // don't trigger if target is something up in the prototype chain of original
        // 同get的场景 只针对原始对象的修改才触发
        if (target === toRaw(receiver)) {
            if (!hadKey) {
                trigger(target, "add" /* ADD */, key, value);
            }
            else if (hasChanged(value, oldValue)) {
                trigger(target, "set" /* SET */, key, value, oldValue);
            }
        }
        return result;
    };
}
// 删除某个属性
function deleteProperty(target, key) {
    const hadKey = hasOwn(target, key);
    const oldValue = target[key];
    const result = Reflect.deleteProperty(target, key);
    if (result && hadKey) {
        trigger(target, "delete" /* DELETE */, key, undefined, oldValue);
    }
    return result;
}
// 追踪 in 操作
function has(target, key) {
    const result = Reflect.has(target, key);
    if (!isSymbol(key) || !builtInSymbols.has(key)) {
        track(target, "has" /* HAS */, key);
    }
    return result;
}
// 追踪遍历操作 ownKeys
function ownKeys(target) {
    track(target, "iterate" /* ITERATE */, isArray(target) ? 'length' : ITERATE_KEY);
    return Reflect.ownKeys(target);
}
// 组合成基础handlers
const mutableHandlers = {
    get,
    set,
    deleteProperty,
    has,
    ownKeys
};

// 集合有关的handlers
const toReactive = (value) => isObject(value) ? reactive(value) : value;
const getProto = (v) => Reflect.getPrototypeOf(v);
// 集合的get
function get$1(target, key) {
    // #1772: readonly(reactive(Map)) should return readonly + reactive version
    // of the value
    target = target["__v_raw" /* RAW */];
    // 原始对象
    const rawTarget = toRaw(target);
    // 原始key
    const rawKey = toRaw(key);
    // 如果key也是一个代理 那就对原始对象和代理都做一次追踪
    if (key !== rawKey) {
        track(rawTarget, "get" /* GET */, key);
    }
    track(rawTarget, "get" /* GET */, rawKey);
    const { has } = getProto(rawTarget);
    const wrap = toReactive;
    // 返回key对应的value
    if (has.call(rawTarget, key)) {
        return wrap(target.get(key));
    }
    else if (has.call(rawTarget, rawKey)) {
        return wrap(target.get(rawKey));
    }
}
// 集合的has 分析同上文
function has$1(key) {
    const target = this["__v_raw" /* RAW */];
    const rawTarget = toRaw(target);
    const rawKey = toRaw(key);
    if (key !== rawKey) {
        track(rawTarget, "has" /* HAS */, key);
    }
    track(rawTarget, "has" /* HAS */, rawKey);
    return key === rawKey
        ? target.has(key)
        : target.has(key) || target.has(rawKey);
}
// 集合的size 分析同上文
function size(target) {
    target = target["__v_raw" /* RAW */];
    track(toRaw(target), "iterate" /* ITERATE */, ITERATE_KEY);
    return Reflect.get(target, 'size', target);
}
// 集合的add 分析同上文
function add(value) {
    value = toRaw(value);
    const target = toRaw(this);
    const proto = getProto(target);
    const hadKey = proto.has.call(target, value);
    if (!hadKey) {
        target.add(value);
        trigger(target, "add" /* ADD */, value, value);
    }
    return this;
}
// Map的set 分析同上文
function set$1(key, value) {
    value = toRaw(value);
    // map实例对象
    const target = toRaw(this);
    const { has, get } = getProto(target);
    let hadKey = has.call(target, key);
    if (!hadKey) {
        key = toRaw(key);
        hadKey = has.call(target, key);
    }
    else {
        // Map中key的原始版本和代理版本都存在 输出警告信息
        checkIdentityKeys(target, has, key);
    }
    const oldValue = get.call(target, key);
    target.set(key, value);
    if (!hadKey) {
        trigger(target, "add" /* ADD */, key, value);
    }
    else if (hasChanged(value, oldValue)) {
        trigger(target, "set" /* SET */, key, value, oldValue);
    }
    return this;
}
// Map的delete 分析同上文
function deleteEntry(key) {
    const target = toRaw(this);
    const { has, get } = getProto(target);
    let hadKey = has.call(target, key);
    if (!hadKey) {
        key = toRaw(key);
        hadKey = has.call(target, key);
    }
    else {
        checkIdentityKeys(target, has, key);
    }
    const oldValue = get ? get.call(target, key) : undefined;
    // forward the operation before queueing reactions
    const result = target.delete(key);
    if (hadKey) {
        trigger(target, "delete" /* DELETE */, key, undefined, oldValue);
    }
    return result;
}
// 集合的clear 分析同上
function clear() {
    const target = toRaw(this);
    const hadItems = target.size !== 0;
    const oldTarget = isMap(target)
            ? new Map(target)
            : new Set(target)
        ;
    // forward the operation before queueing reactions
    const result = target.clear();
    if (hadItems) {
        trigger(target, "clear" /* CLEAR */, undefined, undefined, oldTarget);
    }
    return result;
}
// 集合的foreach
function createForEach() {
    return function forEach(callback, thisArg) {
        // 某个集合实例对象
        const observed = this;
        const target = observed["__v_raw" /* RAW */];
        const rawTarget = toRaw(target);
        const wrap = toReactive;
        // 追踪迭代事件
        track(rawTarget, "iterate" /* ITERATE */, ITERATE_KEY);
        // 调用原生方法 参数做了包装 把参数也设为响应式了
        return target.forEach((value, key) => {
            // important: make sure the callback is
            // 1. invoked with the reactive map as `this` and 3rd arg
            // 2. the value received should be a corresponding reactive/readonly.
            return callback.call(thisArg, wrap(value), wrap(key), observed);
        });
    };
}
// 依赖迭代器遍历的方法实现
function createIterableMethod(method, isReadonly, isShallow) {
    return function (...args) {
        const target = this["__v_raw" /* RAW */];
        const rawTarget = toRaw(target);
        const targetIsMap = isMap(rawTarget);
        // 是否是取键值对
        const isPair = method === 'entries' || (method === Symbol.iterator && targetIsMap);
        // 是否是map独有的keys
        const isKeyOnly = method === 'keys' && targetIsMap;
        // 原始迭代器
        const innerIterator = target[method](...args);
        const wrap = toReactive;
        // 追踪迭代事件
        track(rawTarget, "iterate" /* ITERATE */, isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY);
        // return a wrapped iterator which returns observed versions of the
        // values emitted from the real iterator
        // 迭代器协议的定义内容实现
        return {
            // iterator protocol
            next() {
                const { value, done } = innerIterator.next();
                return done
                    ? { value, done }
                    : {
                        value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
                        done
                    };
            },
            // iterable protocol
            [Symbol.iterator]() {
                return this;
            }
        };
    };
}
const mutableInstrumentations = {
    get(key) {
        return get$1(this, key);
    },
    get size() {
        return size(this);
    },
    has: has$1,
    add,
    set: set$1,
    delete: deleteEntry,
    clear,
    forEach: createForEach()
};

// 重写这几个遍历方法
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator];
iteratorMethods.forEach(method => {
    mutableInstrumentations[method] = createIterableMethod(method);
});
// 单独再对get做一次包装处理
function createInstrumentationGetter() {
    const instrumentations = mutableInstrumentations;
    return (target, key, receiver) => {
        if (key === "__v_isReactive" /* IS_REACTIVE */) {
            return true;
        }
        else if (key === "__v_raw" /* RAW */) {
            return target;
        }
        // 只处理上面几种方法
        return Reflect.get(hasOwn(instrumentations, key) && key in target
            ? instrumentations
            : target, key, receiver);
    };
}
const mutableCollectionHandlers = {
    get: createInstrumentationGetter()
};

// 检查key的有效性
function checkIdentityKeys(target, has, key) {
    const rawKey = toRaw(key);
    if (rawKey !== key && has.call(target, rawKey)) {
        const type = toRawType(target);
        console.warn(`Reactive ${type} contains both the raw and reactive ` +
            `versions of the same object${type === `Map` ? ` as keys` : ``}, ` +
            `which can lead to inconsistencies. ` +
            `Avoid differentiating between the raw and reactive versions ` +
            `of an object and only use the reactive version if possible.`);
    }
}

// key:被代理的对象 target value: 得到的代理对象 proxy
const reactiveMap = new WeakMap();

// 获取proxy代理对应的原始对象
function toRaw(observed) {
    // get __v_raw 这个key的时候handler返回原始对象
    return ((observed && toRaw(observed["__v_raw" /* RAW */])) || observed);
}

// 白名单列表
function targetTypeMap(rawType) {
    switch (rawType) {
        case 'Object':
        case 'Array':
            return 1 /* COMMON */;
        case 'Map':
        case 'Set':
        case 'WeakMap':
        case 'WeakSet':
            return 2 /* COLLECTION */;
        default:
            return 0 /* INVALID */;
    }
}

// 获取对象类型 0 1 2
function getTargetType(value) {
    return !Object.isExtensible(value)
        ? 0 /* INVALID */
        : targetTypeMap(toRawType(value));
}

function createReactiveObject(target, baseHandlers, collectionHandlers) {
    // 只把object类型的数据设置为响应式
    if (!isObject(target)) {
        {
            console.warn(`value cannot be made reactive: ${String(target)}`);
        }
        return target;
    }
    
    // target already has corresponding Proxy
    // 确认是否已经代理过它了
    const proxyMap = reactiveMap;
    const existingProxy = proxyMap.get(target);
    if (existingProxy) {
        return existingProxy;
    }
    // only a whitelist of value types can be observed.
    // 可设置对象类型白名单
    const targetType = getTargetType(target);
    if (targetType === 0 /* INVALID */) {
        return target;
    }
    // 分情况设置handler 2大类:1.普通对象 2.集合对象
    const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
    proxyMap.set(target, proxy);
    return proxy;
}

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

function test() {
    let proxy;
    let updateCall = false;

    let obj = {
        key1: '123'
    }

    proxy = reactive(obj);

    // 测试已有key赋值
    effect(() => {
        let empty = proxy.key1;
        if (!updateCall) {
            console.log('测试已有key赋值开始:');
            updateCall = true;
        } else {
            console.log('effect函数执行')
            updateCall = false;
        }
        
    })
    proxy.key1 = '321';

    // 测试get不存在的key会拥有响应式不
    effect(() => {
        let empty = proxy.key2;
        if (!updateCall) {
            console.log('测试新增key会拥有响应式不开始:');
            updateCall = true;
        } else {
            console.log('effect函数执行')
            updateCall = false;
        }
        
    })

    proxy.key2 = '333';


    // 测试修改数组
    proxy = reactive([1,2,3]);
    effect(() => {
        let empty = proxy[0];
        if (!updateCall) {
            console.log('测试修改数组开始:');
            updateCall = true;
        } else {
            console.log('effect函数执行')
            updateCall = false;
        }
        
    })
    proxy[0] = 4;

    // 测试数组实例方法
    proxy = reactive([1,2,3]);
    effect(() => {
        // 需要触发length类型的追踪才可以相应push之类的操作
        let empty = proxy.includes(1);
        // 如下面的普通某个key取值就是无法捕获下面的push操作的
        // let empty = proxy[0];
        if (!updateCall) {
            console.log('测试数组实例方法开始:');
            updateCall = true;
        } else {
            console.log('effect函数执行')
            updateCall = false;
        }
        
    })
    proxy.push(4);

    let k1 = {};
    let map;
    // 测试Map实例方法
    map = new Map([[k1, 1]]);
    proxy = reactive(map);
    effect(() => {
        let empty = proxy.get(k1);
        if (!updateCall) {
            console.log('测试Map实例方法开始:');
            updateCall = true;
        } else {
            console.log('effect函数执行')
            updateCall = false;
        }
    })
    proxy.set(k1, 2);

    // 测试Map新增key方法
    map = new Map([[k1, 1]]);
    proxy = reactive(map);
    effect(() => {
        // 需要触发iterate类型的追踪才可以相应set或者add之类的操作
        let empty = proxy.forEach(_ => _);
        // 如下面的普通某个key取值就是无法捕获下面的set操作的
        // let empty = proxy.get(k1);
        if (!updateCall) {
            console.log('测试Map新增key方法:');
            updateCall = true;
        } else {
            console.log('effect函数执行')
            updateCall = false;
        }
    })
    proxy.set({}, 2);

    console.log('测试结束');
}

test();

// 总结一下: 通过代理设置所有可观察对象的handlers来控制数据的取值赋值逻辑,实现自己的某种响应式处理,在vue中是重新执行render函数得到最新的vnode,然后patch到原dom上更新UI视图。 我们参考源码删除了一些支线情况且加了注释,应该会比较好理解一些。

// 上面的响应式对象 约束只能是object类型 但是我们如果只想观察一个基础类型对象呢?

// 看下vue中ref是怎么做的:

const convert = (val) => isObject(val) ? reactive(val) : val;
function isRef(r) {
    return Boolean(r && r.__v_isRef === true);
}
// 外部调用方法
function ref(value) {
    return createRef(value);
}
// ref标准类实现
class RefImpl {
    constructor(_rawValue) {
        this._rawValue = _rawValue;
        this.__v_isRef = true;
        this._value = convert(_rawValue);
    }
    get value() {
        // this指向某个ref实例对象
        track(toRaw(this), "get" /* GET */, 'value');
        return this._value;
    }
    set value(newVal) {
        if (hasChanged(toRaw(newVal), this._rawValue)) {
            this._rawValue = newVal;
            // 新值也设置为响应式的
            this._value = convert(newVal);
            trigger(toRaw(this), "set" /* SET */, 'value', newVal);
        }
    }
}
// 实际执行函数
function createRef(rawValue, shallow = false) {
    if (isRef(rawValue)) {
        return rawValue;
    }
    return new RefImpl(rawValue, shallow);
}
// 解除ref
function unref(ref) {
    return isRef(ref) ? ref.value : ref;
}

// 其实就是对原始值做了一层包装 我们要通过value来存取值


// 然后看下自定义ref
class CustomRefImpl {
    constructor(factory) {
        this.__v_isRef = true;
        // 跟文档对应 需要用户在 get set的时候执行对应的方法
        const { get, set } = factory(() => track(this, "get" /* GET */, 'value'), () => trigger(this, "set" /* SET */, 'value'));
        this._get = get;
        this._set = set;
    }
    get value() {
        return this._get();
    }
    set value(newVal) {
        this._set(newVal);
    }
}
function customRef(factory) {
    return new CustomRefImpl(factory);
}

// 而对于一个已经是 proxy的对象 我们如何提出它的某个属性的引用 ?
function toRefs(object) {
    if (!isProxy(object)) {
        console.warn(`toRefs() expects a reactive object but received a plain one.`);
    }
    const ret = isArray(object) ? new Array(object.length) : {};
    for (const key in object) {
        // 其实就是调用toRef
        ret[key] = toRef(object, key);
    }
    return ret;
}
// 普通对象 是没有响应式的
class ObjectRefImpl {
    constructor(_object, _key) {
        this._object = _object;
        this._key = _key;
        this.__v_isRef = true;
    }
    get value() {
        return this._object[this._key];
    }
    set value(newVal) {
        this._object[this._key] = newVal;
    }
}
// 取出目标对象的key对应的value 
function toRef(object, key) {
    return isRef(object[key])
        ? object[key]
        : new ObjectRefImpl(object, key);
}

// 总结一下:ref的内容比较简单 主要是对象的包装 然后取值的时候要注意value


// 看完响应式的基础之后 来看下一些衍生的应用

// 1. computed对象:依赖的对象值发生改变后自身也自动发送改变的对象

// 先看下vue怎么做的:

// computed对象类实现
class ComputedRefImpl {
    constructor(getter, _setter, isReadonly) {
        
        this._setter = _setter;
        // 只有这个dirty为true的时候才会进行一次getter的求值操作更新this._vlaue
        // 否则一直返回缓存的旧值
        this._dirty = true;
        this.__v_isRef = true;
        this.effect = effect(getter, {
            // 延迟 这个effect不会立即触发
            lazy: true,
            // 存在调度函数 那么当依赖源发生改变执行trigger方法的的时候执行它 而不是 执行getter
            scheduler: () => {
                // 方法被执行的时候 意味着有依赖对象发生改变了 副作用函数被触发执行了
                // 曾经有某个地方依赖了这个getter 导致 _dirty 变false 所以需要更新了
                if (!this._dirty) {
                    this._dirty = true;
                    // 再次触发依赖这个computed对象的对象的更新
                    trigger(toRaw(this), "set" /* SET */, 'value');
                }
            }
        });
        this["__v_isReadonly" /* IS_READONLY */] = isReadonly;
    }
    get value() {
        // 依赖这个computed的时候 会触发get
        if (this._dirty) {
            // 只有上面的 scheduler 方法被trigger函数执行后才会更新value
            // effect会重新执行getter 取到最新值
            this._value = this.effect();
            this._dirty = false;
        }
        // 追踪关注这个computed对象的对象
        track(toRaw(this), "get" /* GET */, 'value');
        return this._value;
    }
    set value(newValue) {
        this._setter(newValue);
    }
}

// 回忆一下vue中如何写computed对象的:
/**
 * computed: {
 *    someExpress() {
 *      return this.a + this.b;
 *    }
 * } 
 * 
 * 类似上文的这种
 */
function computed(getterOrOptions) {
    let getter;
    let setter;
    // 一般情况都是直接用一个函数当做getter的
    if (isFunction(getterOrOptions)) {
        getter = getterOrOptions;
        // 默认setter
        setter = () => {
                console.warn('Write operation failed: computed value is readonly');
            }
            ;
    }
    else {
        getter = getterOrOptions.get;
        setter = getterOrOptions.set;
    }
    return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions) || !getterOrOptions.set);
}

// 总结一下: 写的比vue2.x时候的computed对象好好理解多了呢,也很清晰明了。

// 2. watch 对象

// 看下vue的实现:

// Simple effect.
// 从文档上看定义:在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。
// doWatch 中会有它的实现
function watchEffect(effect, options) {
    return doWatch(effect, null, options);
}
// initial value for watchers to trigger on undefined initial values
const INITIAL_WATCHER_VALUE = {};
// implementation
// 其实都是调用的 doWatch
function watch(source, cb, options) {
    if (!isFunction(cb)) {
        warn(`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
            `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
            `supports \`watch(source, cb, options?) signature.`);
    }
    return doWatch(source, cb, options);
}

// this.$watch
// 组件实例的调用 $watch 
function instanceWatch(source, cb, options) {
    const publicThis = this.proxy;
    // 设置合适的getter 如果是 watch的是一个字符串 说明是this上的某个属性
    // 如果是一个function 那就绑定this到实例,再执行 doWatch ,注意 第二个参数cb也被绑定了this
    const getter = isString(source)
        ? () => publicThis[source]
        : source.bind(publicThis);
    return doWatch(getter, cb.bind(publicThis), options, this);
}

// deep观察一个对象的时候 这个对象子属性的子属性发生改变了 也要响应 所以需要深度遍历触发value的get
function traverse(value, seen = new Set()) {
    if (!isObject(value) || seen.has(value)) {
        return value;
    }
    seen.add(value);
    if (isRef(value)) {
        traverse(value.value, seen);
    }
    else if (isArray(value)) {
        for (let i = 0; i < value.length; i++) {
            traverse(value[i], seen);
        }
    }
    else if (isSet(value) || isMap(value)) {
        value.forEach((v) => {
            traverse(v, seen);
        });
    }
    else {
        for (const key in value) {
            traverse(value[key], seen);
        }
    }
    return value;
}

// 核心就是这个方法了
function doWatch(source, cb, { immediate, deep, flush, onTrack, onTrigger } = EMPTY_OBJ, instance = currentInstance) {
    // 参数的使用场景约定 见提示文案即可
    if (!cb) {
        if (immediate !== undefined) {
            warn(`watch() "immediate" option is only respected when using the ` +
                `watch(source, callback, options?) signature.`);
        }
        if (deep !== undefined) {
            warn(`watch() "deep" option is only respected when using the ` +
                `watch(source, callback, options?) signature.`);
        }
    }
    // 可以watch的数据类型见提示文案
    const warnInvalidSource = (s) => {
        warn(`Invalid watch source: `, s, `A watch source can only be a getter/effect function, a ref, ` +
            `a reactive object, or an array of these types.`);
    };
    let getter;
    let forceTrigger = false;
    if (isRef(source)) {
        // ref类型 取它的value
        getter = () => source.value;
        forceTrigger = !!source._shallow;
    }
    else if (isReactive(source)) {
        // 响应式对象 默认是deep getter返回响应对象即可
        getter = () => source;
        deep = true;
    }
    // 参数类型是数组
    else if (isArray(source)) {
        getter = () => source.map(s => {
            if (isRef(s)) {
                return s.value;
            }
            else if (isReactive(s)) {
                return traverse(s);
            }
            else if (isFunction(s)) {
                // 数组中如果有函数的情况 在触发getter的时候 就包裹一下执行它 返回它的结果即可
                return callWithErrorHandling(s, instance, 2 /* WATCH_GETTER */, [
                    instance && instance.proxy
                ]);
            }
            else {
                warnInvalidSource(s);
            }
        });
    }
    else if (isFunction(source)) {
        if (cb) {
            // getter with cb
            getter = () => callWithErrorHandling(source, instance, 2 /* WATCH_GETTER */, [
                instance && instance.proxy
            ]);
        }
        else {
            // no cb -> simple effect
            // 对应上文的 watchEffect
            getter = () => {
                if (instance && instance.isUnmounted) {
                    return;
                }
                if (cleanup) {
                    cleanup();
                }
                // 也是执行它
                return callWithErrorHandling(source, instance, 3 /* WATCH_CALLBACK */, [onInvalidate]);
            };
        }
    }
    else {
        getter = NOOP;
        warnInvalidSource(source);
    }
    // 深度递归去触发子属性的getter
    if (cb && deep) {
        const baseGetter = getter;
        getter = () => traverse(baseGetter());
    }
    // 以上操作都是为了得到格式化的getter
    let cleanup;
    // 暴露给用户设置的清除函数
    const onInvalidate = (fn) => {
        cleanup = runner.options.onStop = () => {
            callWithErrorHandling(fn, instance, 4 /* WATCH_CLEANUP */);
        };
    };
    // 默认初始值
    let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE;
    const job = () => {
        if (!runner.active) {
            return;
        }
        if (cb) {
            // watch(source, cb)
            // 直接触发effect 执行getter方法 获取当前值 触发依赖收集
            const newValue = runner();
            if (deep || forceTrigger || hasChanged(newValue, oldValue)) {
                // cleanup before running cb again
                if (cleanup) {
                    cleanup();
                }
                // 执行数据变更后用户设置的回调cb
                callWithAsyncErrorHandling(cb, instance, 3 /* WATCH_CALLBACK */, [
                    newValue,
                    // pass undefined as the old value when it's changed for the first time
                    oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
                    onInvalidate
                ]);
                oldValue = newValue;
            }
        }
        else {
            // watchEffect
            // 没有cb的情况 直接执行getter 触发依赖收集就好了
            runner();
        }
    };
    // important: mark the job as a watcher callback so that scheduler knows
    // it is allowed to self-trigger (#1727)
    job.allowRecurse = !!cb;
    let scheduler;
    // watch的cb触发时机控制 是同步执行job 还是调用 queuePostRenderEffect 放到render渲染函数执行完之后再执行
    // vue的更新队列后面在单独分析
    if (flush === 'sync') {
        scheduler = job;
    }
    else if (flush === 'post') {
        scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
    }
    // 默认是pre队列 优先级最高的队列
    else {
        // default: 'pre'
        scheduler = () => {
            // instance 只有在渲染过程中才存在
            // 所以用户单独调用watch如果不设置post参数 就被推入pre队列了
            if (!instance || instance.isMounted) {
                // 在这里推送任务
                queuePreFlushCb(job);
            }
            else {
                // with 'pre' option, the first call must happen before
                // the component is mounted so it is called synchronously.
                // pre情况直接执行job
                job();
            }
        };
    }
    // 延迟型effect
    const runner = effect(getter, {
        lazy: true,
        onTrack,
        onTrigger,
        scheduler
    });
    // vue内部的实例收集effect 与分析watch的实现没有太大的关系 忽略先
    // recordInstanceBoundEffect(runner, instance);
    // initial run
    if (cb) {
        // 立即执行 getter
        if (immediate) {
            job();
        }
        else {
            // 不然就让scheduler来控制
            oldValue = runner();
        }
    }
    else if (flush === 'post') {
        queuePostRenderEffect(runner, instance && instance.suspense);
    }
    else {
        runner();
    }
    // 返回一个方法给用户停止这个watcher
    return () => {
        stop(runner);
        if (instance) {
            remove(instance.effects, runner);
        }
    };
}

// 总结一下:watch 也是构建合适的getter函数,然后创建effect,然后根据参数选择合适的触发getter时机,然后在依赖源发生变化后依靠trigger去执行用户设置的回调cb。

总结:Vue3中的数据响应式实现是一个较为独立的实现,适合单独分析学习哈。上文是删除了部分支线逻辑的版本,只保留了主线逻辑,大家如果想看完整的实现,还是建议去读源码哦。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档