Match-id-bae8261fcf2fb4e5c1748e8ce921736fe3bd9ae4
This commit is contained in:
parent
40160ad11b
commit
2f7787c1cf
|
@ -26,8 +26,10 @@ module.exports = {
|
||||||
testEnvironment: 'jest-environment-jsdom-sixteen',
|
testEnvironment: 'jest-environment-jsdom-sixteen',
|
||||||
|
|
||||||
testMatch: [
|
testMatch: [
|
||||||
|
// '<rootDir>/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx',
|
||||||
|
// '<rootDir>/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx',
|
||||||
'<rootDir>/scripts/__tests__/**/*.test.js',
|
'<rootDir>/scripts/__tests__/**/*.test.js',
|
||||||
'<rootDir>/scripts/__tests__/**/*.test.tsx'
|
'<rootDir>/scripts/__tests__/**/*.test.tsx',
|
||||||
],
|
],
|
||||||
|
|
||||||
timers: 'fake',
|
timers: 'fake',
|
||||||
|
|
|
@ -84,7 +84,6 @@ export function isSame(x, y) {
|
||||||
export function getDetailedType(val: any) {
|
export function getDetailedType(val: any) {
|
||||||
if (val === undefined) return 'undefined';
|
if (val === undefined) return 'undefined';
|
||||||
if (val === null) return 'null';
|
if (val === null) return 'null';
|
||||||
if (isCollection(val)) return 'collection';
|
|
||||||
if (isPromise(val)) return 'promise';
|
if (isPromise(val)) return 'promise';
|
||||||
if (isArray(val)) return 'array';
|
if (isArray(val)) return 'array';
|
||||||
if (isWeakMap(val)) return 'weakMap';
|
if (isWeakMap(val)) return 'weakMap';
|
||||||
|
@ -121,7 +120,24 @@ export function resolveMutation(from, to) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'object': {
|
case 'object': {
|
||||||
let keys = Object.keys({ ...from, ...to });
|
if (from._type && from._type === to._type) {
|
||||||
|
if (from._type === 'Map') {
|
||||||
|
const entries = resolveMutation(from.entries, to.entries);
|
||||||
|
return {
|
||||||
|
mutation: entries.items.some(item => item.mutation),
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
entries: entries.items,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from._type === 'Set') {
|
||||||
|
const values = resolveMutation(from.values, to.values);
|
||||||
|
return { mutation: values.items.some(item => item.mutation), from, to, values: values.items };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = Object.keys({ ...from, ...to }).filter(key => key !== '_horizonObserver');
|
||||||
const res = {};
|
const res = {};
|
||||||
let found = false;
|
let found = false;
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
|
@ -142,8 +158,6 @@ export function resolveMutation(from, to) {
|
||||||
return { mutation: found, attributes: res, from, to };
|
return { mutation: found, attributes: res, from, to };
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement collections
|
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
if (from === to) return { mutation: false };
|
if (from === to) return { mutation: false };
|
||||||
|
|
||||||
|
@ -151,3 +165,9 @@ export function resolveMutation(from, to) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function omit(obj, ...attrs) {
|
||||||
|
let res = { ...obj };
|
||||||
|
attrs.forEach(attr => delete res[attr]);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { isDomVNode } from '../../renderer/vnode/VNodeUtils';
|
||||||
|
import { isMap, isSet, isWeakMap, isWeakSet } from '../CommonUtils';
|
||||||
import { getStore, getAllStores } from '../store/StoreHandler';
|
import { getStore, getAllStores } from '../store/StoreHandler';
|
||||||
import { OBSERVED_COMPONENTS } from './constants';
|
import { OBSERVED_COMPONENTS } from './constants';
|
||||||
|
|
||||||
|
@ -24,28 +26,87 @@ function makeStoreSnapshot({ type, data }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// safely serializes variables containing values wrapped in Proxy object
|
// safely serializes variables containing values wrapped in Proxy object
|
||||||
|
function getType(value) {
|
||||||
|
if (!value) return 'nullish';
|
||||||
|
if (value.nativeEvent) return 'event';
|
||||||
|
if (typeof value === 'function') return 'function';
|
||||||
|
if (value.constructor?.name === 'VNode') return 'vnode';
|
||||||
|
if (isWeakMap(value)) return 'weakMap';
|
||||||
|
if (isWeakSet(value)) return 'weakSet';
|
||||||
|
if (isMap(value)) return 'map';
|
||||||
|
if (isSet(value)) return 'set';
|
||||||
|
if (Array.isArray(value)) return 'array';
|
||||||
|
if (typeof value === 'object') return 'object';
|
||||||
|
return 'primitive';
|
||||||
|
}
|
||||||
function makeProxySnapshot(obj) {
|
function makeProxySnapshot(obj) {
|
||||||
|
const type = getType(obj);
|
||||||
let clone;
|
let clone;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!obj) {
|
//NULLISH VALUE
|
||||||
|
if (type === 'nullish') {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
if (obj.nativeEvent) return obj.type + 'Event';
|
//EVENT
|
||||||
if (typeof obj === 'function') {
|
if (type === 'event') return obj.type + 'Event';
|
||||||
|
// FUNCTION
|
||||||
|
if (type === 'function') {
|
||||||
return obj.toString();
|
return obj.toString();
|
||||||
}
|
}
|
||||||
if (Array.isArray(obj)) {
|
// VNODE
|
||||||
|
if (type === 'vnode') {
|
||||||
|
return {
|
||||||
|
_type: 'VNode',
|
||||||
|
id: window['__HORIZON_DEV_HOOK__'].getVnodeId(obj),
|
||||||
|
tag: obj.tag,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// WEAK MAP
|
||||||
|
if (type === 'weakMap') {
|
||||||
|
return {
|
||||||
|
_type: 'WeakMap',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// WEAK SET
|
||||||
|
if (type === 'weakSet') {
|
||||||
|
return {
|
||||||
|
_type: 'WeakSet',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// MAP
|
||||||
|
if (type === 'map') {
|
||||||
|
return {
|
||||||
|
_type: 'Map',
|
||||||
|
entries: Array.from(obj.entries()).map(([key, value]) => ({
|
||||||
|
key: makeProxySnapshot(key),
|
||||||
|
value: makeProxySnapshot(value),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// SET
|
||||||
|
if (type === 'set') {
|
||||||
|
return {
|
||||||
|
_type: 'Set',
|
||||||
|
values: Array.from(obj).map(value => makeProxySnapshot(value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// ARRAY
|
||||||
|
if (type === 'array') {
|
||||||
clone = [];
|
clone = [];
|
||||||
obj.forEach(item => clone.push(makeProxySnapshot(item)));
|
obj.forEach(item => clone.push(makeProxySnapshot(item)));
|
||||||
return clone;
|
return clone;
|
||||||
} else if (typeof obj === 'object') {
|
}
|
||||||
|
// OBJECT
|
||||||
|
if (type === 'object') {
|
||||||
clone = {};
|
clone = {};
|
||||||
Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value)));
|
Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value)));
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
// PRIMITIVE
|
||||||
return obj;
|
return obj;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw console.log('cannot serialize object. ' + err);
|
console.error('cannot serialize object. ', { err, obj, type });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +137,7 @@ function getAffectedComponents() {
|
||||||
const subRes = new Set();
|
const subRes = new Set();
|
||||||
const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values());
|
const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values());
|
||||||
while (process.length) {
|
while (process.length) {
|
||||||
let pivot = process.shift();
|
let pivot = process.shift() as { tag: 'string' };
|
||||||
if (pivot?.tag) subRes.add(pivot);
|
if (pivot?.tag) subRes.add(pivot);
|
||||||
if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item));
|
if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item));
|
||||||
}
|
}
|
||||||
|
@ -117,4 +178,37 @@ window.addEventListener('message', messageEvent => {
|
||||||
const params = data.params;
|
const params = data.params;
|
||||||
action(...params);
|
action(...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// queues store action
|
||||||
|
if (messageEvent.data.payload.type === 'horizonx queue action') {
|
||||||
|
const data = messageEvent.data.payload.data;
|
||||||
|
const store = getStore(data.storeId);
|
||||||
|
if (!store?.[data.action]) return;
|
||||||
|
|
||||||
|
const action = store.$queue?.[data.action];
|
||||||
|
const params = data.params;
|
||||||
|
action(...params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// queues change store state
|
||||||
|
if (messageEvent.data.payload.type === 'horizonx change state') {
|
||||||
|
const data = messageEvent.data.payload;
|
||||||
|
const store = getStore(data.storeId);
|
||||||
|
if (!store) return;
|
||||||
|
let parent = store.$s;
|
||||||
|
if (data.operation === 'edit') {
|
||||||
|
try {
|
||||||
|
const path = messageEvent.data.payload.path;
|
||||||
|
|
||||||
|
while (path.length > 1) {
|
||||||
|
parent = parent[path.pop()];
|
||||||
|
}
|
||||||
|
|
||||||
|
parent[path[0]] = messageEvent.data.payload.value;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO:implement add and delete element
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,6 +31,10 @@ export class HooklessObserver implements IObserver {
|
||||||
this.listeners = this.listeners.filter(item => item != listener);
|
this.listeners = this.listeners.filter(item => item != listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getListeners() {
|
||||||
|
return this.listeners;
|
||||||
|
}
|
||||||
|
|
||||||
setProp(key: string | symbol, mutation: any): void {
|
setProp(key: string | symbol, mutation: any): void {
|
||||||
this.triggerChangeListeners(mutation);
|
this.triggerChangeListeners(mutation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ const proxyMap = new WeakMap();
|
||||||
|
|
||||||
export const hookObserverMap = new WeakMap();
|
export const hookObserverMap = new WeakMap();
|
||||||
|
|
||||||
export function createProxy(rawObj: any, id, isHookObserver = true): any {
|
export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any {
|
||||||
// 不是对象(是原始数据类型)不用代理
|
// 不是对象(是原始数据类型)不用代理
|
||||||
if (!(rawObj && isObject(rawObj))) {
|
if (!(rawObj && isObject(rawObj))) {
|
||||||
return rawObj;
|
return rawObj;
|
||||||
|
@ -56,16 +56,32 @@ export function createProxy(rawObj: any, id, isHookObserver = true): any {
|
||||||
// 创建Proxy
|
// 创建Proxy
|
||||||
let proxyObj;
|
let proxyObj;
|
||||||
if (!isHookObserver) {
|
if (!isHookObserver) {
|
||||||
proxyObj = createObjectProxy(rawObj, true);
|
proxyObj = createObjectProxy(rawObj, true, {
|
||||||
|
current: change => {
|
||||||
|
listener.current(change);
|
||||||
|
},
|
||||||
|
});
|
||||||
} else if (isArray(rawObj)) {
|
} else if (isArray(rawObj)) {
|
||||||
// 数组
|
// 数组
|
||||||
proxyObj = createArrayProxy(rawObj as []);
|
proxyObj = createArrayProxy(rawObj as [], {
|
||||||
|
current: change => {
|
||||||
|
listener.current(change);
|
||||||
|
},
|
||||||
|
});
|
||||||
} else if (isCollection(rawObj)) {
|
} else if (isCollection(rawObj)) {
|
||||||
// 集合
|
// 集合
|
||||||
proxyObj = createCollectionProxy(rawObj);
|
proxyObj = createCollectionProxy(rawObj, true, {
|
||||||
|
current: change => {
|
||||||
|
listener.current(change);
|
||||||
|
},
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 原生对象 或 函数
|
// 原生对象 或 函数
|
||||||
proxyObj = createObjectProxy(rawObj);
|
proxyObj = createObjectProxy(rawObj, false, {
|
||||||
|
current: change => {
|
||||||
|
listener?.current(change);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyMap.set(rawObj, proxyObj);
|
proxyMap.set(rawObj, proxyObj);
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getObserver } from '../ProxyHandler';
|
||||||
|
import { isSame, isValidIntegerKey } from '../../CommonUtils';
|
||||||
|
import { get as objectGet } from './ObjectProxyHandler';
|
||||||
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
|
import { isPanelActive } from '../../devtools';
|
||||||
|
|
||||||
|
export function createArrayProxy(rawObj: any[]): any[] {
|
||||||
|
const handle = {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Proxy(rawObj, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(rawObj: any[], key: string, receiver: any) {
|
||||||
|
if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidIntegerKey(key) || key === 'length') {
|
||||||
|
return objectGet(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(rawObj: any[], key: string, value: any, receiver: any) {
|
||||||
|
const oldValue = rawObj[key];
|
||||||
|
const oldLength = rawObj.length;
|
||||||
|
const newValue = value;
|
||||||
|
|
||||||
|
const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
|
|
||||||
|
const ret = Reflect.set(rawObj, key, newValue, receiver);
|
||||||
|
|
||||||
|
const newLength = rawObj.length;
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : { mutation: true, from: [], to: rawObj };
|
||||||
|
|
||||||
|
if (!isSame(newValue, oldValue)) {
|
||||||
|
// 值不一样,触发监听器
|
||||||
|
if (observer.watchers?.[key]) {
|
||||||
|
observer.watchers[key].forEach(cb => {
|
||||||
|
cb(key, oldValue, newValue, mutation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发属性变化
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldLength !== newLength) {
|
||||||
|
// 触发数组的大小变化
|
||||||
|
observer.setProp('length', mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
|
import { isMap, isWeakMap, isSame } from '../../CommonUtils';
|
||||||
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
|
import { isPanelActive } from '../../devtools';
|
||||||
|
|
||||||
|
const COLLECTION_CHANGE = '_collectionChange';
|
||||||
|
const handler = {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
add,
|
||||||
|
delete: deleteFun,
|
||||||
|
clear,
|
||||||
|
has,
|
||||||
|
entries,
|
||||||
|
forEach,
|
||||||
|
keys,
|
||||||
|
values,
|
||||||
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createCollectionProxy(rawObj: Object, hookObserver = true): Object {
|
||||||
|
const boundHandler = {};
|
||||||
|
Object.entries(handler).forEach(([id, val]) => {
|
||||||
|
boundHandler[id] = (...args: any[]) => {
|
||||||
|
return (val as any)(...args, hookObserver);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return new Proxy(rawObj, { ...boundHandler });
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(rawObj: { size: number }, key: any, receiver: any): any {
|
||||||
|
if (key === 'size') {
|
||||||
|
return size(rawObj);
|
||||||
|
} else if (key === 'get') {
|
||||||
|
return getFun.bind(null, rawObj);
|
||||||
|
} else if (Object.prototype.hasOwnProperty.call(handler, key)) {
|
||||||
|
const value = Reflect.get(handler, key, receiver);
|
||||||
|
return value.bind(null, rawObj);
|
||||||
|
} else if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFun(rawObj: { get: (key: any) => any }, key: any) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
const value = rawObj.get(key);
|
||||||
|
// 对于value也需要进一步代理
|
||||||
|
const valProxy = createProxy(value, hookObserverMap.get(rawObj));
|
||||||
|
|
||||||
|
return valProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map的set方法
|
||||||
|
function set(
|
||||||
|
rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean },
|
||||||
|
key: any,
|
||||||
|
value: any
|
||||||
|
) {
|
||||||
|
const oldValue = rawObj.get(key);
|
||||||
|
const newValue = value;
|
||||||
|
rawObj.set(key, newValue);
|
||||||
|
const valChange = !isSame(newValue, oldValue);
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : { mutation: true, from: null, to: rawObj };
|
||||||
|
|
||||||
|
if (valChange || !rawObj.has(key)) {
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valChange) {
|
||||||
|
if (observer.watchers?.[key]) {
|
||||||
|
observer.watchers[key].forEach(cb => {
|
||||||
|
cb(key, oldValue, newValue, mutation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set的add方法
|
||||||
|
function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object {
|
||||||
|
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
|
if (!rawObj.has(value)) {
|
||||||
|
rawObj.add(value);
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = isPanelActive()
|
||||||
|
? resolveMutation(oldCollection, rawObj)
|
||||||
|
: { mutation: true, from: null, to: rawObj };
|
||||||
|
observer.setProp(value, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function has(rawObj: { has: (string) => boolean }, key: any): boolean {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
return rawObj.has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(rawObj: { size: number; clear: () => void }) {
|
||||||
|
const oldSize = rawObj.size;
|
||||||
|
rawObj.clear();
|
||||||
|
|
||||||
|
if (oldSize > 0) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.allChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) {
|
||||||
|
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
|
if (rawObj.has(key)) {
|
||||||
|
rawObj.delete(key);
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = isPanelActive()
|
||||||
|
? resolveMutation(oldCollection, rawObj)
|
||||||
|
: { mutation: true, from: null, to: rawObj };
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function size(rawObj: { size: number }) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
return rawObj.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.entries(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function forOf(rawObj: {
|
||||||
|
entries: () => { next: () => { value: any; done: boolean } };
|
||||||
|
values: () => { next: () => { value: any; done: boolean } };
|
||||||
|
}) {
|
||||||
|
const isMapType = isMap(rawObj) || isWeakMap(rawObj);
|
||||||
|
const iterator = isMapType ? rawObj.entries() : rawObj.values();
|
||||||
|
return wrapIterator(rawObj, iterator, isMapType);
|
||||||
|
}
|
||||||
|
|
||||||
|
function forEach(
|
||||||
|
rawObj: { forEach: (callback: (value: any, key: any) => void) => void },
|
||||||
|
callback: (valProxy: any, keyProxy: any, rawObj: any) => void
|
||||||
|
) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
rawObj.forEach((value, key) => {
|
||||||
|
const valProxy = createProxy(value, hookObserverMap.get(rawObj));
|
||||||
|
const keyProxy = createProxy(key, hookObserverMap.get(rawObj));
|
||||||
|
// 最后一个参数要返回代理对象
|
||||||
|
return callback(valProxy, keyProxy, rawObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, isPair = false) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const hookObserver = hookObserverMap.get(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
const { value, done } = rawIt.next();
|
||||||
|
if (done) {
|
||||||
|
return { value: createProxy(value, hookObserver), done };
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
|
||||||
|
let newVal;
|
||||||
|
if (isPair) {
|
||||||
|
newVal = [createProxy(value[0], hookObserver), createProxy(value[1], hookObserver)];
|
||||||
|
} else {
|
||||||
|
newVal = createProxy(value, hookObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { value: newVal, done };
|
||||||
|
},
|
||||||
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { isSame, resolveMutation } from '../../CommonUtils';
|
||||||
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
|
import { OBSERVER_KEY } from '../../Constants';
|
||||||
|
import { isPanelActive } from '../../devtools';
|
||||||
|
|
||||||
|
export function createObjectProxy<T extends object>(rawObj: T, singleLevel = false): ProxyHandler<T> {
|
||||||
|
const proxy = new Proxy(rawObj, {
|
||||||
|
get: (...args) => get(...args, singleLevel),
|
||||||
|
set,
|
||||||
|
});
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any {
|
||||||
|
// The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep.
|
||||||
|
if (key === OBSERVER_KEY) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
if (key === 'watch') {
|
||||||
|
return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return observer.addListener.bind(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return observer.removeListener.bind(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
const value = Reflect.get(rawObj, key, receiver);
|
||||||
|
|
||||||
|
// 对于prototype不做代理
|
||||||
|
if (key !== 'prototype') {
|
||||||
|
// 对于value也需要进一步代理
|
||||||
|
const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj));
|
||||||
|
|
||||||
|
return valProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set(rawObj: object, key: string, value: any, receiver: any): boolean {
|
||||||
|
const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
const oldValue = rawObj[key];
|
||||||
|
const newValue = value;
|
||||||
|
|
||||||
|
const ret = Reflect.set(rawObj, key, newValue, receiver);
|
||||||
|
const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : { mutation: true, from: null, to: rawObj };
|
||||||
|
|
||||||
|
if (!isSame(newValue, oldValue)) {
|
||||||
|
if (observer.watchers?.[key]) {
|
||||||
|
observer.watchers[key].forEach(cb => {
|
||||||
|
cb(key, oldValue, newValue, mutation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -13,43 +13,112 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getObserver } from '../ProxyHandler';
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
import { isSame, isValidIntegerKey } from '../../CommonUtils';
|
import { isSame, isValidIntegerKey } from '../../CommonUtils';
|
||||||
import { get as objectGet } from './ObjectProxyHandler';
|
|
||||||
import { resolveMutation } from '../../CommonUtils';
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
import { isPanelActive } from '../../devtools';
|
import { isPanelActive } from '../../devtools';
|
||||||
|
import { OBSERVER_KEY } from '../../Constants';
|
||||||
|
|
||||||
|
export function createArrayProxy(rawObj: any[], listener: { current: (...args) => any }): any[] {
|
||||||
|
let listeners = [] as ((...args) => void)[];
|
||||||
|
|
||||||
|
function objectGet(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any {
|
||||||
|
// The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep.
|
||||||
|
if (key === OBSERVER_KEY) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
if (key === 'watch') {
|
||||||
|
return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners.push(listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners = listeners.filter(item => item != listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
const value = Reflect.get(rawObj, key, receiver);
|
||||||
|
|
||||||
|
// 对于prototype不做代理
|
||||||
|
if (key !== 'prototype') {
|
||||||
|
// 对于value也需要进一步代理
|
||||||
|
const valProxy = singleLevel
|
||||||
|
? value
|
||||||
|
: createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [key]: change.mutation.from },
|
||||||
|
{ ...rawObj, [key]: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current(mutation);
|
||||||
|
listeners.forEach(lst => lst(mutation));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return valProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(rawObj: any[], key: string, receiver: any) {
|
||||||
|
if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidIntegerKey(key) || key === 'length') {
|
||||||
|
return objectGet(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
export function createArrayProxy(rawObj: any[]): any[] {
|
|
||||||
const handle = {
|
const handle = {
|
||||||
get,
|
get,
|
||||||
set,
|
set,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getObserver(rawObj).addListener(change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
listener.current(change);
|
||||||
|
listeners.forEach(lst => lst(change));
|
||||||
|
});
|
||||||
|
|
||||||
return new Proxy(rawObj, handle);
|
return new Proxy(rawObj, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(rawObj: any[], key: string, receiver: any) {
|
|
||||||
if (key === 'watch') {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
|
|
||||||
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
|
||||||
if (!observer.watchers[prop]) {
|
|
||||||
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
|
||||||
}
|
|
||||||
observer.watchers[prop].push(handler);
|
|
||||||
return () => {
|
|
||||||
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidIntegerKey(key) || key === 'length') {
|
|
||||||
return objectGet(rawObj, key, receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.get(rawObj, key, receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
function set(rawObj: any[], key: string, value: any, receiver: any) {
|
function set(rawObj: any[], key: string, value: any, receiver: any) {
|
||||||
const oldValue = rawObj[key];
|
const oldValue = rawObj[key];
|
||||||
const oldLength = rawObj.length;
|
const oldLength = rawObj.length;
|
||||||
|
@ -62,7 +131,7 @@ function set(rawObj: any[], key: string, value: any, receiver: any) {
|
||||||
const newLength = rawObj.length;
|
const newLength = rawObj.length;
|
||||||
const observer = getObserver(rawObj);
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : { mutation: true, from: [], to: rawObj };
|
const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj);
|
||||||
|
|
||||||
if (!isSame(newValue, oldValue)) {
|
if (!isSame(newValue, oldValue)) {
|
||||||
// 值不一样,触发监听器
|
// 值不一样,触发监听器
|
||||||
|
|
|
@ -13,223 +13,25 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
import { isWeakMap, isWeakSet, isSet } from '../../CommonUtils';
|
||||||
import { isMap, isWeakMap, isSame } from '../../CommonUtils';
|
import { createWeakSetProxy } from './WeakSetProxy';
|
||||||
import { resolveMutation } from '../../CommonUtils';
|
import { createSetProxy } from './SetProxy';
|
||||||
import { isPanelActive } from '../../devtools';
|
import { createWeakMapProxy } from './WeakMapProxy';
|
||||||
|
import { createMapProxy } from './MapProxy';
|
||||||
|
|
||||||
const COLLECTION_CHANGE = '_collectionChange';
|
export function createCollectionProxy(
|
||||||
const handler = {
|
rawObj: Object,
|
||||||
get,
|
hookObserver = true,
|
||||||
set,
|
listener: { current: (...args) => any }
|
||||||
add,
|
): Object {
|
||||||
delete: deleteFun,
|
if (isWeakSet(rawObj)) {
|
||||||
clear,
|
return createWeakSetProxy(rawObj, hookObserver, listener);
|
||||||
has,
|
|
||||||
entries,
|
|
||||||
forEach,
|
|
||||||
keys,
|
|
||||||
values,
|
|
||||||
// 判断Symbol类型,兼容IE
|
|
||||||
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createCollectionProxy(rawObj: Object, hookObserver = true): Object {
|
|
||||||
const boundHandler = {};
|
|
||||||
Object.entries(handler).forEach(([id, val]) => {
|
|
||||||
boundHandler[id] = (...args: any[]) => {
|
|
||||||
return (val as any)(...args, hookObserver);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return new Proxy(rawObj, { ...boundHandler });
|
|
||||||
}
|
|
||||||
|
|
||||||
function get(rawObj: { size: number }, key: any, receiver: any): any {
|
|
||||||
if (key === 'size') {
|
|
||||||
return size(rawObj);
|
|
||||||
} else if (key === 'get') {
|
|
||||||
return getFun.bind(null, rawObj);
|
|
||||||
} else if (Object.prototype.hasOwnProperty.call(handler, key)) {
|
|
||||||
const value = Reflect.get(handler, key, receiver);
|
|
||||||
return value.bind(null, rawObj);
|
|
||||||
} else if (key === 'watch') {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
|
|
||||||
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
|
||||||
if (!observer.watchers[prop]) {
|
|
||||||
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
|
||||||
}
|
|
||||||
observer.watchers[prop].push(handler);
|
|
||||||
return () => {
|
|
||||||
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
if (isSet(rawObj)) {
|
||||||
return Reflect.get(rawObj, key, receiver);
|
return createSetProxy(rawObj, hookObserver, listener);
|
||||||
}
|
|
||||||
|
|
||||||
function getFun(rawObj: { get: (key: any) => any }, key: any) {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
observer.useProp(key);
|
|
||||||
|
|
||||||
const value = rawObj.get(key);
|
|
||||||
// 对于value也需要进一步代理
|
|
||||||
const valProxy = createProxy(value, hookObserverMap.get(rawObj));
|
|
||||||
|
|
||||||
return valProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map的set方法
|
|
||||||
function set(
|
|
||||||
rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean },
|
|
||||||
key: any,
|
|
||||||
value: any
|
|
||||||
) {
|
|
||||||
const oldValue = rawObj.get(key);
|
|
||||||
const newValue = value;
|
|
||||||
rawObj.set(key, newValue);
|
|
||||||
const valChange = !isSame(newValue, oldValue);
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
|
|
||||||
const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : { mutation: true, from: null, to: rawObj };
|
|
||||||
|
|
||||||
if (valChange || !rawObj.has(key)) {
|
|
||||||
observer.setProp(COLLECTION_CHANGE, mutation);
|
|
||||||
}
|
}
|
||||||
|
if (isWeakMap(rawObj)) {
|
||||||
if (valChange) {
|
return createWeakMapProxy(rawObj, hookObserver, listener);
|
||||||
if (observer.watchers?.[key]) {
|
|
||||||
observer.watchers[key].forEach(cb => {
|
|
||||||
cb(key, oldValue, newValue, mutation);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
observer.setProp(key, mutation);
|
|
||||||
}
|
}
|
||||||
|
return createMapProxy(rawObj, hookObserver, listener);
|
||||||
return rawObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set的add方法
|
|
||||||
function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object {
|
|
||||||
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
|
||||||
if (!rawObj.has(value)) {
|
|
||||||
rawObj.add(value);
|
|
||||||
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
const mutation = isPanelActive()
|
|
||||||
? resolveMutation(oldCollection, rawObj)
|
|
||||||
: { mutation: true, from: null, to: rawObj };
|
|
||||||
observer.setProp(value, mutation);
|
|
||||||
observer.setProp(COLLECTION_CHANGE, mutation);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rawObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function has(rawObj: { has: (string) => boolean }, key: any): boolean {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
observer.useProp(key);
|
|
||||||
|
|
||||||
return rawObj.has(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear(rawObj: { size: number; clear: () => void }) {
|
|
||||||
const oldSize = rawObj.size;
|
|
||||||
rawObj.clear();
|
|
||||||
|
|
||||||
if (oldSize > 0) {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
observer.allChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) {
|
|
||||||
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
|
||||||
if (rawObj.has(key)) {
|
|
||||||
rawObj.delete(key);
|
|
||||||
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
const mutation = isPanelActive()
|
|
||||||
? resolveMutation(oldCollection, rawObj)
|
|
||||||
: { mutation: true, from: null, to: rawObj };
|
|
||||||
observer.setProp(key, mutation);
|
|
||||||
observer.setProp(COLLECTION_CHANGE, mutation);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function size(rawObj: { size: number }) {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
observer.useProp(COLLECTION_CHANGE);
|
|
||||||
return rawObj.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
|
|
||||||
return wrapIterator(rawObj, rawObj.keys());
|
|
||||||
}
|
|
||||||
|
|
||||||
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
|
|
||||||
return wrapIterator(rawObj, rawObj.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
|
|
||||||
return wrapIterator(rawObj, rawObj.entries(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function forOf(rawObj: {
|
|
||||||
entries: () => { next: () => { value: any; done: boolean } };
|
|
||||||
values: () => { next: () => { value: any; done: boolean } };
|
|
||||||
}) {
|
|
||||||
const isMapType = isMap(rawObj) || isWeakMap(rawObj);
|
|
||||||
const iterator = isMapType ? rawObj.entries() : rawObj.values();
|
|
||||||
return wrapIterator(rawObj, iterator, isMapType);
|
|
||||||
}
|
|
||||||
|
|
||||||
function forEach(
|
|
||||||
rawObj: { forEach: (callback: (value: any, key: any) => void) => void },
|
|
||||||
callback: (valProxy: any, keyProxy: any, rawObj: any) => void
|
|
||||||
) {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
observer.useProp(COLLECTION_CHANGE);
|
|
||||||
rawObj.forEach((value, key) => {
|
|
||||||
const valProxy = createProxy(value, hookObserverMap.get(rawObj));
|
|
||||||
const keyProxy = createProxy(key, hookObserverMap.get(rawObj));
|
|
||||||
// 最后一个参数要返回代理对象
|
|
||||||
return callback(valProxy, keyProxy, rawObj);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, isPair = false) {
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
const hookObserver = hookObserverMap.get(rawObj);
|
|
||||||
observer.useProp(COLLECTION_CHANGE);
|
|
||||||
|
|
||||||
return {
|
|
||||||
next() {
|
|
||||||
const { value, done } = rawIt.next();
|
|
||||||
if (done) {
|
|
||||||
return { value: createProxy(value, hookObserver), done };
|
|
||||||
}
|
|
||||||
|
|
||||||
observer.useProp(COLLECTION_CHANGE);
|
|
||||||
|
|
||||||
let newVal;
|
|
||||||
if (isPair) {
|
|
||||||
newVal = [createProxy(value[0], hookObserver), createProxy(value[1], hookObserver)];
|
|
||||||
} else {
|
|
||||||
newVal = createProxy(value, hookObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { value: newVal, done };
|
|
||||||
},
|
|
||||||
// 判断Symbol类型,兼容IE
|
|
||||||
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() {
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,386 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
|
import { isSame } from '../../CommonUtils';
|
||||||
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
|
import { isPanelActive } from '../../devtools';
|
||||||
|
|
||||||
|
const COLLECTION_CHANGE = '_collectionChange';
|
||||||
|
|
||||||
|
export function createMapProxy(rawObj: Object, hookObserver = true, listener: { current: (...args) => any }): Object {
|
||||||
|
let listeners: ((mutation) => {})[] = [];
|
||||||
|
let oldData: [any, any][] = [];
|
||||||
|
let proxies = new Map();
|
||||||
|
|
||||||
|
function get(rawObj: { size: number }, key: any, receiver: any): any {
|
||||||
|
if (key === 'size') {
|
||||||
|
return size(rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'get') {
|
||||||
|
return getFun.bind(null, rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(handler, key)) {
|
||||||
|
const value = Reflect.get(handler, key, receiver);
|
||||||
|
return value.bind(null, rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners.push(listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners = listeners.filter(item => item != listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) {
|
||||||
|
const keyProxy = rawObj.has(key) ? key : proxies.get(key);
|
||||||
|
if (!keyProxy) return;
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(key);
|
||||||
|
const value = rawObj.get(keyProxy);
|
||||||
|
|
||||||
|
// 对于value也需要进一步代理
|
||||||
|
const valProxy = createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [key]: change.mutation.from },
|
||||||
|
{ ...rawObj, [key]: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return valProxy;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Map的set方法
|
||||||
|
function set(
|
||||||
|
rawObj: {
|
||||||
|
get: (key: any) => any;
|
||||||
|
set: (key: any, value: any) => any;
|
||||||
|
has: (key: any) => boolean;
|
||||||
|
entries: () => [any, any][];
|
||||||
|
},
|
||||||
|
key: any,
|
||||||
|
value: any
|
||||||
|
) {
|
||||||
|
if (rawObj.has(key) || rawObj.has(proxies.get(key))) {
|
||||||
|
// VALUE CHANGE (whole value for selected key is changed)
|
||||||
|
const oldValue = rawObj.get(proxies.get(key));
|
||||||
|
if (isSame(value, oldValue)) return;
|
||||||
|
rawObj.set(proxies.get(key), value);
|
||||||
|
const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : resolveMutation(null, rawObj);
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
|
||||||
|
if (observer.watchers?.[key]) {
|
||||||
|
observer.watchers[key].forEach(cb => {
|
||||||
|
cb(key, oldValue, value, mutation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
oldData = [...Array.from(rawObj.entries())];
|
||||||
|
} else {
|
||||||
|
// NEW VALUE
|
||||||
|
const keyProxy = createProxy(key, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
// KEY CHANGE
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, ['_keyChange']: change.mutation.from },
|
||||||
|
{ ...rawObj, ['_keyChange']: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
proxies.set(key, keyProxy);
|
||||||
|
|
||||||
|
rawObj.set(keyProxy, value);
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = resolveMutation(
|
||||||
|
{
|
||||||
|
_type: 'Map',
|
||||||
|
entries: oldData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_type: 'Map',
|
||||||
|
entries: Array.from(rawObj.entries()),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
|
||||||
|
if (observer.watchers?.[key]) {
|
||||||
|
observer.watchers[key].forEach(cb => {
|
||||||
|
cb(key, null, value, mutation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
oldData = [...Array.from(rawObj.entries())];
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function has(rawObj: { has: (any) => boolean }, key: any): boolean {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(key);
|
||||||
|
if (rawObj.has(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return proxies.has(key);
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function clear(rawObj: { size: number; clear: () => void; entries: () => [any, any][] }) {
|
||||||
|
const oldSize = rawObj.size;
|
||||||
|
rawObj.clear();
|
||||||
|
|
||||||
|
if (oldSize > 0) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.allChange();
|
||||||
|
oldData = [...Array.from(rawObj.entries())];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function deleteFun(
|
||||||
|
rawObj: { has: (key: any) => boolean; delete: (key: any) => void; entries: () => [any, any][] },
|
||||||
|
key: any
|
||||||
|
) {
|
||||||
|
if (rawObj.has(key) || proxies.has(key)) {
|
||||||
|
rawObj.delete(key || proxies.get(key));
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = resolveMutation(
|
||||||
|
{
|
||||||
|
_type: 'Map',
|
||||||
|
entries: oldData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_type: 'Map',
|
||||||
|
entries: Array.from(rawObj.entries()),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
|
||||||
|
oldData = [...Array.from(rawObj.entries())];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function size(rawObj: { size: number }) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
return rawObj.size;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.keys(), 'keys');
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.values(), 'values');
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.entries(), 'entries');
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function forOf(rawObj: {
|
||||||
|
entries: () => { next: () => { value: any; done: boolean } };
|
||||||
|
values: () => { next: () => { value: any; done: boolean } };
|
||||||
|
}) {
|
||||||
|
return wrapIterator(rawObj, rawObj.entries(), 'entries');
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function forEach(
|
||||||
|
rawObj: { forEach: (callback: (value: any, key: any) => void) => void },
|
||||||
|
callback: (valProxy: any, keyProxy: any, rawObj: any) => void
|
||||||
|
) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
rawObj.forEach((value, key) => {
|
||||||
|
const keyProxy = createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
//KEY ATTRIBUTES CHANGED
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, ['_keyChange']: change.mutation.from },
|
||||||
|
{ ...rawObj, ['_keyChange']: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const valProxy = createProxy(key, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
// VALUE ATTRIBUTE CHANGED
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, key: change.mutation.from },
|
||||||
|
{ ...rawObj, key: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 最后一个参数要返回代理对象
|
||||||
|
return callback(keyProxy, valProxy, rawObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, type) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const hookObserver = hookObserverMap.get(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
const { value, done } = rawIt.next();
|
||||||
|
if (done) {
|
||||||
|
return {
|
||||||
|
value: createProxy(value, hookObserver, {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [value]: change.mutation.from },
|
||||||
|
{ ...rawObj, [value]: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
done,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
let newVal;
|
||||||
|
if (type === 'entries') {
|
||||||
|
//ENTRY CHANGED
|
||||||
|
newVal = [
|
||||||
|
createProxy(value[0], hookObserver, {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, ['itemChange']: { key: change.mutation.from, value: value[1] } },
|
||||||
|
{ ...rawObj, ['itemChange']: { key: change.mutation.to, value: value[1] } }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
createProxy(value[1], hookObserver, {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, item: { key: value[0], value: change.mutation.from } },
|
||||||
|
{ ...rawObj, item: { key: value[0], value: change.mutation.to } }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// SINGLE VALUE CHANGED
|
||||||
|
newVal = createProxy(value, hookObserver, {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.from },
|
||||||
|
{ ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { value: newVal, done };
|
||||||
|
},
|
||||||
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const handler = {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
delete: deleteFun,
|
||||||
|
clear,
|
||||||
|
has,
|
||||||
|
entries,
|
||||||
|
forEach,
|
||||||
|
keys,
|
||||||
|
values,
|
||||||
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf,
|
||||||
|
};
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const boundHandler = {};
|
||||||
|
Object.entries(handler).forEach(([id, val]) => {
|
||||||
|
boundHandler[id] = (...args: any[]) => {
|
||||||
|
return (val as any)(...args, hookObserver);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
getObserver(rawObj).addListener(change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
listener.current(change);
|
||||||
|
listeners.forEach(lst => lst(change));
|
||||||
|
});
|
||||||
|
return new Proxy(rawObj, { ...boundHandler });
|
||||||
|
}
|
|
@ -18,70 +18,97 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
import { OBSERVER_KEY } from '../../Constants';
|
import { OBSERVER_KEY } from '../../Constants';
|
||||||
import { isPanelActive } from '../../devtools';
|
import { isPanelActive } from '../../devtools';
|
||||||
|
|
||||||
export function createObjectProxy<T extends object>(rawObj: T, singleLevel = false): ProxyHandler<T> {
|
export function createObjectProxy<T extends object>(
|
||||||
|
rawObj: T,
|
||||||
|
singleLevel = false,
|
||||||
|
listener: { current: (...args) => any }
|
||||||
|
): ProxyHandler<T> {
|
||||||
|
let listeners = [] as ((...args) => void)[];
|
||||||
|
|
||||||
|
function get(rawObj: object, key: string | symbol, receiver: any): any {
|
||||||
|
// The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep.
|
||||||
|
if (key === OBSERVER_KEY) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
if (key === 'watch') {
|
||||||
|
return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners.push(listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners = listeners.filter(item => item != listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
const value = Reflect.get(rawObj, key, receiver);
|
||||||
|
|
||||||
|
// 对于prototype不做代理
|
||||||
|
if (key !== 'prototype') {
|
||||||
|
// 对于value也需要进一步代理
|
||||||
|
const valProxy = singleLevel
|
||||||
|
? value
|
||||||
|
: createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [key]: change.mutation.from },
|
||||||
|
{ ...rawObj, [key]: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return valProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
const proxy = new Proxy(rawObj, {
|
const proxy = new Proxy(rawObj, {
|
||||||
get: (...args) => get(...args, singleLevel),
|
get,
|
||||||
set,
|
set,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getObserver(rawObj).addListener(change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
listener.current(change);
|
||||||
|
listeners.forEach(lst => lst(change));
|
||||||
|
});
|
||||||
|
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any {
|
function set(rawObj: object, key: string, value: any, receiver: any): boolean {
|
||||||
// The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep.
|
|
||||||
if (key === OBSERVER_KEY) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const observer = getObserver(rawObj);
|
|
||||||
|
|
||||||
if (key === 'watch') {
|
|
||||||
return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
|
||||||
if (!observer.watchers[prop]) {
|
|
||||||
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
|
||||||
}
|
|
||||||
observer.watchers[prop].push(handler);
|
|
||||||
return () => {
|
|
||||||
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'addListener') {
|
|
||||||
return observer.addListener.bind(observer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'removeListener') {
|
|
||||||
return observer.removeListener.bind(observer);
|
|
||||||
}
|
|
||||||
|
|
||||||
observer.useProp(key);
|
|
||||||
|
|
||||||
const value = Reflect.get(rawObj, key, receiver);
|
|
||||||
|
|
||||||
// 对于prototype不做代理
|
|
||||||
if (key !== 'prototype') {
|
|
||||||
// 对于value也需要进一步代理
|
|
||||||
const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj));
|
|
||||||
|
|
||||||
return valProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function set(rawObj: object, key: string, value: any, receiver: any): boolean {
|
|
||||||
const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
const observer = getObserver(rawObj);
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
if (value && key == 'removeListener') {
|
|
||||||
observer.removeListener(value);
|
|
||||||
}
|
|
||||||
const oldValue = rawObj[key];
|
const oldValue = rawObj[key];
|
||||||
const newValue = value;
|
const newValue = value;
|
||||||
|
|
||||||
const ret = Reflect.set(rawObj, key, newValue, receiver);
|
const ret = Reflect.set(rawObj, key, newValue, receiver);
|
||||||
const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : { mutation: true, from: null, to: rawObj };
|
const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj);
|
||||||
|
|
||||||
if (!isSame(newValue, oldValue)) {
|
if (!isSame(newValue, oldValue)) {
|
||||||
if (observer.watchers?.[key]) {
|
if (observer.watchers?.[key]) {
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
|
|
||||||
|
const COLLECTION_CHANGE = '_collectionChange';
|
||||||
|
|
||||||
|
export function createSetProxy<T extends object>(
|
||||||
|
rawObj: T,
|
||||||
|
hookObserver = true,
|
||||||
|
listener: { current: (...args) => any }
|
||||||
|
): ProxyHandler<T> {
|
||||||
|
let listeners: ((mutation) => {})[] = [];
|
||||||
|
let proxies = new WeakMap();
|
||||||
|
|
||||||
|
function get(rawObj: { size: number }, key: any, receiver: any): any {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(handler, key)) {
|
||||||
|
const value = Reflect.get(handler, key, receiver);
|
||||||
|
return value.bind(null, rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'size') {
|
||||||
|
return size(rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners.push(listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners = listeners.filter(item => item != listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Set的add方法
|
||||||
|
function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object {
|
||||||
|
if (!rawObj.has(proxies.get(value))) {
|
||||||
|
const proxy = createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, valueChange: change.mutation.from },
|
||||||
|
{ ...rawObj, valueChange: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({
|
||||||
|
...change,
|
||||||
|
mutation,
|
||||||
|
});
|
||||||
|
listeners.forEach(lst =>
|
||||||
|
lst({
|
||||||
|
...change,
|
||||||
|
mutation,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const oldValues = Array.from(rawObj.values());
|
||||||
|
|
||||||
|
proxies.set(value, proxy);
|
||||||
|
|
||||||
|
rawObj.add(proxies.get(value));
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = resolveMutation(
|
||||||
|
{
|
||||||
|
_type: 'Set',
|
||||||
|
values: oldValues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_type: 'Set',
|
||||||
|
values: Array.from(rawObj.values()),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
observer.setProp(value, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function has(rawObj: { has: (string) => boolean }, value: any): boolean {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(value);
|
||||||
|
|
||||||
|
return rawObj.has(proxies.get(value));
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function deleteFun(
|
||||||
|
rawObj: { has: (key: any) => boolean; delete: (value: any) => void; values: () => any[] },
|
||||||
|
value: any
|
||||||
|
) {
|
||||||
|
const val = rawObj.has(proxies.get(value)) ? proxies.get(value) : value;
|
||||||
|
if (rawObj.has(val)) {
|
||||||
|
const oldValues = Array.from(rawObj.values());
|
||||||
|
rawObj.delete(val);
|
||||||
|
|
||||||
|
proxies.delete(value);
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = resolveMutation(
|
||||||
|
{
|
||||||
|
_type: 'Set',
|
||||||
|
values: oldValues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_type: 'Set',
|
||||||
|
values: Array.from(rawObj.values()),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
observer.setProp(value, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function clear(rawObj: { size: number; clear: () => void }) {
|
||||||
|
const oldSize = rawObj.size;
|
||||||
|
rawObj.clear();
|
||||||
|
|
||||||
|
if (oldSize > 0) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.allChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function size(rawObj: { size: number }) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
return rawObj.size;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
|
||||||
|
return wrapIterator(rawObj, rawObj.entries());
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const hookObserver = hookObserverMap.get(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
const currentListener = {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, valueChange: change.mutation.from },
|
||||||
|
{ ...rawObj, valueChange: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({
|
||||||
|
...change,
|
||||||
|
mutation,
|
||||||
|
});
|
||||||
|
listeners.forEach(lst =>
|
||||||
|
lst({
|
||||||
|
...change,
|
||||||
|
mutation,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { value, done } = rawIt.next();
|
||||||
|
if (done) {
|
||||||
|
return { value: createProxy(value, hookObserver, currentListener), done };
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
|
||||||
|
let newVal;
|
||||||
|
newVal = createProxy(value, hookObserver, currentListener);
|
||||||
|
|
||||||
|
return { value: newVal, done };
|
||||||
|
},
|
||||||
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function forOf(rawObj: {
|
||||||
|
entries: () => { next: () => { value: any; done: boolean } };
|
||||||
|
values: () => { next: () => { value: any; done: boolean } };
|
||||||
|
}) {
|
||||||
|
const iterator = rawObj.values();
|
||||||
|
return wrapIterator(rawObj, iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
function forEach(
|
||||||
|
rawObj: { forEach: (callback: (value: any, key: any) => void) => void },
|
||||||
|
callback: (valProxy: any, keyProxy: any, rawObj: any) => void
|
||||||
|
) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(COLLECTION_CHANGE);
|
||||||
|
rawObj.forEach((value, key) => {
|
||||||
|
const currentListener = {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, valueChange: change.mutation.from },
|
||||||
|
{ ...rawObj, valueChange: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({
|
||||||
|
...change,
|
||||||
|
mutation,
|
||||||
|
});
|
||||||
|
listeners.forEach(lst =>
|
||||||
|
lst({
|
||||||
|
...change,
|
||||||
|
mutation,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const valProxy = createProxy(value, hookObserverMap.get(rawObj), currentListener);
|
||||||
|
const keyProxy = createProxy(key, hookObserverMap.get(rawObj), currentListener);
|
||||||
|
// 最后一个参数要返回代理对象
|
||||||
|
return callback(valProxy, keyProxy, rawObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const handler = {
|
||||||
|
get,
|
||||||
|
add,
|
||||||
|
delete: deleteFun,
|
||||||
|
has,
|
||||||
|
clear,
|
||||||
|
forEach,
|
||||||
|
forOf,
|
||||||
|
entries,
|
||||||
|
keys,
|
||||||
|
values,
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf,
|
||||||
|
};
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
getObserver(rawObj).addListener(change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
listener.current(change);
|
||||||
|
listeners.forEach(lst => lst(change));
|
||||||
|
});
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const boundHandler = {};
|
||||||
|
Object.entries(handler).forEach(([id, val]) => {
|
||||||
|
boundHandler[id] = (...args: any[]) => {
|
||||||
|
return (val as any)(...args, hookObserver);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return new Proxy(rawObj, { ...boundHandler });
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
|
import { isSame } from '../../CommonUtils';
|
||||||
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
|
import { isPanelActive } from '../../devtools';
|
||||||
|
|
||||||
|
const COLLECTION_CHANGE = '_collectionChange';
|
||||||
|
|
||||||
|
export function createWeakMapProxy(
|
||||||
|
rawObj: Object,
|
||||||
|
hookObserver = true,
|
||||||
|
listener: { current: (...args) => any }
|
||||||
|
): Object {
|
||||||
|
let listeners: ((mutation) => {})[] = [];
|
||||||
|
|
||||||
|
function get(rawObj: { size: number }, key: any, receiver: any): any {
|
||||||
|
if (key === 'get') {
|
||||||
|
return getFun.bind(null, rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(handler, key)) {
|
||||||
|
const value = Reflect.get(handler, key, receiver);
|
||||||
|
return value.bind(null, rawObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners.push(listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners = listeners.filter(item => item != listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFun(rawObj: { get: (key: any) => any }, key: any) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
const value = rawObj.get(key);
|
||||||
|
// 对于value也需要进一步代理
|
||||||
|
const valProxy = createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [key]: change.mutation.from },
|
||||||
|
{ ...rawObj, [key]: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return valProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map的set方法
|
||||||
|
function set(
|
||||||
|
rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean },
|
||||||
|
key: any,
|
||||||
|
value: any
|
||||||
|
) {
|
||||||
|
const oldValue = rawObj.get(key);
|
||||||
|
const newValue = value;
|
||||||
|
rawObj.set(key, newValue);
|
||||||
|
const valChange = !isSame(newValue, oldValue);
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : resolveMutation(null, rawObj);
|
||||||
|
|
||||||
|
if (valChange || !rawObj.has(key)) {
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valChange) {
|
||||||
|
if (observer.watchers?.[key]) {
|
||||||
|
observer.watchers[key].forEach(cb => {
|
||||||
|
cb(key, oldValue, newValue, mutation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Set的add方法
|
||||||
|
function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object {
|
||||||
|
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
|
if (!rawObj.has(value)) {
|
||||||
|
rawObj.add(value);
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = isPanelActive()
|
||||||
|
? resolveMutation(oldCollection, rawObj)
|
||||||
|
: { mutation: true, from: null, to: rawObj };
|
||||||
|
observer.setProp(value, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function has(rawObj: { has: (string) => boolean }, key: any): boolean {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(key);
|
||||||
|
|
||||||
|
return rawObj.has(key);
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function clear(rawObj: { size: number; clear: () => void }) {
|
||||||
|
const oldSize = rawObj.size;
|
||||||
|
rawObj.clear();
|
||||||
|
|
||||||
|
if (oldSize > 0) {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.allChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) {
|
||||||
|
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||||
|
if (rawObj.has(key)) {
|
||||||
|
rawObj.delete(key);
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = isPanelActive()
|
||||||
|
? resolveMutation(oldCollection, rawObj)
|
||||||
|
: { mutation: true, from: null, to: rawObj };
|
||||||
|
observer.setProp(key, mutation);
|
||||||
|
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const handler = {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
add,
|
||||||
|
delete: deleteFun,
|
||||||
|
clear,
|
||||||
|
has,
|
||||||
|
};
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
getObserver(rawObj).addListener(change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
listener.current(change);
|
||||||
|
listeners.forEach(lst => lst(change));
|
||||||
|
});
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const boundHandler = {};
|
||||||
|
Object.entries(handler).forEach(([id, val]) => {
|
||||||
|
boundHandler[id] = (...args: any[]) => {
|
||||||
|
return (val as any)(...args, hookObserver);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return new Proxy(rawObj, { ...boundHandler });
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { resolveMutation } from '../../CommonUtils';
|
||||||
|
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||||
|
|
||||||
|
export function createWeakSetProxy<T extends object>(
|
||||||
|
rawObj: T,
|
||||||
|
hookObserver = true,
|
||||||
|
listener: { current: (...args) => any }
|
||||||
|
): ProxyHandler<T> {
|
||||||
|
let listeners: ((mutation) => {})[] = [];
|
||||||
|
let proxies = new WeakMap();
|
||||||
|
|
||||||
|
function get(rawObj: { size: number }, key: any, receiver: any): any {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(handler, key)) {
|
||||||
|
const value = Reflect.get(handler, key, receiver);
|
||||||
|
return value.bind(null, rawObj);
|
||||||
|
}
|
||||||
|
if (key === 'addListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners.push(listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'removeListener') {
|
||||||
|
return listener => {
|
||||||
|
listeners = listeners.filter(item => item != listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (key === 'watch') {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
|
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
|
||||||
|
if (!observer.watchers[prop]) {
|
||||||
|
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
|
||||||
|
}
|
||||||
|
observer.watchers[prop].push(handler);
|
||||||
|
return () => {
|
||||||
|
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Reflect.get(rawObj, key, receiver);
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Set的add方法
|
||||||
|
function add(rawObj: { add: (any) => void; has: (any) => boolean }, value: any): Object {
|
||||||
|
if (!rawObj.has(proxies.get(value))) {
|
||||||
|
const proxy = createProxy(value, hookObserverMap.get(rawObj), {
|
||||||
|
current: change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
let mutation = resolveMutation(
|
||||||
|
{ ...rawObj, [value]: change.mutation.from },
|
||||||
|
{ ...rawObj, [value]: change.mutation.to }
|
||||||
|
);
|
||||||
|
listener.current({ ...change, mutation });
|
||||||
|
listeners.forEach(lst => lst({ ...change, mutation }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
proxies.set(value, proxy);
|
||||||
|
|
||||||
|
rawObj.add(proxies.get(value));
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = { mutation: true, from: rawObj, to: value };
|
||||||
|
|
||||||
|
observer.setProp(value, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawObj;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function has(rawObj: { has: (string) => boolean }, value: any): boolean {
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
observer.useProp(value);
|
||||||
|
|
||||||
|
return rawObj.has(proxies.get(value));
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
function deleteFun(rawObj: { has: (key: any) => boolean; delete: (value: any) => void }, value: any) {
|
||||||
|
if (rawObj.has(proxies.get(value))) {
|
||||||
|
rawObj.delete(proxies.get(value));
|
||||||
|
|
||||||
|
proxies.delete(value);
|
||||||
|
|
||||||
|
const observer = getObserver(rawObj);
|
||||||
|
const mutation = { mutation: true, from: value, to: rawObj };
|
||||||
|
|
||||||
|
observer.setProp(value, mutation);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const handler = {
|
||||||
|
get,
|
||||||
|
add,
|
||||||
|
delete: deleteFun,
|
||||||
|
has,
|
||||||
|
};
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
getObserver(rawObj).addListener(change => {
|
||||||
|
if (!change.parents) change.parents = [];
|
||||||
|
change.parents.push(rawObj);
|
||||||
|
listener.current(change);
|
||||||
|
listeners.forEach(lst => lst(change));
|
||||||
|
});
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const boundHandler = {};
|
||||||
|
Object.entries(handler).forEach(([id, val]) => {
|
||||||
|
boundHandler[id] = (...args: any[]) => {
|
||||||
|
return (val as any)(...args, hookObserver);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return new Proxy(rawObj, { ...boundHandler });
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import {
|
||||||
ACTION_QUEUED,
|
ACTION_QUEUED,
|
||||||
INITIALIZED,
|
INITIALIZED,
|
||||||
QUEUE_FINISHED,
|
QUEUE_FINISHED,
|
||||||
|
QUEUE_PENDING,
|
||||||
STATE_CHANGE,
|
STATE_CHANGE,
|
||||||
SUBSCRIBED,
|
SUBSCRIBED,
|
||||||
UNSUBSCRIBED,
|
UNSUBSCRIBED,
|
||||||
|
@ -62,7 +63,11 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
|
|
||||||
const id = config.id || idGenerator.get('UNNAMED_STORE');
|
const id = config.id || idGenerator.get('UNNAMED_STORE');
|
||||||
|
|
||||||
const proxyObj = createProxy(config.state, id, !config.options?.isReduxAdapter);
|
const listener = {
|
||||||
|
current: listener => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const proxyObj = createProxy(config.state, !config.options?.isReduxAdapter, listener);
|
||||||
|
|
||||||
proxyObj.$pending = false;
|
proxyObj.$pending = false;
|
||||||
|
|
||||||
|
@ -76,16 +81,28 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
$c: $c as ComputedValues<S, C>,
|
$c: $c as ComputedValues<S, C>,
|
||||||
$queue: $queue as QueuedStoreActions<S, A>,
|
$queue: $queue as QueuedStoreActions<S, A>,
|
||||||
$config: config,
|
$config: config,
|
||||||
|
$listeners: [
|
||||||
|
change => {
|
||||||
|
devtools.emit(STATE_CHANGE, {
|
||||||
|
store: storeObj,
|
||||||
|
change,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
],
|
||||||
$subscribe: listener => {
|
$subscribe: listener => {
|
||||||
devtools.emit(SUBSCRIBED, { store: storeObj, listener });
|
devtools.emit(SUBSCRIBED, { store: storeObj, listener });
|
||||||
proxyObj.addListener(listener);
|
storeObj.$listeners.push(listener);
|
||||||
},
|
},
|
||||||
$unsubscribe: listener => {
|
$unsubscribe: listener => {
|
||||||
devtools.emit(UNSUBSCRIBED, storeObj);
|
devtools.emit(UNSUBSCRIBED, { store: storeObj });
|
||||||
proxyObj.removeListener(listener);
|
storeObj.$listeners = storeObj.$listeners.filter(item => item != listener);
|
||||||
},
|
},
|
||||||
} as unknown as StoreObj<S, A, C>;
|
} as unknown as StoreObj<S, A, C>;
|
||||||
|
|
||||||
|
listener.current = (...args) => {
|
||||||
|
storeObj.$listeners.forEach(listener => listener(...args));
|
||||||
|
};
|
||||||
|
|
||||||
const plannedActions: PlannedAction<S, ActionFunction<S>>[] = [];
|
const plannedActions: PlannedAction<S, ActionFunction<S>>[] = [];
|
||||||
|
|
||||||
// 包装actions
|
// 包装actions
|
||||||
|
@ -104,7 +121,11 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
});
|
});
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (!proxyObj.$pending) {
|
if (!proxyObj.$pending) {
|
||||||
proxyObj.$pending = true;
|
proxyObj.$pending = Date.now();
|
||||||
|
devtools.emit(QUEUE_PENDING, {
|
||||||
|
store: storeObj,
|
||||||
|
startedAt: proxyObj.$pending,
|
||||||
|
});
|
||||||
|
|
||||||
const result = config.actions![action].bind(storeObj, proxyObj)(...payload);
|
const result = config.actions![action].bind(storeObj, proxyObj)(...payload);
|
||||||
|
|
||||||
|
@ -192,20 +213,22 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
store: storeObj,
|
store: storeObj,
|
||||||
});
|
});
|
||||||
|
|
||||||
proxyObj.addListener(change => {
|
|
||||||
devtools.emit(STATE_CHANGE, {
|
|
||||||
store: storeObj,
|
|
||||||
change,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return createGetStore(storeObj);
|
return createGetStore(storeObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过该方法执行store.$queue中的action
|
// 通过该方法执行store.$queue中的action
|
||||||
function tryNextAction(storeObj, proxyObj, config, plannedActions) {
|
function tryNextAction(storeObj, proxyObj, config, plannedActions) {
|
||||||
if (!plannedActions.length) {
|
if (!plannedActions.length) {
|
||||||
proxyObj.$pending = false;
|
if (proxyObj.$pending) {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const duration = timestamp - proxyObj.$pending;
|
||||||
|
proxyObj.$pending = false;
|
||||||
|
devtools.emit(QUEUE_FINISHED, {
|
||||||
|
store: storeObj,
|
||||||
|
endedAt: timestamp,
|
||||||
|
duration,
|
||||||
|
});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ export type StoreObj<S extends object, A extends UserActions<S>, C extends UserC
|
||||||
$a: StoreActions<S, A>;
|
$a: StoreActions<S, A>;
|
||||||
$c: UserComputedValues<S>;
|
$c: UserComputedValues<S>;
|
||||||
$queue: QueuedStoreActions<S, A>;
|
$queue: QueuedStoreActions<S, A>;
|
||||||
|
$listeners;
|
||||||
$subscribe: (listener: (mutation) => void) => void;
|
$subscribe: (listener: (mutation) => void) => void;
|
||||||
$unsubscribe: (listener: (mutation) => void) => void;
|
$unsubscribe: (listener: (mutation) => void) => void;
|
||||||
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
|
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
|
||||||
|
|
|
@ -73,13 +73,13 @@ describe('测试store中的Map', () => {
|
||||||
|
|
||||||
function Parent(props) {
|
function Parent(props) {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const addOnePerson = function() {
|
const addOnePerson = function () {
|
||||||
userStore.addOnePerson(newPerson);
|
userStore.addOnePerson(newPerson);
|
||||||
};
|
};
|
||||||
const delOnePerson = function() {
|
const delOnePerson = function () {
|
||||||
userStore.delOnePerson(newPerson);
|
userStore.delOnePerson(newPerson);
|
||||||
};
|
};
|
||||||
const clearPersons = function() {
|
const clearPersons = function () {
|
||||||
userStore.clearPersons();
|
userStore.clearPersons();
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,7 +64,6 @@ describe('Mutation resolve', () => {
|
||||||
it('should resolve mutation same type types, same object', () => {
|
it('should resolve mutation same type types, same object', () => {
|
||||||
const mutation = resolveMutation({ a: 1, b: 2 }, { a: 1, b: 2 });
|
const mutation = resolveMutation({ a: 1, b: 2 }, { a: 1, b: 2 });
|
||||||
|
|
||||||
console.log(mutation);
|
|
||||||
expect(mutation.mutation).toBe(false);
|
expect(mutation.mutation).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -78,3 +77,17 @@ describe('Mutation resolve', () => {
|
||||||
expect(mutation.attributes.c.to).toBe(2);
|
expect(mutation.attributes.c.to).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Mutation collections', () => {
|
||||||
|
it('should resolve mutation of two sets', () => {
|
||||||
|
const values = [{ a: 1 }, { b: 2 }, { c: 3 }];
|
||||||
|
|
||||||
|
const source = new Set([values[0], values[1], values[2]]);
|
||||||
|
|
||||||
|
const target = new Set([values[0], values[1]]);
|
||||||
|
|
||||||
|
const mutation = resolveMutation(source, target);
|
||||||
|
|
||||||
|
expect(mutation.mutation).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -17,7 +17,7 @@ import { createStore } from '@cloudsop/horizon/src/horizonx/store/StoreHandler';
|
||||||
import { watch } from '@cloudsop/horizon/src/horizonx/proxy/watch';
|
import { watch } from '@cloudsop/horizon/src/horizonx/proxy/watch';
|
||||||
|
|
||||||
describe('watch', () => {
|
describe('watch', () => {
|
||||||
it('shouhld watch promitive state variable', async () => {
|
it('shouhld watch primitive state variable', async () => {
|
||||||
const useStore = createStore({
|
const useStore = createStore({
|
||||||
state: {
|
state: {
|
||||||
variable: 'x',
|
variable: 'x',
|
|
@ -0,0 +1,155 @@
|
||||||
|
import { createStore, useStore } from '@cloudsop/horizon/src/horizonx/store/StoreHandler';
|
||||||
|
import { describe, beforeEach, afterEach, it, expect } from '@jest/globals';
|
||||||
|
|
||||||
|
describe('Using deep variables', () => {
|
||||||
|
it('should listen to object variable change', () => {
|
||||||
|
let counter = 0;
|
||||||
|
const useTestStore = createStore({
|
||||||
|
state: { a: { b: { c: 1 } } },
|
||||||
|
});
|
||||||
|
const testStore = useTestStore();
|
||||||
|
testStore.$subscribe(() => {
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
testStore.a.b.c = 0;
|
||||||
|
|
||||||
|
expect(counter).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should listen to deep variable change', () => {
|
||||||
|
let counter = 0;
|
||||||
|
const useTestStore = createStore({
|
||||||
|
state: { color: [{ a: 1 }, 255, 255] },
|
||||||
|
});
|
||||||
|
const testStore = useTestStore();
|
||||||
|
testStore.$subscribe(() => {
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
testStore.color[0].a = i;
|
||||||
|
}
|
||||||
|
testStore.color = 'x';
|
||||||
|
|
||||||
|
expect(counter).toBe(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use set', () => {
|
||||||
|
const useTestStore = createStore({
|
||||||
|
state: { data: new Set() },
|
||||||
|
});
|
||||||
|
const testStore = useTestStore();
|
||||||
|
|
||||||
|
const a = { a: true };
|
||||||
|
|
||||||
|
testStore.data.add(a);
|
||||||
|
|
||||||
|
expect(testStore.data.has(a)).toBe(true);
|
||||||
|
|
||||||
|
testStore.data.add(a);
|
||||||
|
testStore.data.add(a);
|
||||||
|
testStore.data.delete(a);
|
||||||
|
|
||||||
|
expect(testStore.data.has(a)).toBe(false);
|
||||||
|
|
||||||
|
testStore.data.add(a);
|
||||||
|
|
||||||
|
const values = Array.from(testStore.data.values());
|
||||||
|
expect(values.length).toBe(1);
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
testStore.$subscribe(mutation => {
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
values.forEach(val => {
|
||||||
|
val.a = !val.a;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(testStore.data.has(a)).toBe(true);
|
||||||
|
|
||||||
|
expect(counter).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use map', () => {
|
||||||
|
const useTestStore = createStore({
|
||||||
|
state: { data: new Map() },
|
||||||
|
});
|
||||||
|
const testStore = useTestStore();
|
||||||
|
|
||||||
|
const data = { key: { a: 1 }, value: { b: 2 } };
|
||||||
|
|
||||||
|
testStore.data.set(data.key, data.value);
|
||||||
|
|
||||||
|
const key = Array.from(testStore.data.keys())[0];
|
||||||
|
|
||||||
|
expect(testStore.data.has(key)).toBe(true);
|
||||||
|
|
||||||
|
testStore.data.set(data.key, data.value);
|
||||||
|
testStore.data.set(data.key, data.value);
|
||||||
|
testStore.data.delete(key);
|
||||||
|
|
||||||
|
expect(testStore.data.get(key)).toBe();
|
||||||
|
|
||||||
|
testStore.data.set(data.key, data.value);
|
||||||
|
|
||||||
|
const entries = Array.from(testStore.data.entries());
|
||||||
|
expect(entries.length).toBe(1);
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
testStore.$subscribe(mutation => {
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
entries.forEach(([key, value]) => {
|
||||||
|
key.a++;
|
||||||
|
value.b++;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(counter).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use weakSet', () => {
|
||||||
|
const useTestStore = createStore({
|
||||||
|
state: { data: new WeakSet() },
|
||||||
|
});
|
||||||
|
const testStore = useTestStore();
|
||||||
|
|
||||||
|
const a = { a: true };
|
||||||
|
|
||||||
|
testStore.data.add(a);
|
||||||
|
|
||||||
|
expect(testStore.data.has(a)).toBe(true);
|
||||||
|
|
||||||
|
testStore.data.add(a);
|
||||||
|
testStore.data.add(a);
|
||||||
|
testStore.data.delete(a);
|
||||||
|
|
||||||
|
expect(testStore.data.has(a)).toBe(false);
|
||||||
|
|
||||||
|
testStore.data.add(a);
|
||||||
|
|
||||||
|
expect(testStore.data.has(a)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use weakMap', () => {
|
||||||
|
const useTestStore = createStore({
|
||||||
|
state: { data: new WeakMap() },
|
||||||
|
});
|
||||||
|
const testStore = useTestStore();
|
||||||
|
|
||||||
|
const data = { key: { a: 1 }, value: { b: 2 } };
|
||||||
|
|
||||||
|
testStore.data.set(data.key, data.value);
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
testStore.$subscribe(mutation => {
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
testStore.data.get(data.key).b++;
|
||||||
|
|
||||||
|
expect(counter).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue