@whitesev/utils
Version:
一个常用的工具库
242 lines (240 loc) • 5.66 kB
text/typescript
const VueUtils = {
/** 标签 */
ReactiveFlags: {
IS_REACTIVE: Symbol("isReactive"),
},
/**
* 判断是否是对象
* @param value
*/
isObject(value: any) {
return typeof value === "object" && value !== null;
},
/**
* 判断是否是函数
* @param val
*/
isFunction(val: any) {
return typeof val === "function";
},
/**
* 处理对象再次代理,可以直接返回
* @param value
*/
isReactive(value: any) {
return !!(value && value[VueUtils.ReactiveFlags.IS_REACTIVE]);
},
/**
* 判断是否是数组
* @param value
*/
isArray(value: any): boolean {
return Array.isArray(value);
},
};
class ReactiveEffect {
deps: any[] = [];
private active = true;
private fn;
// @ts-ignore
private scheduler;
constructor(fn: Function, scheduler: any) {
this.fn = fn;
this.scheduler = scheduler;
}
run(cb: (activeEffect: any) => void) {
if (!this.active) {
this.fn();
}
try {
if (typeof cb === "function") {
cb(this);
}
return this.fn();
} finally {
if (typeof cb === "function") {
cb(undefined);
}
}
}
}
class RefImpl {
_value;
_isRef = true;
_rawValue;
_vue: Vue;
constructor(vueIns: Vue, rawValue: any) {
this._vue = vueIns;
this._rawValue = rawValue;
this._value = this._vue.toReactive(rawValue);
}
get value() {
return this._value;
}
set value(newValue) {
if (newValue !== this._rawValue) {
this._value = this._vue.toReactive(newValue);
this._rawValue = newValue;
}
}
}
class ObjectRefImpl {
object;
key;
constructor(object: any, key: any) {
this.object = object;
this.key = key;
}
get value() {
return this.object[this.key];
}
set value(newValue) {
this.object[this.key] = newValue;
}
}
export class Vue {
private reactMap = new WeakMap();
private targetMap = new WeakMap();
private activeEffect = undefined as any as ReactiveEffect;
constructor() {
// 将数据转化成响应式的数据,只能做对象的代理
}
/**
* 生成一个被代理的对象
* @param target 需要代理的对象
*/
reactive<T extends object>(target: T): T {
const that = this;
if (!(typeof target === "object" && target !== null)) {
// @ts-ignore
return;
}
if (VueUtils.isReactive(target)) {
return target;
}
let exisProxy = this.reactMap.get(target);
if (exisProxy) {
return exisProxy;
}
const proxy = new Proxy(target, {
get(target, key, receiver) {
if (key === VueUtils.ReactiveFlags.IS_REACTIVE) {
return true;
}
that.track(target, "get", key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
let oldValue = target[key as keyof T];
let result = Reflect.set(target, key, value, receiver);
if (oldValue !== value) {
that.trigger(target, "set", key, oldValue, value);
}
return result;
},
});
that.reactMap.set(target, proxy);
return proxy;
}
/**
* 观察被reactive的对象值改变
* @param source 被观察的对象,这里采用函数返回对象
* @param changeCallBack 值改变的回调
*/
watch<T>(
source: () => T,
changeCallBack: (newValue: T | undefined, oldValue: T | undefined) => void
) {
let getter;
if (VueUtils.isReactive(source)) {
getter = () => this.traversal(source);
} else if (VueUtils.isFunction(source)) {
getter = source;
} else {
return;
}
let oldValue: any;
const job = () => {
const newValue = effect.run((activeEffect) => {
this.activeEffect = activeEffect;
});
changeCallBack(newValue, oldValue);
oldValue = newValue;
};
const effect = new ReactiveEffect(getter, job);
oldValue = effect.run((activeEffect) => {
this.activeEffect = activeEffect;
});
}
toReactive(value: any) {
return VueUtils.isObject(value) ? this.reactive(value) : value;
}
ref(value: any) {
return new RefImpl(this, value);
}
toRef(object: any, key: any) {
return new ObjectRefImpl(object, key);
}
toRefs(object: any) {
const result = VueUtils.isArray(object) ? new Array(object.length) : {};
for (let key in object) {
// @ts-ignore
result[key] = this.toRef(object, key);
}
return result;
}
private trigger(
target: any,
type: string,
key: string | symbol,
oldValue: any,
value: any
) {
const depsMap = this.targetMap.get(target);
if (!depsMap) return;
const effects = depsMap.get(key);
this.triggerEffect(effects, "effects");
}
private triggerEffect(effects: any[], name: string) {
effects &&
effects.forEach((effect) => {
if (effect.scheduler) {
effect.scheduler();
} else {
effect.run();
}
});
}
private track(target: WeakKey, type: string, key: string | symbol) {
if (!this.activeEffect) return;
let depsMap = this.targetMap.get(target);
if (!depsMap) {
this.targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
this.trackEffect(dep);
}
private trackEffect(dep: any) {
if (this.activeEffect) {
let shouldTrack = !dep.has(this.activeEffect);
if (shouldTrack) {
dep.add(this.activeEffect);
this.activeEffect.deps.push(dep);
}
}
}
private traversal(value: any, set = new Set()) {
if (!VueUtils.isObject(value)) return value;
if (set.has(value)) {
return value;
}
set.add(value);
for (let key in value) {
this.traversal(value[key], set);
}
return value;
}
}