mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-01 16:20:25 +00:00
Cache object properties to avoid extra RPC
Serialize non-function properties of server-side objects as cachedProps so simple property reads don't require additional RPCs. Added cachedProps to SerializedValue, have RpcServer.storeObjectRef serialize and attach cachedProps (skipping functions), updated serializer to deserialize cachedProps and pass them to proxyCreator, and updated client proxy creation to accept cachedProps and return cached top-level properties directly. Tests updated to expect direct property access for serialized/simple objects and arrays.
This commit is contained in:
@@ -48,7 +48,7 @@ export function createDeepProxy<T = unknown> (options: DeepProxyOptions): T {
|
||||
if (!cb) throw new Error(`Nested callback not found: ${id}`);
|
||||
return cb;
|
||||
},
|
||||
proxyCreator: (path, proxyRefId) => createProxyAtPath(path, proxyRefId),
|
||||
proxyCreator: (path, proxyRefId, cachedProps) => createProxyAtPath(path, proxyRefId, cachedProps),
|
||||
}));
|
||||
const result = await callback(...args);
|
||||
return serialize(result, { callbackRegistry });
|
||||
@@ -57,8 +57,11 @@ export function createDeepProxy<T = unknown> (options: DeepProxyOptions): T {
|
||||
|
||||
/**
|
||||
* 在指定路径创建代理
|
||||
* @param path 路径
|
||||
* @param refId 远程对象引用 ID
|
||||
* @param cachedProps 缓存的属性值(避免属性访问需要 RPC)
|
||||
*/
|
||||
function createProxyAtPath (path: PropertyKey[], refId?: string): unknown {
|
||||
function createProxyAtPath (path: PropertyKey[], refId?: string, cachedProps?: Record<string, unknown>): unknown {
|
||||
const proxyMeta: ProxyMeta = {
|
||||
path: [...path],
|
||||
isProxy: true,
|
||||
@@ -80,7 +83,12 @@ export function createDeepProxy<T = unknown> (options: DeepProxyOptions): T {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 返回新的子路径代理(继承 refId)
|
||||
// 检查缓存属性(仅顶层代理,即 path 为空时)
|
||||
if (path.length === 0 && cachedProps && typeof prop === 'string' && prop in cachedProps) {
|
||||
return cachedProps[prop];
|
||||
}
|
||||
|
||||
// 返回新的子路径代理(继承 refId,不继承 cachedProps)
|
||||
return createProxyAtPath([...path, prop], refId);
|
||||
},
|
||||
|
||||
@@ -203,7 +211,7 @@ export function createDeepProxy<T = unknown> (options: DeepProxyOptions): T {
|
||||
if (!cb) throw new Error(`Callback not found: ${id}`);
|
||||
return cb;
|
||||
},
|
||||
proxyCreator: (proxyPath, proxyRefId) => createProxyAtPath(proxyPath, proxyRefId),
|
||||
proxyCreator: (proxyPath, proxyRefId, cachedProps) => createProxyAtPath(proxyPath, proxyRefId, cachedProps),
|
||||
});
|
||||
return deserialized;
|
||||
}
|
||||
@@ -214,7 +222,7 @@ export function createDeepProxy<T = unknown> (options: DeepProxyOptions): T {
|
||||
if (!cb) throw new Error(`Callback not found: ${id}`);
|
||||
return cb;
|
||||
},
|
||||
proxyCreator: (proxyPath, proxyRefId) => createProxyAtPath(proxyPath, proxyRefId),
|
||||
proxyCreator: (proxyPath, proxyRefId, cachedProps) => createProxyAtPath(proxyPath, proxyRefId, cachedProps),
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export interface DeserializeContext {
|
||||
/** 回调解析器 */
|
||||
callbackResolver?: (id: string) => Function;
|
||||
/** 代理创建器 */
|
||||
proxyCreator?: (path: PropertyKey[], refId?: string) => unknown;
|
||||
proxyCreator?: (path: PropertyKey[], refId?: string, cachedProps?: Record<string, unknown>) => unknown;
|
||||
/** 对象引用解析器 */
|
||||
refResolver?: (refId: string) => unknown;
|
||||
}
|
||||
@@ -368,7 +368,15 @@ export function deserialize (data: SerializedValue, context: DeserializeContext
|
||||
}
|
||||
// 否则创建代理(客户端场景)
|
||||
if (proxyCreator) {
|
||||
return proxyCreator([], data.refId);
|
||||
// 反序列化缓存的属性
|
||||
let cachedValues: Record<string, unknown> | undefined;
|
||||
if (data.cachedProps) {
|
||||
cachedValues = {};
|
||||
for (const [key, val] of Object.entries(data.cachedProps)) {
|
||||
cachedValues[key] = deserialize(val, context);
|
||||
}
|
||||
}
|
||||
return proxyCreator([], data.refId, cachedValues);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -155,15 +155,36 @@ export class RpcServer {
|
||||
|
||||
/**
|
||||
* 存储对象引用并返回序列化的引用
|
||||
* 同时序列化可序列化的属性值,避免属性访问需要额外 RPC
|
||||
*/
|
||||
private storeObjectRef (value: unknown): SerializedValue {
|
||||
const refId = generateRefId();
|
||||
this.objectRefs.set(refId, value);
|
||||
const className = value?.constructor?.name;
|
||||
|
||||
// 序列化非函数属性
|
||||
const cachedProps: Record<string, SerializedValue> = {};
|
||||
if (value && typeof value === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
const propValue = (value as Record<string, unknown>)[key];
|
||||
// 跳过函数(方法需要远程调用)
|
||||
if (typeof propValue === 'function') {
|
||||
continue;
|
||||
}
|
||||
// 序列化属性值
|
||||
try {
|
||||
cachedProps[key] = serialize(propValue, { callbackRegistry: this.localCallbacks });
|
||||
} catch {
|
||||
// 序列化失败的属性跳过,让客户端通过 RPC 获取
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: SerializedValueType.OBJECT_REF,
|
||||
refId,
|
||||
className: className !== 'Object' ? className : undefined,
|
||||
cachedProps: Object.keys(cachedProps).length > 0 ? cachedProps : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ export interface SerializedValue {
|
||||
elements?: SerializedValue[];
|
||||
/** 远程对象引用 ID(用于保持代理能力) */
|
||||
refId?: string;
|
||||
/** 缓存的属性值(OBJECT_REF 时使用,避免属性访问需要 RPC) */
|
||||
cachedProps?: Record<string, SerializedValue>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user