UNPKG

@whitesev/utils

Version:

一个常用的工具库

242 lines (240 loc) 5.66 kB
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; } }