fix: fork model

This commit is contained in:
chenos 2025-06-19 13:54:45 +08:00
parent 4d0ebc10a4
commit aa4ea33698

View File

@ -7,191 +7,209 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import { action, define, observable } from '@formily/reactive'; import { action, define, observable } from '@formily/reactive';
import type { IModelComponentProps } from '../types'; import type { IModelComponentProps } from '../types';
import { FlowModel } from './flowModel'; import { FlowModel } from './flowModel';
/** /**
* ForkFlowModel FlowModel * ForkFlowModel FlowModel
* - master FlowModel * - master FlowModel
* - props (localProps) master * - props (localProps) master
* - this fork master * - this fork master
* - 使 Object.create this.constructor * - 使 Object.create this.constructor
* - setter this fork * - setter this fork
* - FlowEngine.modelInstances uid master * - FlowEngine.modelInstances uid master
*/ */
export class ForkFlowModel<TMaster extends FlowModel = FlowModel> { export class ForkFlowModel<TMaster extends FlowModel = FlowModel> {
/** 与 master 相同的 UID用于日志调试 */ /** 与 master 相同的 UID用于日志调试 */
public readonly uid: string; public readonly uid: string;
/** 调试标识,便于在日志或断言中快速识别 */ /** 调试标识,便于在日志或断言中快速识别 */
public readonly isFork = true; public readonly isFork = true;
/** 本地覆盖的 propsfork 层面的 UI/状态 */ /** 本地覆盖的 propsfork 层面的 UI/状态 */
public localProps: IModelComponentProps; public localProps: IModelComponentProps;
/** master 引用 */ /** master 引用 */
private master: TMaster; private master: TMaster;
/** 是否已被释放 */ /** 是否已被释放 */
private disposed = false; private disposed = false;
/** fork 在 master.forks 中的索引 */ /** fork 在 master.forks 中的索引 */
public readonly forkId: number; public readonly forkId: number;
constructor(master: TMaster, initialProps: IModelComponentProps = {}, forkId = 0) { /** 用于共享上下文的对象,存储跨 fork 的共享数据 */
this.master = master; private _sharedContext: Record<string, any> = {};
this.uid = master.uid;
this.localProps = { ...initialProps }; constructor(master: TMaster, initialProps: IModelComponentProps = {}, forkId = 0) {
this.forkId = forkId; this.master = master;
this.uid = master.uid;
define(this, { this.localProps = { ...initialProps };
localProps: observable, this.forkId = forkId;
setProps: action,
}); define(this, {
localProps: observable,
// 返回代理对象,实现自动透传 setProps: action,
return new Proxy(this, { });
get: (target: any, prop: PropertyKey, receiver: any) => {
// disposed check // 返回代理对象,实现自动透传
if (prop === 'disposed') return target.disposed; return new Proxy(this, {
get: (target: any, prop: PropertyKey, receiver: any) => {
// 特殊处理 constructor应该返回 master 的 constructor // disposed check
if (prop === 'constructor') { if (prop === 'disposed') return target.disposed;
return target.master.constructor;
} // 特殊处理 constructor应该返回 master 的 constructor
if (prop === 'props') { if (prop === 'constructor') {
// 对 props 做合并返回 return target.master.constructor;
return { ...target.master.getProps(), ...target.localProps }; }
} if (prop === 'props') {
// fork 自身属性 / 方法优先 // 对 props 做合并返回
if (prop in target) { return { ...target.master.getProps(), ...target.localProps };
return Reflect.get(target, prop, receiver); }
} // fork 自身属性 / 方法优先
if (prop in target) {
// 默认取 master 上的值 return Reflect.get(target, prop, receiver);
const value = (target.master as any)[prop]; }
// 如果是函数,需要绑定到 fork 实例,让 this 指向 fork // 默认取 master 上的值
// 使用闭包捕获正确的 constructor避免异步方法中的竞态条件 const value = (target.master as any)[prop];
if (typeof value === 'function') {
const masterConstructor = target.master.constructor; // 如果是函数,需要绑定到 fork 实例,让 this 指向 fork
return function (this: any, ...args: any[]) { // 使用闭包捕获正确的 constructor避免异步方法中的竞态条件
// 创建一个临时的 this 对象,包含正确的 constructor if (typeof value === 'function') {
const contextThis = Object.create(this); const masterConstructor = target.master.constructor;
Object.defineProperty(contextThis, 'constructor', { return function (this: any, ...args: any[]) {
value: masterConstructor, // 创建一个临时的 this 对象,包含正确的 constructor
configurable: true, const contextThis = Object.create(this);
enumerable: false, Object.defineProperty(contextThis, 'constructor', {
writable: false, value: masterConstructor,
}); configurable: true,
enumerable: false,
return value.apply(contextThis, args); writable: false,
}.bind(receiver); });
}
return value; return value.apply(contextThis, args);
}, }.bind(receiver);
set: (target: any, prop: PropertyKey, value: any, receiver: any) => { }
if (prop === 'props') { return value;
return true; },
} set: (target: any, prop: PropertyKey, value: any, receiver: any) => {
if (prop === 'props') {
// 如果 fork 自带字段,则写到自身(例如 localProps return true;
if (prop in target) { }
return Reflect.set(target, prop, value, receiver);
} // 如果 fork 自带字段,则写到自身(例如 localProps
if (prop in target) {
// 其余写入 master但需要确保 setter 中的 this 指向 fork return Reflect.set(target, prop, value, receiver);
// 检查 master 上是否有对应的 setter }
const descriptor = this.getPropertyDescriptor(target.master, prop);
if (descriptor && descriptor.set) { // 其余写入 master但需要确保 setter 中的 this 指向 fork
// 如果有 setter直接用 receiverfork 实例)作为 this 调用 // 检查 master 上是否有对应的 setter
// 这样 setter 中的 this 就指向 fork可以正确调用 fork 的方法 const descriptor = this.getPropertyDescriptor(target.master, prop);
descriptor.set.call(receiver, value); if (descriptor && descriptor.set) {
return true; // 如果有 setter直接用 receiverfork 实例)作为 this 调用
} else { // 这样 setter 中的 this 就指向 fork可以正确调用 fork 的方法
// 没有 setter直接赋值到 master descriptor.set.call(receiver, value);
(target.master as any)[prop] = value; return true;
return true; } else {
} // 没有 setter直接赋值到 master
}, (target.master as any)[prop] = value;
}); return true;
} }
},
/** });
* }
*/
private getPropertyDescriptor(obj: any, prop: PropertyKey): PropertyDescriptor | undefined { public setSharedContext(ctx: Record<string, any>) {
let current = obj; this._sharedContext = ctx;
while (current) { }
const descriptor = Object.getOwnPropertyDescriptor(current, prop);
if (descriptor) { public getSharedContext() {
return descriptor; if (this.async || !this.parent) {
} return this._sharedContext;
current = Object.getPrototypeOf(current); }
} return {
return undefined; ...this.parent?.getSharedContext(),
} ...this._sharedContext, // 当前实例的 context 优先级最高
};
/** }
* props fork
*/ /**
setProps(key: string | IModelComponentProps, value?: any): void { *
if (this.disposed) return; */
private getPropertyDescriptor(obj: any, prop: PropertyKey): PropertyDescriptor | undefined {
if (typeof key === 'string') { let current = obj;
this.localProps[key] = value; while (current) {
} else { const descriptor = Object.getOwnPropertyDescriptor(current, prop);
this.localProps = { ...this.localProps, ...key }; if (descriptor) {
} return descriptor;
} }
current = Object.getPrototypeOf(current);
/** }
* render 使 master props return undefined;
*/ }
render() {
if (this.disposed) return null; /**
// 将 master.render 以 fork 作为 this 调用,使其读取到合并后的 props * props fork
const mergedProps = { ...this.master.getProps(), ...this.localProps }; */
// 临时替换 this.props setProps(key: string | IModelComponentProps, value?: any): void {
const originalProps = (this as any).props; if (this.disposed) return;
(this as any).props = mergedProps;
try { if (typeof key === 'string') {
return (this.master.render as any).call(this); this.localProps[key] = value;
} finally { } else {
(this as any).props = originalProps; this.localProps = { ...this.localProps, ...key };
} }
} }
/** /**
* fork master.forks * render 使 master props
*/ */
dispose() { render() {
if (this.disposed) return; if (this.disposed) return null;
this.disposed = true; // 将 master.render 以 fork 作为 this 调用,使其读取到合并后的 props
if (this.master && (this.master as any).forks) { const mergedProps = { ...this.master.getProps(), ...this.localProps };
(this.master as any).forks.delete(this as any); // 临时替换 this.props
} const originalProps = (this as any).props;
// 从 master 的 forkCache 中移除自己 (this as any).props = mergedProps;
if (this.master && (this.master as any).forkCache) { try {
const forkCache = (this.master as any).forkCache; return (this.master.render as any).call(this);
for (const [key, fork] of forkCache.entries()) { } finally {
if (fork === this) { (this as any).props = originalProps;
forkCache.delete(key); }
break; }
}
} /**
} * fork master.forks
// @ts-ignore */
this.master = null; dispose() {
} if (this.disposed) return;
this.disposed = true;
/** if (this.master && (this.master as any).forks) {
* propsmaster + localPropslocal (this.master as any).forks.delete(this as any);
*/ }
getProps(): IModelComponentProps { // 从 master 的 forkCache 中移除自己
return { ...this.master.getProps(), ...this.localProps }; if (this.master && (this.master as any).forkCache) {
} const forkCache = (this.master as any).forkCache;
} for (const [key, fork] of forkCache.entries()) {
if (fork === this) {
// 类型断言:让 ForkFlowModel 可以被当作 FlowModel 使用 forkCache.delete(key);
export interface ForkFlowModel<TMaster extends FlowModel = FlowModel> extends FlowModel {} break;
}
}
}
// @ts-ignore
this.master = null;
}
/**
* propsmaster + localPropslocal
*/
getProps(): IModelComponentProps {
return { ...this.master.getProps(), ...this.localProps };
}
}
// 类型断言:让 ForkFlowModel 可以被当作 FlowModel 使用
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ForkFlowModel<TMaster extends FlowModel = FlowModel> extends FlowModel {}