Match-id-760cbbb23b2babb6aab29450b9834f032fec4914
This commit is contained in:
parent
26d41bf933
commit
0b11860c9d
|
@ -6,3 +6,5 @@ export const ACTION = 'horizonx action';
|
|||
export const ACTION_QUEUED = 'horizonx action queued';
|
||||
export const QUEUE_PENDING = 'horizonx queue pending';
|
||||
export const QUEUE_FINISHED = 'horizonx queue finished';
|
||||
export const RENDER_TRIGGERED = 'horizonx render triggered';
|
||||
export const OBSERVED_COMPONENTS = 'horizonx observed components';
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import { getStore, getAllStores } from '../store/StoreHandler';
|
||||
import { OBSERVED_COMPONENTS } from './constants';
|
||||
|
||||
const sessionId = Date.now();
|
||||
|
||||
export function isPanelActive() {
|
||||
return window['__HORIZON_DEV_HOOK__'];
|
||||
}
|
||||
|
||||
function makeStoreSnapshot({ type, data }) {
|
||||
const expanded = {};
|
||||
Object.keys(data.store.$c).forEach(key => {
|
||||
|
@ -40,9 +47,12 @@ function makeProxySnapshot(obj) {
|
|||
}
|
||||
|
||||
export const devtools = {
|
||||
getVNodeId: vNode => {
|
||||
if (!isPanelActive()) return;
|
||||
getVNodeId(vNode);
|
||||
},
|
||||
emit: (type, data) => {
|
||||
if (!window['__HORIZON_DEV_HOOK__']) return;
|
||||
console.log('store snapshot:', makeStoreSnapshot({ type, data }));
|
||||
if (!isPanelActive()) return;
|
||||
window.postMessage({
|
||||
type: 'HORIZON_DEV_TOOLS',
|
||||
payload: makeStoreSnapshot({ type, data }),
|
||||
|
@ -50,3 +60,58 @@ export const devtools = {
|
|||
});
|
||||
},
|
||||
};
|
||||
|
||||
function getAffectedComponents() {
|
||||
const allStores = getAllStores();
|
||||
const keys = Object.keys(allStores);
|
||||
let res = {};
|
||||
keys.forEach(key => {
|
||||
const subRes = new Set();
|
||||
const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values());
|
||||
while (process.length) {
|
||||
let pivot = process.shift();
|
||||
if (pivot?.tag) subRes.add(pivot);
|
||||
if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item));
|
||||
}
|
||||
res[key] = Array.from(subRes).map(vnode => {
|
||||
return {
|
||||
name: vnode?.type
|
||||
.toString()
|
||||
.replace(/\{.*\}/gms, '{...}')
|
||||
.replace('function ', ''),
|
||||
nodeId: window.__HORIZON_DEV_HOOK__.getVnodeId(vnode),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
window.addEventListener('message', messageEvent => {
|
||||
if (messageEvent.data.payload.type === 'horizonx request observed components') {
|
||||
// get observed components
|
||||
setTimeout(() => {
|
||||
window.postMessage({
|
||||
type: 'HORIZON_DEV_TOOLS',
|
||||
payload: { type: OBSERVED_COMPONENTS, data: getAffectedComponents() },
|
||||
from: 'dev tool hook',
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
if (messageEvent.data.payload.type === 'horizonx executue action') {
|
||||
const data = messageEvent.data.payload.data;
|
||||
const store = getStore(data.storeId);
|
||||
if (!store?.[data.action]) {
|
||||
}
|
||||
|
||||
const action = store[data.action];
|
||||
const params = data.params;
|
||||
action(...params).bind(store);
|
||||
}
|
||||
});
|
||||
|
||||
export function getVNodeId(vNode) {
|
||||
window['__HORIZON_DEV_HOOK__'].send();
|
||||
return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import { launchUpdateFromVNode } from '../../renderer/TreeBuilder';
|
||||
import { getProcessingVNode } from '../../renderer/GlobalVar';
|
||||
import { VNode } from '../../renderer/vnode/VNode';
|
||||
import { devtools } from '../devtools';
|
||||
|
||||
export interface IObserver {
|
||||
useProp: (key: string) => void;
|
||||
|
@ -89,7 +90,8 @@ export class Observer implements IObserver {
|
|||
this.triggerUpdate(vNode);
|
||||
});
|
||||
|
||||
this.triggerChangeListeners(mutation);
|
||||
// NOTE: mutations are different in dev and production.
|
||||
this.triggerChangeListeners({ mutation, vNodes });
|
||||
}
|
||||
|
||||
triggerUpdate(vNode: VNode): void {
|
||||
|
@ -109,8 +111,27 @@ export class Observer implements IObserver {
|
|||
this.listeners = this.listeners.filter(item => item != listener);
|
||||
}
|
||||
|
||||
triggerChangeListeners(mutation: any): void {
|
||||
this.listeners.forEach(listener => listener(mutation));
|
||||
triggerChangeListeners({ mutation, vNodes }): void {
|
||||
const nodesList = vNodes ? Array.from(vNodes) : [];
|
||||
this.listeners.forEach(listener =>
|
||||
listener({
|
||||
mutation,
|
||||
vNodes: nodesList.map(vNode => {
|
||||
let realNode = vNode.realNode;
|
||||
let searchedNode = vNode;
|
||||
while (!realNode) {
|
||||
searchedNode = searchedNode.child;
|
||||
realNode = searchedNode.realNode;
|
||||
}
|
||||
return {
|
||||
type: vNode?.type?.name,
|
||||
id: devtools.getVNodeId(vNode),
|
||||
path: vNode.path,
|
||||
element: realNode?.outerHTML?.substr(0, 100),
|
||||
};
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 触发所有使用的props的VNode更新
|
||||
|
|
|
@ -27,7 +27,7 @@ const proxyMap = new WeakMap();
|
|||
|
||||
export const hookObserverMap = new WeakMap();
|
||||
|
||||
export function createProxy(rawObj: any, isHookObserver = true): any {
|
||||
export function createProxy(rawObj: any, id, isHookObserver = true): any {
|
||||
// 不是对象(是原始数据类型)不用代理
|
||||
if (!(rawObj && isObject(rawObj))) {
|
||||
return rawObj;
|
||||
|
|
|
@ -17,6 +17,7 @@ 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 = {
|
||||
|
@ -54,28 +55,30 @@ function set(rawObj: any[], key: string, value: any, receiver: any) {
|
|||
const oldLength = rawObj.length;
|
||||
const newValue = value;
|
||||
|
||||
const oldArray = JSON.parse(JSON.stringify(rawObj));
|
||||
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, resolveMutation(oldArray, rawObj));
|
||||
cb(key, oldValue, newValue, mutation);
|
||||
});
|
||||
}
|
||||
|
||||
// 触发属性变化
|
||||
observer.setProp(key, resolveMutation(oldValue, rawObj));
|
||||
observer.setProp(key, mutation);
|
||||
}
|
||||
|
||||
if (oldLength !== newLength) {
|
||||
// 触发数组的大小变化
|
||||
observer.setProp('length', resolveMutation(oldValue, rawObj));
|
||||
observer.setProp('length', mutation);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
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 = {
|
||||
|
@ -91,18 +92,20 @@ function set(
|
|||
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, resolveMutation(oldValue, rawObj));
|
||||
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||
}
|
||||
|
||||
if (valChange) {
|
||||
if (observer.watchers?.[key]) {
|
||||
observer.watchers[key].forEach(cb => {
|
||||
cb(key, oldValue, newValue, resolveMutation(oldValue, rawObj));
|
||||
cb(key, oldValue, newValue, mutation);
|
||||
});
|
||||
}
|
||||
|
||||
observer.setProp(key, resolveMutation(oldValue, rawObj));
|
||||
observer.setProp(key, mutation);
|
||||
}
|
||||
|
||||
return rawObj;
|
||||
|
@ -110,13 +113,16 @@ function set(
|
|||
|
||||
// Set的add方法
|
||||
function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object {
|
||||
const oldCollection = JSON.parse(JSON.stringify(rawObj));
|
||||
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||
if (!rawObj.has(value)) {
|
||||
rawObj.add(value);
|
||||
|
||||
const observer = getObserver(rawObj);
|
||||
observer.setProp(value, resolveMutation(oldCollection, rawObj));
|
||||
observer.setProp(COLLECTION_CHANGE, resolveMutation(oldCollection, rawObj));
|
||||
const mutation = isPanelActive()
|
||||
? resolveMutation(oldCollection, rawObj)
|
||||
: { mutation: true, from: null, to: rawObj };
|
||||
observer.setProp(value, mutation);
|
||||
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||
}
|
||||
|
||||
return rawObj;
|
||||
|
@ -140,13 +146,16 @@ function clear(rawObj: { size: number; clear: () => void }) {
|
|||
}
|
||||
|
||||
function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) {
|
||||
const oldCollection = JSON.parse(JSON.stringify(rawObj));
|
||||
const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||
if (rawObj.has(key)) {
|
||||
rawObj.delete(key);
|
||||
|
||||
const observer = getObserver(rawObj);
|
||||
observer.setProp(key, resolveMutation(oldCollection, rawObj));
|
||||
observer.setProp(COLLECTION_CHANGE, resolveMutation(oldCollection, rawObj));
|
||||
const mutation = isPanelActive()
|
||||
? resolveMutation(oldCollection, rawObj)
|
||||
: { mutation: true, from: null, to: rawObj };
|
||||
observer.setProp(key, mutation);
|
||||
observer.setProp(COLLECTION_CHANGE, mutation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
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, {
|
||||
|
@ -70,7 +71,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL
|
|||
}
|
||||
|
||||
export function set(rawObj: object, key: string, value: any, receiver: any): boolean {
|
||||
const oldObject = JSON.parse(JSON.stringify(rawObj));
|
||||
const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
|
||||
const observer = getObserver(rawObj);
|
||||
|
||||
if (value && key == 'removeListener') {
|
||||
|
@ -80,14 +81,15 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo
|
|||
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, resolveMutation(oldObject, rawObj));
|
||||
cb(key, oldValue, newValue, mutation);
|
||||
});
|
||||
}
|
||||
observer.setProp(key, resolveMutation(oldObject, rawObj));
|
||||
observer.setProp(key, mutation);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
|
||||
import { useEffect, useRef } from '../../renderer/hooks/HookExternal';
|
||||
import { getProcessingVNode } from '../../renderer/GlobalVar';
|
||||
import { getProcessingVNode, getStartVNode } from '../../renderer/GlobalVar';
|
||||
import { createProxy } from '../proxy/ProxyHandler';
|
||||
import readonlyProxy from '../proxy/readonlyProxy';
|
||||
import { Observer } from '../proxy/Observer';
|
||||
|
@ -60,7 +60,9 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
throw new Error('store obj must be pure object');
|
||||
}
|
||||
|
||||
const proxyObj = createProxy(config.state, !config.options?.isReduxAdapter);
|
||||
const id = config.id || idGenerator.get('UNNAMED_STORE');
|
||||
|
||||
const proxyObj = createProxy(config.state, id, !config.options?.isReduxAdapter);
|
||||
|
||||
proxyObj.$pending = false;
|
||||
|
||||
|
@ -68,6 +70,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
const $queue: Partial<StoreActions<S, A>> = {};
|
||||
const $c: Partial<ComputedValues<S, C>> = {};
|
||||
const storeObj = {
|
||||
id,
|
||||
$s: proxyObj,
|
||||
$a: $a as StoreActions<S, A>,
|
||||
$c: $c as ComputedValues<S, C>,
|
||||
|
@ -183,18 +186,16 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
});
|
||||
}
|
||||
|
||||
if (config.id) {
|
||||
storeMap.set(config.id, storeObj);
|
||||
}
|
||||
storeMap.set(id, storeObj);
|
||||
|
||||
devtools.emit(INITIALIZED, {
|
||||
store: storeObj,
|
||||
});
|
||||
|
||||
proxyObj.addListener(mutation => {
|
||||
proxyObj.addListener(change => {
|
||||
devtools.emit(STATE_CHANGE, {
|
||||
store: storeObj,
|
||||
mutation,
|
||||
change,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -299,6 +300,14 @@ export function useStore<S extends object, A extends UserActions<S>, C extends U
|
|||
return storeObj as StoreObj<S, A, C>;
|
||||
}
|
||||
|
||||
export function getStore(id: string) {
|
||||
return storeMap.get(id);
|
||||
}
|
||||
|
||||
export function getAllStores() {
|
||||
return Object.fromEntries(storeMap);
|
||||
}
|
||||
|
||||
export function clearStore(id: string): void {
|
||||
storeMap.delete(id);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue