diff --git a/packages/designer/jest.config.js b/packages/designer/jest.config.js index 553eeb33f..b32307826 100644 --- a/packages/designer/jest.config.js +++ b/packages/designer/jest.config.js @@ -6,7 +6,7 @@ module.exports = { // // '^.+\\.(ts|tsx)$': 'ts-jest', // // '^.+\\.(js|jsx)$': 'babel-jest', // }, - // testMatch: ['**/builtin-hotkey.test.ts'], + testMatch: ['**/bugs/*.test.ts'], // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], transformIgnorePatterns: [ `/node_modules/(?!${esModules})/`, diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 6b9e70f13..c493da22d 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -198,7 +198,7 @@ export class DocumentModel { * 根据 id 获取节点 */ getNodeCount(): number { - return this._nodesMap.size; + return this._nodesMap?.size; } /** diff --git a/packages/designer/tests/bugs/misc.ts b/packages/designer/tests/bugs/misc.ts.bak similarity index 100% rename from packages/designer/tests/bugs/misc.ts rename to packages/designer/tests/bugs/misc.ts.bak diff --git a/packages/designer/tests/bugs/prop-variable-jse.test.ts b/packages/designer/tests/bugs/prop-variable-jse.test.ts new file mode 100644 index 000000000..c830f8fbd --- /dev/null +++ b/packages/designer/tests/bugs/prop-variable-jse.test.ts @@ -0,0 +1,72 @@ +// @ts-nocheck +import { Editor } from '@ali/lowcode-editor-core'; +import { isJSBlock, TransformStage } from '@ali/lowcode-types'; +import { isPlainObject, isVariable } from '@ali/lowcode-utils'; +import '../fixtures/window'; +import { Designer } from '../../src/designer/designer'; +import { DocumentModel } from '../../src/document/document-model'; +import { Project } from '../../src/project/project'; +import formSchema from '../fixtures/schema/form'; + +/** + * bug 背景: + * Prop 在每次 setValue 时都会调用 dispose 方法用于重新计算子 Prop,我认为在 Node 未完成初始化之前的 dispose 都是 + * 无意义的,所以增加了判断条件来调用 dispose,结果导致了 variable 结果没有正确转成 JSExpression 结构。 + * + * 因为 propsReducer 的 Init / Upgrade 阶段依然可以更改 props,且此时的 Node 也未完成初始化,不调用 dispose 则导致新的 Prop 结构无法生效 + */ + +function upgradePropsReducer(props: any): any { + if (!props || !isPlainObject(props)) { + return props; + } + + if (isJSBlock(props)) { + if (props.value.componentName === 'Slot') { + return { + type: 'JSSlot', + title: (props.value.props as any)?.slotTitle, + name: (props.value.props as any)?.slotName, + value: props.value.children, + }; + } else { + return props.value; + } + } + if (isVariable(props)) { + return { + type: 'JSExpression', + value: props.variable, + mock: props.value, + }; + } + const newProps: any = {}; + Object.keys(props).forEach((key) => { + if (/^__slot__/.test(key) && props[key] === true) { + return; + } + newProps[key] = upgradePropsReducer(props[key]); + }); + return newProps; +} + +describe('Node 方法测试', () => { + let editor: Editor; + let designer: Designer; + let project: Project; + let doc: DocumentModel; + + it('原始 prop 值是 variable 结构,通过一个 propsReducer 转成了 JSExpression 结构', () => { + editor = new Editor(); + designer = new Designer({ editor }); + designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade); + project = designer.project; + doc = new DocumentModel(project, formSchema); + + const form = doc.getNode('form'); + expect(form.getPropValue('dataSource')).toEqual({ + type: 'JSExpression', + value: 'state.formData', + }) + }); +}); diff --git a/packages/designer/tests/bugs/why.md b/packages/designer/tests/bugs/why.md new file mode 100644 index 000000000..519dee1b5 --- /dev/null +++ b/packages/designer/tests/bugs/why.md @@ -0,0 +1,6 @@ +背景: +在 UT 的基础上,希望借助一些 Bug 修复来完成场景测试,从而进一步增强稳定性。 +至少在真正的 E2E 测试来临之前,我们保证不会重复犯两次相同的错误。 + +做法: +Bugs 文件夹每个文件记录一个 bug 修复的场景测试~ \ No newline at end of file diff --git a/packages/designer/tests/document/node/node.test.ts b/packages/designer/tests/document/node/node.test.ts index 18f40e215..ee3928a64 100644 --- a/packages/designer/tests/document/node/node.test.ts +++ b/packages/designer/tests/document/node/node.test.ts @@ -1,4 +1,4 @@ -// @ts-ignore +// @ts-nocheck import '../../fixtures/window'; import { set, delayObxTick, delay } from '../../utils'; import { Editor } from '@ali/lowcode-editor-core'; @@ -24,7 +24,6 @@ import rootHeaderMetadata from '../../fixtures/component-metadata/root-header'; import rootContentMetadata from '../../fixtures/component-metadata/root-content'; import rootFooterMetadata from '../../fixtures/component-metadata/root-footer'; - describe('Node 方法测试', () => { let editor: Editor; let designer: Designer; @@ -185,12 +184,16 @@ describe('Node 方法测试', () => { it('null', () => { expect( - doc.rootNode?.getSuitablePlace.call({ contains: () => false, isContainer: () => false, isRoot: () => false }), + doc.rootNode?.getSuitablePlace.call({ + contains: () => false, + isContainer: () => false, + isRoot: () => false, + }), ).toBeNull(); }); }); - it('removeChild / replaceWith / replaceChild / onChildrenChange / mergeChildren', () => { + it('removeChild / replaceWith / replaceChild', () => { const firstBtn = doc.getNode('node_k1ow3cbn')!; firstBtn.select(); @@ -254,7 +257,7 @@ describe('Node 方法测试', () => { }); }); - it('setVisible / getVisible / onVisibleChange', async () => { + it('setVisible / getVisible / onVisibleChange', () => { const mockFn = jest.fn(); const firstBtn = doc.getNode('node_k1ow3cbn')!; const off = firstBtn.onVisibleChange(mockFn); @@ -265,7 +268,6 @@ describe('Node 方法测试', () => { firstBtn.setVisible(false); - await delayObxTick(); expect(firstBtn.getVisible()).toBeFalsy(); expect(mockFn).toHaveBeenCalledTimes(2); expect(mockFn).toHaveBeenCalledWith(false); @@ -273,7 +275,22 @@ describe('Node 方法测试', () => { off(); mockFn.mockClear(); firstBtn.setVisible(true); - await delayObxTick(); + expect(mockFn).not.toHaveBeenCalled(); + }); + + it('onPropChange', () => { + const mockFn = jest.fn(); + const firstBtn = doc.getNode('node_k1ow3cbn')!; + const off = firstBtn.onPropChange(mockFn); + + firstBtn.setPropValue('x', 1); + expect(mockFn).toHaveBeenCalledTimes(1); + firstBtn.setPropValue('x', 2); + expect(mockFn).toHaveBeenCalledTimes(2); + + off(); + mockFn.mockClear(); + firstBtn.setPropValue('x', 3); expect(mockFn).not.toHaveBeenCalled(); }); @@ -292,7 +309,9 @@ describe('Node 方法测试', () => { const pageMeta = designer.getComponentMeta('Page'); const autorunMockFn = jest.fn(); - set(pageMeta, '_transformedMetadata.experimental.autoruns', [{ name: 'a', autorun: autorunMockFn }]); + set(pageMeta, '_transformedMetadata.experimental.autoruns', [ + { name: 'a', autorun: autorunMockFn }, + ]); const initialChildrenMockFn = jest.fn(); set(pageMeta, '_transformedMetadata.experimental.initialChildren', initialChildrenMockFn); doc.createNode({ componentName: 'Page', props: { a: 1 } }); diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts index 1b55890f1..76ed65af6 100644 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ b/packages/designer/tests/document/node/props/prop.test.ts @@ -200,9 +200,11 @@ describe('Prop 类测试', () => { // 更新 slot slotProp.setValue({ type: 'JSSlot', - value: [{ - componentName: 'Form', - }] + value: [ + { + componentName: 'Form', + }, + ], }); expect(slotNodeImportMockFn).toBeCalled(); @@ -276,19 +278,20 @@ describe('Prop 类测试', () => { expect(prop.get('z.z1')?.getValue()).toBe(1); expect(prop.get('z.z2')?.getValue()).toBe('str'); - const fromStashProp = prop.get('l'); - const fromStashNestedProp = prop.get('m.m1'); - fromStashProp.setValue('fromStashProp'); - fromStashNestedProp?.setValue('fromStashNestedProp'); + const newlyCreatedProp = prop.get('l', true); + const newlyCreatedNestedProp = prop.get('m.m1', true); + newlyCreatedProp.setValue('newlyCreatedProp'); + newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp'); - await delayObxTick(); - expect(prop.get('l').getValue()).toBe('fromStashProp'); - expect(prop.get('m.m1').getValue()).toBe('fromStashNestedProp'); + expect(prop.get('l').getValue()).toBe('newlyCreatedProp'); + expect(prop.get('m.m1').getValue()).toBe('newlyCreatedNestedProp'); + + const newlyCreatedNestedProp2 = prop.get('m.m2', true); + // .m2 的值为 undefined,导出时将会被移除 + expect(prop.get('m').getValue()).toEqual({ m1: 'newlyCreatedNestedProp' }); }); it('export', () => { - // TODO: 需要访问一下才能触发构造 _items - prop.items; expect(prop.export()).toEqual({ a: 1, b: 'str', diff --git a/packages/designer/tests/document/node/props/props.test.ts b/packages/designer/tests/document/node/props/props.test.ts index eb49568a5..360db47eb 100644 --- a/packages/designer/tests/document/node/props/props.test.ts +++ b/packages/designer/tests/document/node/props/props.test.ts @@ -2,32 +2,42 @@ import '../../../fixtures/window'; import { set, delayObxTick } from '../../../utils'; import { Editor } from '@ali/lowcode-editor-core'; -import { Props, getConvertedExtraKey, getOriginalExtraKey, Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/props'; +import { + Props, + getConvertedExtraKey, + getOriginalExtraKey, + Prop, + isProp, + isValidArrayIndex, +} from '../../../../src/document/node/props/props'; import { Designer } from '../../../../src/designer/designer'; import { Project } from '../../../../src/project/project'; import { DocumentModel } from '../../../../src/document/document-model'; import { TransformStage } from '@ali/lowcode-types'; - const mockedOwner = { componentName: 'Page' }; describe('Props 类测试', () => { let props: Props; beforeEach(() => { - props = new Props(mockedOwner, { - a: 1, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', + props = new Props( + mockedOwner, + { + a: 1, + b: 'str', + c: true, + d: { + type: 'JSExpression', + value: 'state.a', + }, + z: { + z1: 1, + z2: 'str', + }, }, - z: { - z1: 1, - z2: 'str', - }, - }, { condition: true }); + { condition: true }, + ); }); afterEach(() => { props.purge(); @@ -49,7 +59,6 @@ describe('Props 类测试', () => { z2: 'str', }); - expect(props.getPropValue('a')).toBe(1); props.setPropValue('a', 2); expect(props.getPropValue('a')).toBe(2); @@ -59,14 +68,15 @@ describe('Props 类测试', () => { expect(props.get('z.z1')?.getValue()).toBe(1); expect(props.get('z.z2')?.getValue()).toBe('str'); - const fromStashProp = props.get('l', true); - const fromStashNestedProp = props.get('m.m1', true); - fromStashProp.setValue('fromStashProp'); - fromStashNestedProp?.setValue('fromStashNestedProp'); + const notCreatedProp = props.get('i'); + expect(notCreatedProp).toBeNull(); + const newlyCreatedProp = props.get('l', true); + const newlyCreatedNestedProp = props.get('m.m1', true); + newlyCreatedProp.setValue('newlyCreatedProp'); + newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp'); - await delayObxTick(); - expect(props.get('l').getValue()).toBe('fromStashProp'); - expect(props.get('m.m1').getValue()).toBe('fromStashNestedProp'); + expect(props.get('l').getValue()).toBe('newlyCreatedProp'); + expect(props.get('m.m1').getValue()).toBe('newlyCreatedNestedProp'); }); it('export', () => { @@ -119,11 +129,66 @@ describe('Props 类测试', () => { }); }); + it('export - remove undefined items', () => { + props.import( + { + a: 1, + }, + { loop: false }, + ); + props.setPropValue('x', undefined); + expect(props.export()).toEqual({ + props: { + a: 1, + }, + extras: { + loop: false, + }, + }); + + props.setPropValue('x', 2); + expect(props.export()).toEqual({ + props: { + a: 1, + x: 2, + }, + extras: { + loop: false, + }, + }); + + props.setPropValue('y.z', undefined); + expect(props.export()).toEqual({ + props: { + a: 1, + x: 2, + }, + extras: { + loop: false, + }, + }); + + props.setPropValue('y.z', 2); + expect(props.export()).toEqual({ + props: { + a: 1, + x: 2, + y: { z: 2 }, + }, + extras: { + loop: false, + }, + }); + }); + it('import', () => { - props.import({ - x: 1, - y: true, - }, { loop: false }); + props.import( + { + x: 1, + y: true, + }, + { loop: false }, + ); expect(props.export()).toEqual({ props: { x: 1, @@ -173,19 +238,19 @@ describe('Props 类测试', () => { expect(mockedFn).toHaveBeenCalledTimes(6); mockedFn.mockClear(); - props.forEach(item => { + props.forEach((item) => { mockedFn(); }); expect(mockedFn).toHaveBeenCalledTimes(6); mockedFn.mockClear(); - props.map(item => { + props.map((item) => { return mockedFn(); }); expect(mockedFn).toHaveBeenCalledTimes(6); mockedFn.mockClear(); - props.filter(item => { + props.filter((item) => { return mockedFn(); }); expect(mockedFn).toHaveBeenCalledTimes(6); @@ -229,7 +294,6 @@ describe('Props 类测试', () => { }); }); - describe('其他函数', () => { it('getConvertedExtraKey', () => { expect(getConvertedExtraKey()).toBe(''); diff --git a/packages/rax-simulator-renderer/src/renderer-view.tsx b/packages/rax-simulator-renderer/src/renderer-view.tsx index 711053e80..8444e73ef 100644 --- a/packages/rax-simulator-renderer/src/renderer-view.tsx +++ b/packages/rax-simulator-renderer/src/renderer-view.tsx @@ -187,7 +187,7 @@ class Renderer extends Component<{ render() { const { documentInstance } = this.props; - const { container } = documentInstance; + const { container, document } = documentInstance; const { designMode, device } = container; const { rendererContainer: renderer } = this.props; this.startTime = Date.now(); @@ -209,6 +209,7 @@ class Renderer extends Component<{ onCompGetRef={(schema: any, ref: any) => { documentInstance.mountInstance(schema.id, ref); }} + documentId={document.id} getNode={(id: string) => documentInstance.getNode(id) as any} customCreateElement={(Component: any, props: any, children: any) => { const { __id, ...viewProps } = props; diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx index 3c0978aa3..ec1ebdf4e 100644 --- a/packages/react-simulator-renderer/src/renderer-view.tsx +++ b/packages/react-simulator-renderer/src/renderer-view.tsx @@ -154,7 +154,7 @@ class Renderer extends Component<{ render() { const { documentInstance, rendererContainer: renderer } = this.props; - const { container } = documentInstance; + const { container, document } = documentInstance; const { designMode, device, locale } = container; const messages = container.context?.utils?.i18n?.messages || {}; this.startTime = Date.now(); @@ -172,6 +172,7 @@ class Renderer extends Component<{ appHelper={container.context} designMode={designMode} device={device} + documentId={document.id} suspended={renderer.suspended} self={renderer.scope} getNode={(id: string) => documentInstance.getNode(id) as Node} diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index f40522a58..c6dde182f 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -50,16 +50,30 @@ enum RerenderType { } // 缓存 Leaf 层组件,防止重新渲染问题 -const leafComponentCache: { +let leafComponentCaches: { [componentName: string]: any; } = {}; + +let cacheDocumentId: any; + +function clearCaches(curDocumentId: any, { + __debug, +}: any) { + if (cacheDocumentId === curDocumentId) { + return; + } + __debug(`DocumentId changed to ${curDocumentId}, clear caches!`); + cacheDocumentId = curDocumentId; + leafComponentCaches = {}; +} + // 缓存导致 rerender 的订阅事件 const rerenderEventCache: { [componentId: string]: any; } = {}; /** 部分没有渲染的 node 节点进行兜底处理 or 渲染方式没有渲染 LeafWrapper */ -function makeRerenderEvent({ +function initRerenderEvent({ schema, __debug, container, @@ -98,6 +112,7 @@ function clearRerenderEvent(id: string): void { if (!rerenderEventCache[id]) { rerenderEventCache[id] = { clear: true, + dispose: [], }; return; } @@ -119,27 +134,34 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } = baseRenderer; const engine = baseRenderer.context.engine; const host: BuiltinSimulatorHost = baseRenderer.props.__host; + const curDocumentId = baseRenderer.props?.documentId; const getNode = baseRenderer.props?.getNode; const container: BuiltinSimulatorHost = baseRenderer.props.__container; const editor = host?.designer?.editor; - const { Component } = adapter.getRuntime(); + const { Component, forwardRef } = adapter.getRuntime(); + + if (curDocumentId !== cacheDocumentId) { + clearCaches(curDocumentId, { + __debug, + }); + } if (!isReactComponent(Comp)) { console.error(`${schema.componentName} component may be has errors: `, Comp); } - makeRerenderEvent({ + initRerenderEvent({ schema, __debug, container, getNode, }); - if (leafComponentCache[schema.componentName]) { - return leafComponentCache[schema.componentName]; + if (leafComponentCaches[schema.componentName]) { + return leafComponentCaches[schema.componentName]; } - class LeafWrapper extends Component { + class LeafHoc extends Component { recordInfo: { startTime?: number | null; type?: string; @@ -156,7 +178,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, { return; } const endTime = Date.now(); - const nodeCount = host.designer.currentDocument?.getNodeCount?.(); + const nodeCount = host?.designer?.currentDocument?.getNodeCount?.(); const componentName = this.recordInfo.node?.componentName || this.leaf?.componentName || 'UnknownComponent'; editor?.emit(GlobalEvent.Node.Rerender, { componentName, @@ -171,18 +193,39 @@ export function leafWrapper(Comp: types.IBaseRenderer, { this.recordTime(); } + componentDidMount() { + this.recordTime(); + } + + get childrenMap(): any { + const map = new Map(); + + if (!this.hasChildren) { + return map; + } + + this.children.forEach((d: any) => { + if (Array.isArray(d)) { + map.set(d[0].props.componentId, d); + return; + } + map.set(d.props.componentId, d); + }); + + return map; + } + constructor(props: IProps, context: any) { super(props, context); // 监听以下事件,当变化时更新自己 - clearRerenderEvent(schema.id); + __debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`); + clearRerenderEvent(this.props.componentId); this.initOnPropsChangeEvent(); this.initOnChildrenChangeEvent(); this.initOnVisibleChangeEvent(); - __debug(`${schema.componentName}[${schema.id}] leaf render in SimulatorRendererView`); this.state = { nodeChildren: null, childrenInState: false, - __tag: props.__tag, }; } @@ -198,17 +241,11 @@ export function leafWrapper(Comp: types.IBaseRenderer, { // } componentWillReceiveProps(nextProps: any) { - const { _leaf, __tag, children, ...rest } = nextProps; - if (nextProps.__tag === this.state.__tag) { - const nextProps = getProps(this.leaf?.export?.(TransformStage.Render) as types.ISchema, Comp, componentInfo); - this.setState({ - nodeProps: { - ...rest, - ...nextProps, - }, - }); + const { _leaf } = nextProps; + if (nextProps.__tag === this.props.__tag) { return null; } + if (_leaf && this.leaf && _leaf !== this.leaf) { this.disposeFunctions.forEach(fn => fn()); this.disposeFunctions = []; @@ -218,10 +255,9 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } this.setState({ - nodeChildren: children, - nodeProps: rest, - childrenInState: true, - __tag, + nodeChildren: null, + nodeProps: {}, + childrenInState: false, }); } @@ -300,7 +336,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, { // } this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node); __debug(`${leaf} component trigger onChildrenChange event`); - const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, Comp); + const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, Comp, this.childrenMap); this.setState({ nodeChildren: nextChild, childrenInState: true, @@ -359,17 +395,28 @@ export function leafWrapper(Comp: types.IBaseRenderer, { return null; } + const { + ref, + ...rest + } = this.props; + const compProps = { - ...this.props, + ...rest, ...(this.state.nodeProps || {}), children: [], __id: this.props.componentId, + ref: this.props.forwardedRef, }; return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null); } } + const LeafWrapper = forwardRef((props: any, ref: any) => ( + // @ts-ignore + + )); + if (typeof Comp === 'object') { const compExtraPropertyNames = Object.getOwnPropertyNames(Comp).filter(d => !compDefaultPropertyNames.includes(d)); @@ -382,7 +429,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, { LeafWrapper.displayName = (Comp as any).displayName; - leafComponentCache[schema.componentName] = LeafWrapper; + leafComponentCaches[schema.componentName] = LeafWrapper; return LeafWrapper; } \ No newline at end of file diff --git a/packages/renderer-core/src/renderer/addon.tsx b/packages/renderer-core/src/renderer/addon.tsx index cedb3e065..7720051a4 100644 --- a/packages/renderer-core/src/renderer/addon.tsx +++ b/packages/renderer-core/src/renderer/addon.tsx @@ -77,7 +77,7 @@ export default function addonRendererFactory() { return '插件 schema 结构异常!'; } - this.__debug(`render - ${__schema.fileName}`); + this.__debug(`${AddonRenderer.dislayName} render - ${__schema.fileName}`); this.__generateCtx({ component: this, }); diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx index 87f4ad919..f234677ac 100644 --- a/packages/renderer-core/src/renderer/base.tsx +++ b/packages/renderer-core/src/renderer/base.tsx @@ -521,7 +521,7 @@ export default function baseRenererFactory() { props.key = props.__id; } - let child: any = this.__getSchemaChildrenVirtualDom(schema, Comp); + let child: any = parentInfo.componentChildren || this.__getSchemaChildrenVirtualDom(schema, Comp); const renderComp = (props: any) => engine.createElement(Comp, props, child); // 设计模式下的特殊处理 if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) { @@ -580,7 +580,7 @@ export default function baseRenererFactory() { .map((d: IComponentHoc) => d.hoc); } - __getSchemaChildrenVirtualDom = (schema: ISchema, Comp: any) => { + __getSchemaChildrenVirtualDom = (schema: ISchema, Comp: any, childrenMap?: Map) => { let _children = this.getSchemaChildren(schema); let children: any = []; @@ -596,6 +596,8 @@ export default function baseRenererFactory() { { schema, Comp, + // 有 childrenMap 情况下,children 只计算第一层,不需要遍历多层。 + componentChildren: childrenMap?.get(_child.id)?.props.children || null, }, ); @@ -834,11 +836,16 @@ export default function baseRenererFactory() { componentInfo: {}, }); const { className } = data; + const otherProps: any = {}; const { engine } = this.context || {}; if (!engine) { return null; } + if (this._designModeIsDesign) { + otherProps.__tag = Math.random(); + } + const child = engine.createElement( Comp, { @@ -847,6 +854,7 @@ export default function baseRenererFactory() { ref: this.__getRef, className: classnames(getFileCssName(__schema?.fileName), className, this.props.className), __id: __schema?.id, + ...otherProps, }, this.__createDom(), ); diff --git a/packages/renderer-core/src/renderer/block.tsx b/packages/renderer-core/src/renderer/block.tsx index 599410c4d..d6d230c4d 100644 --- a/packages/renderer-core/src/renderer/block.tsx +++ b/packages/renderer-core/src/renderer/block.tsx @@ -23,7 +23,7 @@ export default function blockRendererFactory() { return '区块 schema 结构异常!'; } - this.__debug(`render - ${__schema.fileName}`); + this.__debug(`${BlockRenderer.dislayName} render - ${__schema.fileName}`); this.__generateCtx({}); this.__render(); diff --git a/packages/renderer-core/src/renderer/component.tsx b/packages/renderer-core/src/renderer/component.tsx index 229958f46..81b40dc32 100644 --- a/packages/renderer-core/src/renderer/component.tsx +++ b/packages/renderer-core/src/renderer/component.tsx @@ -23,7 +23,7 @@ export default function componentRendererFactory() { if (this.__checkSchema(__schema)) { return '自定义组件 schema 结构异常!'; } - this.__debug(`render - ${__schema.fileName}`); + this.__debug(`${CompRenderer.dislayName} render - ${__schema.fileName}`); this.__generateCtx({ component: this, diff --git a/packages/renderer-core/src/renderer/page.tsx b/packages/renderer-core/src/renderer/page.tsx index 3c7be518f..6e7f290ed 100644 --- a/packages/renderer-core/src/renderer/page.tsx +++ b/packages/renderer-core/src/renderer/page.tsx @@ -36,7 +36,7 @@ export default function pageRendererFactory() { if (this.__checkSchema(__schema)) { return '页面schema结构异常!'; } - this.__debug(`render - ${__schema.fileName}`); + this.__debug(`${PageRenderer.dislayName} render - ${__schema.fileName}`); this.__bindCustomMethods(this.props); this.__initDataSource(this.props); diff --git a/packages/renderer-core/src/renderer/temp.tsx b/packages/renderer-core/src/renderer/temp.tsx index fc3e3c48d..061e561b2 100644 --- a/packages/renderer-core/src/renderer/temp.tsx +++ b/packages/renderer-core/src/renderer/temp.tsx @@ -48,7 +48,7 @@ export default function tempRendererFactory() { return '下钻编辑 schema 结构异常!'; } - this.__debug(`render - ${__schema.fileName}`); + this.__debug(`${TempRenderer.dislayName} render - ${__schema.fileName}`); return this.__renderContent(this.__renderContextProvider({ __ctx })); } diff --git a/packages/renderer-core/src/types/index.ts b/packages/renderer-core/src/types/index.ts index e5e68cf3b..e1246082a 100644 --- a/packages/renderer-core/src/types/index.ts +++ b/packages/renderer-core/src/types/index.ts @@ -63,6 +63,7 @@ export interface IInfo { schema: ISchema; Comp: any; componentInfo?: any; + componentChildren?: any } export interface JSExpression {