专栏首页纸上得来终觉浅Vue3源码阅读笔记之数据响应式
原创

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

// 一些工具方法
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中的数据响应式实现是一个较为独立的实现,适合单独分析学习哈。上文是删除了部分支线逻辑的版本,只保留了主线逻辑,大家如果想看完整的实现,还是建议去读源码哦。

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue3源码阅读笔记之$emit实现

    总结:组件实例上的 emit 方法其实就是调用props中从父组件传进来的一个箭头函数。

    wanyicheng
  • Vue3源码阅读笔记之vnode定义

    wanyicheng
  • Vue3源码阅读笔记之事件队列

    总结一下:vue中的事件队列分3种,vue内部实现中主要是把render函数的包裹体effect放到queue队列中。

    wanyicheng
  • Vue3源码阅读笔记之异步组件

    wanyicheng
  • Vue3 深度解析

    距离尤雨溪首次公开 Vue3 (vue-next)源码有一个多月了。青笔观察到,刚发布国庆期间,出现不少解读 Vue3 源码的文章。当然不少有追风蹭热之嫌,文章...

    我是一条小青蛇
  • Vue3源码阅读笔记之组件是如何实现

    wanyicheng
  • Vue3 源码解析(六):响应式原理与 reactive

    今天这篇文章是笔者会带着大家一起深入剖析 Vue3 的响应式原理实现,以及在响应式基础 API 中的 reactive 是如何实现的。对于 Vue 框架来说,其...

    Originalee
  • Vue3源码阅读笔记之整体执行顺序简介(1)

    从Vue官网得到源码(https://unpkg.com/vue@next),拷贝到本地文件,然后创建如下html:

    wanyicheng
  • Vue3源码阅读笔记之整体执行顺序简介(2)

    可以看到,目前只是直接对组件实例的data做了一次代理,handlers在普通对象情况下为 baseHandlers

    wanyicheng

扫码关注云+社区

领取腾讯云代金券