diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index a762daaea..d12427074 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -36,6 +36,7 @@ import { hasOwnProperty, UtilsMetadata, getClosestNode, + transactionManager, } from '@alilc/lowcode-utils'; import { DragObjectType, @@ -59,9 +60,8 @@ import { getClosestClickableNode } from './utils/clickable'; import { ComponentMetadata, ComponentSchema, - TransformStage, - ActivityData, Package, + TransitionType, } from '@alilc/lowcode-types'; import { BuiltinSimulatorRenderer } from './renderer'; import clipboard from '../designer/clipboard'; @@ -181,6 +181,14 @@ export class BuiltinSimulatorHost implements ISimulatorHost { + this.stopAutoRepaintNode(); + }, TransitionType.REPAINT); + transactionManager.onEndTransaction(() => { + this.rerender(); + this.enableAutoRepaintNode(); + }, TransitionType.REPAINT); } get currentDocument() { diff --git a/packages/designer/src/builtin-simulator/renderer.ts b/packages/designer/src/builtin-simulator/renderer.ts index 32c0e2567..a25f53200 100644 --- a/packages/designer/src/builtin-simulator/renderer.ts +++ b/packages/designer/src/builtin-simulator/renderer.ts @@ -13,6 +13,8 @@ export interface BuiltinSimulatorRenderer { setCopyState(state: boolean): void; loadAsyncLibrary(asyncMap: { [index: string]: any }): void; clearState(): void; + stopAutoRepaintNode(): void; + enableAutoRepaintNode(): void; run(): void; } diff --git a/packages/designer/src/document/history.ts b/packages/designer/src/document/history.ts index 5ac0d99cc..436748955 100644 --- a/packages/designer/src/document/history.ts +++ b/packages/designer/src/document/history.ts @@ -1,5 +1,5 @@ import { EventEmitter } from 'events'; -import { autorun, reaction, mobx, untracked, globalContext, Editor } from '@alilc/lowcode-editor-core'; +import { reaction, untracked, globalContext, Editor } from '@alilc/lowcode-editor-core'; import { NodeSchema } from '@alilc/lowcode-types'; import { History as ShellHistory } from '@alilc/lowcode-shell'; @@ -32,31 +32,30 @@ export class History { this.currentSerialization = serialization; } - constructor(dataFn: () => T, private redoer: (data: T) => void, private timeGap: number = 1000) { + constructor(dataFn: () => T | null, private redoer: (data: T) => void, private timeGap: number = 1000) { this.session = new Session(0, null, this.timeGap); this.records = [this.session]; - reaction(() => { + reaction((): any => { + if (this.asleep) return null; return dataFn(); }, (data: T) => { - if (this.asleep) return; untracked(() => { const log = this.currentSerialization.serialize(data); - if (this.session.isActive()) { - this.session.log(log); - } else { - this.session.end(); - const lastState = this.getState(); - const cursor = this.session.cursor + 1; - const session = new Session(cursor, log, this.timeGap); - this.session = session; - this.records.splice(cursor, this.records.length - cursor, session); - const currentState = this.getState(); - if (currentState !== lastState) { - this.emitter.emit('statechange', currentState); - } + if (this.session.isActive()) { + this.session.log(log); + } else { + this.session.end(); + const lastState = this.getState(); + const cursor = this.session.cursor + 1; + const session = new Session(cursor, log, this.timeGap); + this.session = session; + this.records.splice(cursor, this.records.length - cursor, session); + const currentState = this.getState(); + if (currentState !== lastState) { + this.emitter.emit('statechange', currentState); } - // } + } }); }, { fireImmediately: true }); } diff --git a/packages/engine/src/modules/utils.ts b/packages/engine/src/modules/utils.ts index 7e7cf6206..3074b828b 100644 --- a/packages/engine/src/modules/utils.ts +++ b/packages/engine/src/modules/utils.ts @@ -1,6 +1,6 @@ -import { isFormEvent, compatibleLegaoSchema, getNodeSchemaById } from '@alilc/lowcode-utils'; +import { isFormEvent, compatibleLegaoSchema, getNodeSchemaById, transactionManager } from '@alilc/lowcode-utils'; import { isNodeSchema } from '@alilc/lowcode-types'; -import { getConvertedExtraKey, getOriginalExtraKey, isNode, isSettingField } from '@alilc/lowcode-designer'; +import { getConvertedExtraKey, getOriginalExtraKey } from '@alilc/lowcode-designer'; const utils = { isNodeSchema, @@ -9,6 +9,7 @@ const utils = { getNodeSchemaById, getConvertedExtraKey, getOriginalExtraKey, + startTransaction: transactionManager.startTransaction, }; export default utils; \ No newline at end of file diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index 2dfdeccac..98cbbd621 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -349,6 +349,11 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { * 是否为画布自动渲染 */ autoRender = true; + + /** + * 画布是否自动监听事件来重绘节点 + */ + autoRepaintNode = true; /** * 加载资源 */ @@ -491,6 +496,14 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { this._appContext = { ...this._appContext }; } + stopAutoRepaintNode() { + this.autoRepaintNode = false; + } + + enableAutoRepaintNode() { + this.autoRepaintNode = true; + } + dispose() { this.disposeFunctions.forEach(fn => fn()); this.documentInstances.forEach(docInst => docInst.dispose()); diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index e2dbd22d4..0dfb2d9a0 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -102,14 +102,23 @@ function initRerenderEvent({ leaf, dispose: [ leaf?.onPropChange?.(() => { + if (!container.autoRepaintNode) { + return; + } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onPropsChange make rerender`); container.rerender(); }), leaf?.onChildrenChange?.(() => { + if (!container.autoRepaintNode) { + return; + } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onChildrenChange make rerender`); container.rerender(); }) as Function, leaf?.onVisibleChange?.(() => { + if (!container.autoRepaintNode) { + return; + } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onVisibleChange make rerender`); container.rerender(); }), @@ -213,14 +222,18 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { } componentDidMount() { + const _leaf = this.leaf; + this.initOnPropsChangeEvent(_leaf); + this.initOnChildrenChangeEvent(_leaf); + this.initOnVisibleChangeEvent(_leaf); this.recordTime(); } - get defaultState() { + getDefaultState(nextProps: any) { const { hidden = false, condition = true, - } = this.leaf?.export?.(TransformStage.Render) || {}; + } = nextProps.__inner__ || {}; return { nodeChildren: null, childrenInState: false, @@ -236,11 +249,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { // 监听以下事件,当变化时更新自己 __debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`); clearRerenderEvent(componentCacheId); - const _leaf = this.leaf; - this.initOnPropsChangeEvent(_leaf); - this.initOnChildrenChangeEvent(_leaf); - this.initOnVisibleChangeEvent(_leaf); - this.curEventLeaf = _leaf; + this.curEventLeaf = this.leaf; cache.ref.set(componentCacheId, { makeUnitRender: this.makeUnitRender, @@ -248,7 +257,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { let cacheState = cache.state.get(componentCacheId); if (!cacheState || cacheState.__tag !== props.__tag) { - cacheState = this.defaultState; + cacheState = this.getDefaultState(props); } this.state = cacheState; @@ -279,6 +288,10 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { singleRender?: boolean; }; + get autoRepaintNode() { + return container.autoRepaintNode; + } + judgeMiniUnitRender() { if (!this.renderUnitInfo) { this.getRenderUnitInfo(); @@ -380,13 +393,16 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { const { visible, ...resetState - } = this.defaultState; + } = this.getDefaultState(nextProps); this.setState(resetState); } /** 监听参数变化 */ initOnPropsChangeEvent(leaf = this.leaf): void { - const dispose = leaf?.onPropChange?.(debounce((propChangeInfo: PropChangeOptions) => { + const dispose = leaf?.onPropChange?.((propChangeInfo: PropChangeOptions) => { + if (!this.autoRepaintNode) { + return; + } const { key, newValue = null, @@ -433,7 +449,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { }); this.judgeMiniUnitRender(); - }, 30)); + }); dispose && this.disposeFunctions.push(dispose); } @@ -443,6 +459,9 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { */ initOnVisibleChangeEvent(leaf = this.leaf) { const dispose = leaf?.onVisibleChange?.((flag: boolean) => { + if (!this.autoRepaintNode) { + return; + } if (this.state.visible === flag) { return; } @@ -463,6 +482,9 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { */ initOnChildrenChangeEvent(leaf = this.leaf) { const dispose = leaf?.onChildrenChange?.((param): void => { + if (!this.autoRepaintNode) { + return; + } const { type, node, @@ -540,6 +562,8 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { ref: forwardedRef, }; + delete compProps.__inner__; + return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null); } } diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx index 2d58cac76..d64ff4196 100644 --- a/packages/renderer-core/src/renderer/base.tsx +++ b/packages/renderer-core/src/renderer/base.tsx @@ -670,7 +670,14 @@ export default function baseRendererFactory(): IBaseRenderComponent { } } } - return renderComp({ ...props, ...otherProps }); + return renderComp({ + ...props, + ...otherProps, + __inner__: { + hidden: schema.hidden, + condition, + }, + }); } catch (e) { return engine.createElement(engine.getFaultComponent(), { error: e, diff --git a/packages/renderer-core/tests/hoc/leaf.test.tsx b/packages/renderer-core/tests/hoc/leaf.test.tsx index 0e594bc5a..86675c771 100644 --- a/packages/renderer-core/tests/hoc/leaf.test.tsx +++ b/packages/renderer-core/tests/hoc/leaf.test.tsx @@ -35,7 +35,8 @@ const baseRenderer: any = { __container: { rerender: () => { rerenderCount = 1 + rerenderCount; - } + }, + autoRepaintNode: true, }, documentId: '01' }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 19d9bfa70..9b540cc5f 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -21,3 +21,4 @@ export * from './code-result'; export * from './assets'; export * as GlobalEvent from './event'; export * from './disposable'; +export * from './start-transaction'; diff --git a/packages/types/src/start-transaction.ts b/packages/types/src/start-transaction.ts new file mode 100644 index 000000000..46547aa44 --- /dev/null +++ b/packages/types/src/start-transaction.ts @@ -0,0 +1,4 @@ +export enum TransitionType { + /** 节点更新后重绘处理 */ + REPAINT +} \ No newline at end of file diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 1277f4481..42f157294 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -26,3 +26,4 @@ export * from './node-helper'; export * from './clone-enumerable-property'; export * from './logger'; export * as css from './css-helper'; +export { transactionManager } from './start-transaction'; diff --git a/packages/utils/src/start-transaction.ts b/packages/utils/src/start-transaction.ts new file mode 100644 index 000000000..8c7c0db3e --- /dev/null +++ b/packages/utils/src/start-transaction.ts @@ -0,0 +1,31 @@ +import { TransitionType } from '@alilc/lowcode-types'; +import { transaction } from 'mobx'; +import EventEmitter from 'events'; + +class TransactionManager { + emitter = new EventEmitter(); + + startTransaction = (fn: () => void, type: TransitionType = TransitionType.REPAINT): void => { + this.emitter.emit(`[${type}]startTransaction`); + transaction(fn); + this.emitter.emit(`[${type}]endTransaction`); + }; + + onStartTransaction = (fn: () => void, type: TransitionType = TransitionType.REPAINT): () => void => { + this.emitter.on(`[${type}]startTransaction`, fn); + return () => { + this.emitter.off(`[${type}]startTransaction`, fn); + }; + }; + + onEndTransaction = (fn: () => void, type: TransitionType = TransitionType.REPAINT): () => void => { + this.emitter.on(`[${type}]endTransaction`, fn); + return () => { + this.emitter.off(`[${type}]endTransaction`, fn); + }; + }; +} + +export const transactionManager = new TransactionManager(); + +export default transactionManager; \ No newline at end of file