/** * 把节点端口/尺寸变化的测量请求合并给 renderer,避免同一轮重复调用更新。 * owner token 区分同 ID 的不同挂载实例;卸载实例的迟到失效不能触发后来新实例的测量。 */ /** One renderer store's transient measurement requests. Not model or history state. */ /** 每个 renderer store 独立创建;默认微任务调度,也可注入测试或宿主 defer。 */ export function createNodeInternalsBatch(update:(ids:string[])=>void,defer:(flush:()=>void)=>void=queueMicrotask){ const owners=new Map(),dirty=new Set();let queued=false; /** 执行时才解析仍有效的 owner,按节点 ID 去重;先清 dirty 再回调,让重入失效进入下一轮。 */ function flush(){ queued=false;const ids=new Set();for(const owner of dirty){const id=owners.get(owner);if(id!==undefined)ids.add(id);}dirty.clear(); if(ids.size)update([...ids]); } return Object.freeze({ /** 每次挂载取得独立租约,即使节点 ID 相同也能单独 dispose。 */ acquire(id:string){ const owner=Symbol(id);owners.set(owner,id); return Object.freeze({ /** 仅活跃租约可以变脏,同一批最多安排一次延后 flush。 */ invalidate(){if(!owners.has(owner))return;dirty.add(owner);if(!queued){queued=true;defer(flush);}}, /** 卸载同时移除 owner 和其未执行请求;已排微任务可自然执行为空,不取得新实例权限。 */ dispose(){owners.delete(owner);dirty.delete(owner);}, }); }, }); }