// 一些工具方法
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 删除。