z-util-page
Version:
635 lines (634 loc) • 20.3 kB
JavaScript
/**
* @category 响应式数据API
* @module Reactive
*/
import { TriggerType } from './type';
import { wrapValue, cleanup, traverse } from './util';
import { getType } from '../deepClone/index';
//对象-副作用函数映射关系字典
const bucket = new WeakMap();
//对象-响应式对象字典
const reactiveMap = new Map();
//副作用函数执行栈
const effectStack = new Array();
//对象迭代标识
const ITERATE_KEY = Symbol();
//MapKey迭代标识
const MAP_KEY_ITERATE_KEY = Symbol();
//取原始对象key
const source = Symbol();
const wrap = wrapValue.bind({ reactive });
//活动副作用函数
let activeEffect = null;
//数组原型方法代理
const arrayInstrumentations = {};
(function () {
//在数组执行查找的原型方法时,在原始对象中进行查找,解决原始值与代理值不一致问题
['includes', 'indexOf', 'lastIndexOf'].forEach(method => {
const originMethod = Reflect.get(Array.prototype, method);
Reflect.set(arrayInstrumentations, method, function (...args) {
let res = originMethod.apply(this, args);
if (res === false || res === -1)
res = originMethod.apply(Reflect.get(this, source), args);
return res;
});
});
//在数组执行删除等涉及length属性修改的原型方法时,关闭依赖追踪
['pop', 'shift', 'splice'].forEach(method => {
const originMethod = Reflect.get(Array.prototype, method);
Reflect.set(arrayInstrumentations, method, function (...args) {
if (activeEffect)
activeEffect.shouldTrack = false;
let res = originMethod.apply(this, args);
if (activeEffect)
activeEffect.shouldTrack = true;
return res;
});
});
//在数组执行增加等涉及length属性修改的原型方法时,关闭依赖追踪
['push', 'unshift'].forEach(method => {
const originMethod = Reflect.get(Array.prototype, method);
Reflect.set(arrayInstrumentations, method, function (...args) {
if (activeEffect)
activeEffect.shouldTrack = false;
let res = originMethod.apply(this, args.map((arg) => {
return toRaw(arg);
}));
if (activeEffect)
activeEffect.shouldTrack = true;
return res;
});
});
})();
//Set-Map原型方法代理
const mutableInstrumentations = {};
(function () {
function iterationMethod() {
//获取原始对象
const target = Reflect.get(this, source);
//获取迭代器对象
const itr = target[Symbol.iterator]();
//建立响应
track(target, ITERATE_KEY);
//返回自定义迭代器
return {
//迭代器协议
next() {
//获取原始数据
const { value, done } = itr.next();
return {
value: value ? [wrap(value[0]), wrap(value[1])] : value,
done
};
},
//可迭代协议
[Symbol.iterator]() {
return this;
}
};
}
function valuesIterationMethod() {
//获取原始对象
const target = Reflect.get(this, source);
//获取迭代器对象
const itr = target.values();
//建立响应
track(target, ITERATE_KEY);
//返回自定义迭代器
return {
//迭代器协议
next() {
//获取原始数据
const { value, done } = itr.next();
return {
value: wrap(value),
done
};
},
//可迭代协议
[Symbol.iterator]() {
return this;
}
};
}
function keysIterationMethod() {
//获取原始对象
const target = Reflect.get(this, source);
//获取迭代器对象
const itr = target.keys();
//建立响应
track(target, MAP_KEY_ITERATE_KEY);
//返回自定义迭代器
return {
//迭代器协议
next() {
//获取原始数据
const { value, done } = itr.next();
return {
value: wrap(value),
done
};
},
//可迭代协议
[Symbol.iterator]() {
return this;
}
};
}
mutableInstrumentations.add = function (key) {
//获取原始对象
const target = Reflect.get(this, source);
//是否只读
const { isReadonly } = reactiveMap.get(target) || {};
if (isReadonly) {
console.log(target, `对象是只读的`);
return false;
}
//取原始值
const orgin = toRaw(key);
//判断值是否已存在
const hadkey = target.has(orgin);
const res = target.add(orgin);
//当值不存在时触发副作用函数
if (!hadkey)
trigger(target, orgin, TriggerType.ADD);
return res;
};
mutableInstrumentations.get = function (key) {
var _a;
//获取原始对象
const target = Reflect.get(this, source);
//是否浅代理、只读
const { isShadow, isReadonly } = reactiveMap.get(target) || {};
//取原始值, 避免数据污染
const orginKey = toRaw(key);
//只读对象或者key为symbol时不进行追踪
if (!isReadonly)
track(target, orginKey);
const res = target.get(orginKey);
//浅代理模式直接返回原始值
if (isShadow)
return res;
//非浅代理模式,如果原始值不是基本类型进行响应式包装
if (typeof res === 'object' && res != null) {
const existionProxy = (_a = reactiveMap.get(res)) === null || _a === void 0 ? void 0 : _a.value;
if (existionProxy)
return existionProxy;
return reactive(res, isShadow, isReadonly);
}
//否则返回基本类型值
return res;
};
mutableInstrumentations.set = function (key, value) {
//获取原始对象
const target = Reflect.get(this, source);
//是否浅代理、只读
const { isReadonly } = reactiveMap.get(target) || {};
if (isReadonly) {
console.log(target, `对象是只读的`);
return false;
}
//取原始值, 避免数据污染
const orginKey = toRaw(key);
const orginValue = toRaw(value);
//判断是否已存在
const hadKey = target.has(orginKey);
//取出旧值
const oldValue = target.get(orginKey);
//非浅代理时设置原始对象而非响应对象
const res = target.set(orginKey, orginValue);
if (!hadKey)
trigger(target, orginKey, TriggerType.ADD);
else if (oldValue !== orginValue && (oldValue === oldValue || orginValue === orginValue)) {
trigger(target, orginKey, TriggerType.SET);
}
return res;
};
mutableInstrumentations.delete = function (key) {
//获取原始对象
const target = Reflect.get(this, source);
//取原始值
const orgin = toRaw(key);
//判断值是否已存在
const hadkey = target.has(orgin);
const res = target.delete(orgin);
//当值存在时触发副作用函数
if (hadkey)
trigger(target, orgin, TriggerType.DELETE);
return res;
};
mutableInstrumentations.forEach = function (cb, thisArg) {
//获取原始对象
const target = Reflect.get(this, source);
//与迭代key建立响应
track(target, ITERATE_KEY);
//调用原函数
target.forEach((value, key) => {
cb.call(thisArg, wrap(value), wrap(key), this);
});
};
mutableInstrumentations.entries = iterationMethod;
mutableInstrumentations.values = valuesIterationMethod;
mutableInstrumentations.keys = keysIterationMethod;
mutableInstrumentations[Symbol.iterator] = iterationMethod;
})();
/**
* 依赖追踪
* @param target 目标对象
* @param p 键值
*/
function track(target, p) {
if (!activeEffect || !activeEffect.shouldTrack) {
return;
}
let depsMap = bucket.get(target);
if (!depsMap) {
bucket.set(target, (depsMap = new Map()));
}
let deps = depsMap.get(p);
if (!deps) {
depsMap.set(p, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
/**
* 触发副作用
* @param target 目标对象
* @param p 键值
* @param type 类型
* @param value 新值
* @returns 是否成功
*/
function trigger(target, p, type, value) {
let depsMap = bucket.get(target);
if (!depsMap) {
return true;
}
//取出所有与key直接相关的副作用函数
let effects = depsMap.get(p);
//待执行副作用函数队列
let effectsToRun = new Set();
//排除正在运行的副作用函数
effects && effects.forEach(effectFn => {
if (effectFn !== activeEffect)
effectsToRun.add(effectFn);
});
//type为ADD或者DELETE或为Map类型设置值时,取出迭代相关副作用函数执行
if (type === TriggerType.ADD || type === TriggerType.DELETE || (type === TriggerType.SET && getType(target) === 'Map')) {
let iterateEffects = depsMap.get(ITERATE_KEY);
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect)
effectsToRun.add(effectFn);
});
}
//type为ADD或者DELETE且target为Map时,取出迭代相关副作用函数执行
if ((type === TriggerType.ADD || type === TriggerType.DELETE) && getType(target) === 'Map') {
let iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY);
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect)
effectsToRun.add(effectFn);
});
}
//type为ADD且target为数组时,取出数组迭代相关副作用函数执行(使用delete删除数组元素不会改变数组长度,故无需触发)
if (type === TriggerType.ADD && Array.isArray(target)) {
let lengthEffects = depsMap.get('length');
lengthEffects && lengthEffects.forEach(effectFn => {
if (effectFn !== activeEffect)
effectsToRun.add(effectFn);
});
}
//target为数组且直接操作数组length属性时,取出大于新length值的副作用函数执行
if (Array.isArray(target) && p === 'length') {
depsMap.forEach((effects, key) => {
if (Number(key) >= Number(value)) {
effects.forEach(effectFn => {
if (effectFn !== activeEffect)
effectsToRun.add(effectFn);
});
}
});
let lengthEffects = depsMap.get('length');
lengthEffects && lengthEffects.forEach(effectFn => {
if (effectFn !== activeEffect)
effectsToRun.add(effectFn);
});
}
//执行副作用函数
effectsToRun.forEach(fn => {
if (typeof fn.options.schedule === 'function') {
fn.options.schedule(fn);
}
else {
fn();
}
});
return true;
}
/**
* 代理对象值,返回响应式数据
* @example
* ```ts
* const obj = reactive({name:'张三'});
* obj.name = '李四';
* console.log(obj.name); //李四
* ```
* @param value 对象值
* @param isShadow true为浅代理,false为深代理
* @param isReadonly 是否只读
* @returns { Proxy<T> }
*/
export function reactive(value, isShadow = false, isReadonly = false) {
const proxy = new Proxy(value, {
get(target, p, reciver) {
var _a;
if (p === source)
return target;
//数组原型方法代理
if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(p)) {
return Reflect.get(arrayInstrumentations, p, reciver);
}
//Set.Map 访问器属性size代理
if (target instanceof Map || target instanceof Set) {
if (mutableInstrumentations.hasOwnProperty(p))
return Reflect.get(mutableInstrumentations, p, reciver);
if (p === 'size')
track(target, ITERATE_KEY);
return Reflect.get(target, p, target);
}
//只读对象或者key为symbol时不进行追踪
if (!isReadonly && typeof p !== 'symbol')
track(target, p);
const res = Reflect.get(target, p, reciver);
//浅代理模式直接返回原始值
if (isShadow)
return res;
//非浅代理模式,如果原始值不是基本类型进行响应式包装
if (typeof res === 'object' && res != null) {
const existionProxy = (_a = reactiveMap.get(res)) === null || _a === void 0 ? void 0 : _a.value;
if (existionProxy)
return existionProxy;
return reactive(res, isShadow, isReadonly);
}
//否则返回基本类型值
return res;
},
has(target, p) {
if (!isReadonly)
track(target, p);
return Reflect.has(target, p);
},
ownKeys(target) {
if (!isReadonly)
track(target, Array.isArray(target) ? 'length' : ITERATE_KEY);
return Reflect.ownKeys(target);
},
deleteProperty(target, p) {
if (isReadonly) {
console.log(target, `对象是只读的`);
return false;
}
//判断变量是否不处于对象原型上
const hadKey = Object.prototype.hasOwnProperty.call(target, p);
const res = Reflect.deleteProperty(target, p);
if (res && hadKey) {
trigger(target, p, TriggerType.DELETE);
}
return res;
},
set(target, p, value, reciver) {
if (isReadonly) {
console.log(target, `对象是只读的`);
return false;
}
//取出旧值
const oldValue = Reflect.get(target, p);
//判断操作类型
const type = Array.isArray(target)
? Number(p) < target.length ? TriggerType.SET : TriggerType.ADD
: Object.prototype.hasOwnProperty.call(target, p) ? TriggerType.SET : TriggerType.ADD;
//取原始值
const orgin = toRaw(value);
//非浅代理时设置原始对象而非响应对象
const res = Reflect.set(target, p, orgin, reciver);
if (target === Reflect.get(reciver, source)) {
if (oldValue !== value && (oldValue === oldValue || value === value)) {
trigger(target, p, type, value);
}
}
return res;
}
});
if (typeof value === 'object' && value != null)
reactiveMap.set(value, {
value: proxy,
isShadow,
isReadonly
});
return proxy;
}
/**
* 代理基本类型值,返回响应式数据
* ```ts
* const obj = ref(3);
* obj.value = 4;
* console.log(obj.value); //4
* ```
* @param value 基本类型值
* @param isReadonly 是否只读
* @returns { value: T }
*/
export function ref(value, isReadonly = false) {
const wrapper = {
value
};
//定义变量标识对象为Ref对象
Object.defineProperty(wrapper, '__isRef', {
value: true
});
return reactive(wrapper, false, isReadonly);
}
/**
* 将响应式对象的某键值转为ref
* @example
* ```ts
* const obj = reactive({ a: 1 });
* const a = toRef(obj, 'a');
* a.value = 2;
* console.log(obj.a); //2
* ```
* @param val 响应式对象
* @param key 键值
* @returns Ref
*/
export function toRef(val, key) {
const wrapper = {
get value() {
return val[key];
},
set value(value) {
val[key] = value;
}
};
//定义变量标识对象为Ref对象
Object.defineProperty(wrapper, '__isRef', {
value: true
});
return wrapper;
}
/**
* 将响应式对象的键值全部转换为Ref, 可解构使用
* @example
* ```ts
* const obj = reactive({ a: 1, b: 2 });
* const { a, b } = toRefs(obj);
* a.value = 2;
* console.log(obj.a); //2
* ```
* @param obj 响应式对象
* @returns Refs
*/
export function toRefs(obj) {
const ret = {};
for (const key in obj) {
Reflect.set(ret, key, toRef(obj, key));
}
return ret;
}
/**
* 创建副作用函数
* @example
* ```ts
* const count = ref(0);
* effect(() => {
* console.log(count.value);
* })
* count.value = 1;
* // 打印1
* ```
* @param func 函数
* @param options 配置
* @returns effectFunc
*/
export function effect(func, options = {}) {
const effectFn = function () {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
const res = func();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.childs = [];
effectFn.deps = [];
effectFn.options = options;
effectFn.shouldTrack = true;
if (effectStack.length) {
const parent = effectStack[effectStack.length - 1];
parent.childs.push(effectFn);
}
if (!options.lazy)
effectFn();
return effectFn;
}
/**
* 获取计算属性
* @example
* ```ts
* const count = ref(0);
* const double = computed(() => count.value * 2);
* console.log(double.value); //0
* count.value = 1;
* console.log(double.value); //2
* ```
* @param getter
* @returns computed
*/
export function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
schedule() {
if (!dirty) {
dirty = true;
trigger(obj, 'value', TriggerType.SET, 0);
}
}
});
const obj = {
//访问器属性
get value() {
if (dirty) {
value = effectFn();
dirty = false;
}
track(obj, 'value');
return value;
}
};
return obj;
}
/**
* 监听响应式数据
* @example
* ```ts
* const count = ref(0);
* watch(count, (newVal, oldVal) => {
* console.log(newVal, oldVal);
* })
* count.value = 1;
* // 打印1 0
* ```
* @param source 副作用函数或者响应式对象
* @param cb 数据变化后回调函数
* @param options 配置
*/
export function watch(source, cb, options = {}) {
let getter;
if (typeof source === 'function')
getter = source;
else
getter = () => traverse(source);
let oldValue, newValue, cleanup;
function onInvalidate(fn) {
cleanup = fn;
}
const job = () => {
newValue = effectFn();
if (cleanup)
cleanup();
cb(oldValue, newValue, onInvalidate);
oldValue = newValue;
};
const effectFn = effect(() => getter(), {
lazy: true,
schedule() {
if (options.flush === 'post') {
const p = Promise.resolve();
p.then(job);
}
else {
job();
}
}
});
if (options.immediate)
job();
else
oldValue = effectFn();
}
/**
* 获取原始对象
* @example
* ```ts
* const count = reactive({ a: 1 });
* console.log(toRaw(count)); //{ a: 1 }
* ```
* @param proxy 响应式对象
* @returns 原始对象
*/
export function toRaw(proxy) {
return Reflect.get((typeof proxy === 'object' && proxy !== null) ? proxy : {}, source) || proxy;
}