Merge commit 'd53ba538b1c3f9989d911ac96dc5f3eadb561a3c' into def_releases_2021092316393939_ali-lowcode_ali-lowcode-engine/1.0.69

This commit is contained in:
tbfed 2021-10-09 15:04:29 +08:00
commit 4e22fc5c09
18 changed files with 310 additions and 88 deletions

View File

@ -6,7 +6,7 @@ module.exports = {
// // '^.+\\.(ts|tsx)$': 'ts-jest', // // '^.+\\.(ts|tsx)$': 'ts-jest',
// // '^.+\\.(js|jsx)$': 'babel-jest', // // '^.+\\.(js|jsx)$': 'babel-jest',
// }, // },
// testMatch: ['**/builtin-hotkey.test.ts'], testMatch: ['**/bugs/*.test.ts'],
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'], // testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [ transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`, `/node_modules/(?!${esModules})/`,

View File

@ -198,7 +198,7 @@ export class DocumentModel {
* id * id
*/ */
getNodeCount(): number { getNodeCount(): number {
return this._nodesMap.size; return this._nodesMap?.size;
} }
/** /**

View File

@ -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',
})
});
});

View File

@ -0,0 +1,6 @@
背景:
在 UT 的基础上,希望借助一些 Bug 修复来完成场景测试,从而进一步增强稳定性。
至少在真正的 E2E 测试来临之前,我们保证不会重复犯两次相同的错误。
做法:
Bugs 文件夹每个文件记录一个 bug 修复的场景测试~

View File

@ -1,4 +1,4 @@
// @ts-ignore // @ts-nocheck
import '../../fixtures/window'; import '../../fixtures/window';
import { set, delayObxTick, delay } from '../../utils'; import { set, delayObxTick, delay } from '../../utils';
import { Editor } from '@ali/lowcode-editor-core'; 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 rootContentMetadata from '../../fixtures/component-metadata/root-content';
import rootFooterMetadata from '../../fixtures/component-metadata/root-footer'; import rootFooterMetadata from '../../fixtures/component-metadata/root-footer';
describe('Node 方法测试', () => { describe('Node 方法测试', () => {
let editor: Editor; let editor: Editor;
let designer: Designer; let designer: Designer;
@ -185,12 +184,16 @@ describe('Node 方法测试', () => {
it('null', () => { it('null', () => {
expect( expect(
doc.rootNode?.getSuitablePlace.call({ contains: () => false, isContainer: () => false, isRoot: () => false }), doc.rootNode?.getSuitablePlace.call({
contains: () => false,
isContainer: () => false,
isRoot: () => false,
}),
).toBeNull(); ).toBeNull();
}); });
}); });
it('removeChild / replaceWith / replaceChild / onChildrenChange / mergeChildren', () => { it('removeChild / replaceWith / replaceChild', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
firstBtn.select(); firstBtn.select();
@ -254,7 +257,7 @@ describe('Node 方法测试', () => {
}); });
}); });
it('setVisible / getVisible / onVisibleChange', async () => { it('setVisible / getVisible / onVisibleChange', () => {
const mockFn = jest.fn(); const mockFn = jest.fn();
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
const off = firstBtn.onVisibleChange(mockFn); const off = firstBtn.onVisibleChange(mockFn);
@ -265,7 +268,6 @@ describe('Node 方法测试', () => {
firstBtn.setVisible(false); firstBtn.setVisible(false);
await delayObxTick();
expect(firstBtn.getVisible()).toBeFalsy(); expect(firstBtn.getVisible()).toBeFalsy();
expect(mockFn).toHaveBeenCalledTimes(2); expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith(false); expect(mockFn).toHaveBeenCalledWith(false);
@ -273,7 +275,22 @@ describe('Node 方法测试', () => {
off(); off();
mockFn.mockClear(); mockFn.mockClear();
firstBtn.setVisible(true); 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(); expect(mockFn).not.toHaveBeenCalled();
}); });
@ -292,7 +309,9 @@ describe('Node 方法测试', () => {
const pageMeta = designer.getComponentMeta('Page'); const pageMeta = designer.getComponentMeta('Page');
const autorunMockFn = jest.fn(); 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(); const initialChildrenMockFn = jest.fn();
set(pageMeta, '_transformedMetadata.experimental.initialChildren', initialChildrenMockFn); set(pageMeta, '_transformedMetadata.experimental.initialChildren', initialChildrenMockFn);
doc.createNode({ componentName: 'Page', props: { a: 1 } }); doc.createNode({ componentName: 'Page', props: { a: 1 } });

View File

@ -200,9 +200,11 @@ describe('Prop 类测试', () => {
// 更新 slot // 更新 slot
slotProp.setValue({ slotProp.setValue({
type: 'JSSlot', type: 'JSSlot',
value: [{ value: [
componentName: 'Form', {
}] componentName: 'Form',
},
],
}); });
expect(slotNodeImportMockFn).toBeCalled(); expect(slotNodeImportMockFn).toBeCalled();
@ -276,19 +278,20 @@ describe('Prop 类测试', () => {
expect(prop.get('z.z1')?.getValue()).toBe(1); expect(prop.get('z.z1')?.getValue()).toBe(1);
expect(prop.get('z.z2')?.getValue()).toBe('str'); expect(prop.get('z.z2')?.getValue()).toBe('str');
const fromStashProp = prop.get('l'); const newlyCreatedProp = prop.get('l', true);
const fromStashNestedProp = prop.get('m.m1'); const newlyCreatedNestedProp = prop.get('m.m1', true);
fromStashProp.setValue('fromStashProp'); newlyCreatedProp.setValue('newlyCreatedProp');
fromStashNestedProp?.setValue('fromStashNestedProp'); newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
await delayObxTick(); expect(prop.get('l').getValue()).toBe('newlyCreatedProp');
expect(prop.get('l').getValue()).toBe('fromStashProp'); expect(prop.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
expect(prop.get('m.m1').getValue()).toBe('fromStashNestedProp');
const newlyCreatedNestedProp2 = prop.get('m.m2', true);
// .m2 的值为 undefined导出时将会被移除
expect(prop.get('m').getValue()).toEqual({ m1: 'newlyCreatedNestedProp' });
}); });
it('export', () => { it('export', () => {
// TODO: 需要访问一下才能触发构造 _items
prop.items;
expect(prop.export()).toEqual({ expect(prop.export()).toEqual({
a: 1, a: 1,
b: 'str', b: 'str',

View File

@ -2,32 +2,42 @@
import '../../../fixtures/window'; import '../../../fixtures/window';
import { set, delayObxTick } from '../../../utils'; import { set, delayObxTick } from '../../../utils';
import { Editor } from '@ali/lowcode-editor-core'; 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 { Designer } from '../../../../src/designer/designer';
import { Project } from '../../../../src/project/project'; import { Project } from '../../../../src/project/project';
import { DocumentModel } from '../../../../src/document/document-model'; import { DocumentModel } from '../../../../src/document/document-model';
import { TransformStage } from '@ali/lowcode-types'; import { TransformStage } from '@ali/lowcode-types';
const mockedOwner = { componentName: 'Page' }; const mockedOwner = { componentName: 'Page' };
describe('Props 类测试', () => { describe('Props 类测试', () => {
let props: Props; let props: Props;
beforeEach(() => { beforeEach(() => {
props = new Props(mockedOwner, { props = new Props(
a: 1, mockedOwner,
b: 'str', {
c: true, a: 1,
d: { b: 'str',
type: 'JSExpression', c: true,
value: 'state.a', d: {
type: 'JSExpression',
value: 'state.a',
},
z: {
z1: 1,
z2: 'str',
},
}, },
z: { { condition: true },
z1: 1, );
z2: 'str',
},
}, { condition: true });
}); });
afterEach(() => { afterEach(() => {
props.purge(); props.purge();
@ -49,7 +59,6 @@ describe('Props 类测试', () => {
z2: 'str', z2: 'str',
}); });
expect(props.getPropValue('a')).toBe(1); expect(props.getPropValue('a')).toBe(1);
props.setPropValue('a', 2); props.setPropValue('a', 2);
expect(props.getPropValue('a')).toBe(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.z1')?.getValue()).toBe(1);
expect(props.get('z.z2')?.getValue()).toBe('str'); expect(props.get('z.z2')?.getValue()).toBe('str');
const fromStashProp = props.get('l', true); const notCreatedProp = props.get('i');
const fromStashNestedProp = props.get('m.m1', true); expect(notCreatedProp).toBeNull();
fromStashProp.setValue('fromStashProp'); const newlyCreatedProp = props.get('l', true);
fromStashNestedProp?.setValue('fromStashNestedProp'); const newlyCreatedNestedProp = props.get('m.m1', true);
newlyCreatedProp.setValue('newlyCreatedProp');
newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
await delayObxTick(); expect(props.get('l').getValue()).toBe('newlyCreatedProp');
expect(props.get('l').getValue()).toBe('fromStashProp'); expect(props.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
expect(props.get('m.m1').getValue()).toBe('fromStashNestedProp');
}); });
it('export', () => { 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', () => { it('import', () => {
props.import({ props.import(
x: 1, {
y: true, x: 1,
}, { loop: false }); y: true,
},
{ loop: false },
);
expect(props.export()).toEqual({ expect(props.export()).toEqual({
props: { props: {
x: 1, x: 1,
@ -173,19 +238,19 @@ describe('Props 类测试', () => {
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear(); mockedFn.mockClear();
props.forEach(item => { props.forEach((item) => {
mockedFn(); mockedFn();
}); });
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear(); mockedFn.mockClear();
props.map(item => { props.map((item) => {
return mockedFn(); return mockedFn();
}); });
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear(); mockedFn.mockClear();
props.filter(item => { props.filter((item) => {
return mockedFn(); return mockedFn();
}); });
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
@ -229,7 +294,6 @@ describe('Props 类测试', () => {
}); });
}); });
describe('其他函数', () => { describe('其他函数', () => {
it('getConvertedExtraKey', () => { it('getConvertedExtraKey', () => {
expect(getConvertedExtraKey()).toBe(''); expect(getConvertedExtraKey()).toBe('');

View File

@ -187,7 +187,7 @@ class Renderer extends Component<{
render() { render() {
const { documentInstance } = this.props; const { documentInstance } = this.props;
const { container } = documentInstance; const { container, document } = documentInstance;
const { designMode, device } = container; const { designMode, device } = container;
const { rendererContainer: renderer } = this.props; const { rendererContainer: renderer } = this.props;
this.startTime = Date.now(); this.startTime = Date.now();
@ -209,6 +209,7 @@ class Renderer extends Component<{
onCompGetRef={(schema: any, ref: any) => { onCompGetRef={(schema: any, ref: any) => {
documentInstance.mountInstance(schema.id, ref); documentInstance.mountInstance(schema.id, ref);
}} }}
documentId={document.id}
getNode={(id: string) => documentInstance.getNode(id) as any} getNode={(id: string) => documentInstance.getNode(id) as any}
customCreateElement={(Component: any, props: any, children: any) => { customCreateElement={(Component: any, props: any, children: any) => {
const { __id, ...viewProps } = props; const { __id, ...viewProps } = props;

View File

@ -154,7 +154,7 @@ class Renderer extends Component<{
render() { render() {
const { documentInstance, rendererContainer: renderer } = this.props; const { documentInstance, rendererContainer: renderer } = this.props;
const { container } = documentInstance; const { container, document } = documentInstance;
const { designMode, device, locale } = container; const { designMode, device, locale } = container;
const messages = container.context?.utils?.i18n?.messages || {}; const messages = container.context?.utils?.i18n?.messages || {};
this.startTime = Date.now(); this.startTime = Date.now();
@ -172,6 +172,7 @@ class Renderer extends Component<{
appHelper={container.context} appHelper={container.context}
designMode={designMode} designMode={designMode}
device={device} device={device}
documentId={document.id}
suspended={renderer.suspended} suspended={renderer.suspended}
self={renderer.scope} self={renderer.scope}
getNode={(id: string) => documentInstance.getNode(id) as Node} getNode={(id: string) => documentInstance.getNode(id) as Node}

View File

@ -50,16 +50,30 @@ enum RerenderType {
} }
// 缓存 Leaf 层组件,防止重新渲染问题 // 缓存 Leaf 层组件,防止重新渲染问题
const leafComponentCache: { let leafComponentCaches: {
[componentName: string]: any; [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 的订阅事件 // 缓存导致 rerender 的订阅事件
const rerenderEventCache: { const rerenderEventCache: {
[componentId: string]: any; [componentId: string]: any;
} = {}; } = {};
/** 部分没有渲染的 node 节点进行兜底处理 or 渲染方式没有渲染 LeafWrapper */ /** 部分没有渲染的 node 节点进行兜底处理 or 渲染方式没有渲染 LeafWrapper */
function makeRerenderEvent({ function initRerenderEvent({
schema, schema,
__debug, __debug,
container, container,
@ -98,6 +112,7 @@ function clearRerenderEvent(id: string): void {
if (!rerenderEventCache[id]) { if (!rerenderEventCache[id]) {
rerenderEventCache[id] = { rerenderEventCache[id] = {
clear: true, clear: true,
dispose: [],
}; };
return; return;
} }
@ -119,27 +134,34 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
} = baseRenderer; } = baseRenderer;
const engine = baseRenderer.context.engine; const engine = baseRenderer.context.engine;
const host: BuiltinSimulatorHost = baseRenderer.props.__host; const host: BuiltinSimulatorHost = baseRenderer.props.__host;
const curDocumentId = baseRenderer.props?.documentId;
const getNode = baseRenderer.props?.getNode; const getNode = baseRenderer.props?.getNode;
const container: BuiltinSimulatorHost = baseRenderer.props.__container; const container: BuiltinSimulatorHost = baseRenderer.props.__container;
const editor = host?.designer?.editor; const editor = host?.designer?.editor;
const { Component } = adapter.getRuntime(); const { Component, forwardRef } = adapter.getRuntime();
if (curDocumentId !== cacheDocumentId) {
clearCaches(curDocumentId, {
__debug,
});
}
if (!isReactComponent(Comp)) { if (!isReactComponent(Comp)) {
console.error(`${schema.componentName} component may be has errors: `, Comp); console.error(`${schema.componentName} component may be has errors: `, Comp);
} }
makeRerenderEvent({ initRerenderEvent({
schema, schema,
__debug, __debug,
container, container,
getNode, getNode,
}); });
if (leafComponentCache[schema.componentName]) { if (leafComponentCaches[schema.componentName]) {
return leafComponentCache[schema.componentName]; return leafComponentCaches[schema.componentName];
} }
class LeafWrapper extends Component { class LeafHoc extends Component {
recordInfo: { recordInfo: {
startTime?: number | null; startTime?: number | null;
type?: string; type?: string;
@ -156,7 +178,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
return; return;
} }
const endTime = Date.now(); 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'; const componentName = this.recordInfo.node?.componentName || this.leaf?.componentName || 'UnknownComponent';
editor?.emit(GlobalEvent.Node.Rerender, { editor?.emit(GlobalEvent.Node.Rerender, {
componentName, componentName,
@ -171,18 +193,39 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
this.recordTime(); 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) { constructor(props: IProps, context: any) {
super(props, context); super(props, context);
// 监听以下事件,当变化时更新自己 // 监听以下事件,当变化时更新自己
clearRerenderEvent(schema.id); __debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`);
clearRerenderEvent(this.props.componentId);
this.initOnPropsChangeEvent(); this.initOnPropsChangeEvent();
this.initOnChildrenChangeEvent(); this.initOnChildrenChangeEvent();
this.initOnVisibleChangeEvent(); this.initOnVisibleChangeEvent();
__debug(`${schema.componentName}[${schema.id}] leaf render in SimulatorRendererView`);
this.state = { this.state = {
nodeChildren: null, nodeChildren: null,
childrenInState: false, childrenInState: false,
__tag: props.__tag,
}; };
} }
@ -198,17 +241,11 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
// } // }
componentWillReceiveProps(nextProps: any) { componentWillReceiveProps(nextProps: any) {
const { _leaf, __tag, children, ...rest } = nextProps; const { _leaf } = nextProps;
if (nextProps.__tag === this.state.__tag) { if (nextProps.__tag === this.props.__tag) {
const nextProps = getProps(this.leaf?.export?.(TransformStage.Render) as types.ISchema, Comp, componentInfo);
this.setState({
nodeProps: {
...rest,
...nextProps,
},
});
return null; return null;
} }
if (_leaf && this.leaf && _leaf !== this.leaf) { if (_leaf && this.leaf && _leaf !== this.leaf) {
this.disposeFunctions.forEach(fn => fn()); this.disposeFunctions.forEach(fn => fn());
this.disposeFunctions = []; this.disposeFunctions = [];
@ -218,10 +255,9 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
} }
this.setState({ this.setState({
nodeChildren: children, nodeChildren: null,
nodeProps: rest, nodeProps: {},
childrenInState: true, childrenInState: false,
__tag,
}); });
} }
@ -300,7 +336,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
// } // }
this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node); this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node);
__debug(`${leaf} component trigger onChildrenChange event`); __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({ this.setState({
nodeChildren: nextChild, nodeChildren: nextChild,
childrenInState: true, childrenInState: true,
@ -359,17 +395,28 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
return null; return null;
} }
const {
ref,
...rest
} = this.props;
const compProps = { const compProps = {
...this.props, ...rest,
...(this.state.nodeProps || {}), ...(this.state.nodeProps || {}),
children: [], children: [],
__id: this.props.componentId, __id: this.props.componentId,
ref: this.props.forwardedRef,
}; };
return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null); return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null);
} }
} }
const LeafWrapper = forwardRef((props: any, ref: any) => (
// @ts-ignore
<LeafHoc {...props} forwardedRef={ref} />
));
if (typeof Comp === 'object') { if (typeof Comp === 'object') {
const compExtraPropertyNames = Object.getOwnPropertyNames(Comp).filter(d => !compDefaultPropertyNames.includes(d)); 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; LeafWrapper.displayName = (Comp as any).displayName;
leafComponentCache[schema.componentName] = LeafWrapper; leafComponentCaches[schema.componentName] = LeafWrapper;
return LeafWrapper; return LeafWrapper;
} }

View File

@ -77,7 +77,7 @@ export default function addonRendererFactory() {
return '插件 schema 结构异常!'; return '插件 schema 结构异常!';
} }
this.__debug(`render - ${__schema.fileName}`); this.__debug(`${AddonRenderer.dislayName} render - ${__schema.fileName}`);
this.__generateCtx({ this.__generateCtx({
component: this, component: this,
}); });

View File

@ -521,7 +521,7 @@ export default function baseRenererFactory() {
props.key = props.__id; 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); const renderComp = (props: any) => engine.createElement(Comp, props, child);
// 设计模式下的特殊处理 // 设计模式下的特殊处理
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) { 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); .map((d: IComponentHoc) => d.hoc);
} }
__getSchemaChildrenVirtualDom = (schema: ISchema, Comp: any) => { __getSchemaChildrenVirtualDom = (schema: ISchema, Comp: any, childrenMap?: Map<any, any>) => {
let _children = this.getSchemaChildren(schema); let _children = this.getSchemaChildren(schema);
let children: any = []; let children: any = [];
@ -596,6 +596,8 @@ export default function baseRenererFactory() {
{ {
schema, schema,
Comp, Comp,
// 有 childrenMap 情况下children 只计算第一层,不需要遍历多层。
componentChildren: childrenMap?.get(_child.id)?.props.children || null,
}, },
); );
@ -834,11 +836,16 @@ export default function baseRenererFactory() {
componentInfo: {}, componentInfo: {},
}); });
const { className } = data; const { className } = data;
const otherProps: any = {};
const { engine } = this.context || {}; const { engine } = this.context || {};
if (!engine) { if (!engine) {
return null; return null;
} }
if (this._designModeIsDesign) {
otherProps.__tag = Math.random();
}
const child = engine.createElement( const child = engine.createElement(
Comp, Comp,
{ {
@ -847,6 +854,7 @@ export default function baseRenererFactory() {
ref: this.__getRef, ref: this.__getRef,
className: classnames(getFileCssName(__schema?.fileName), className, this.props.className), className: classnames(getFileCssName(__schema?.fileName), className, this.props.className),
__id: __schema?.id, __id: __schema?.id,
...otherProps,
}, },
this.__createDom(), this.__createDom(),
); );

View File

@ -23,7 +23,7 @@ export default function blockRendererFactory() {
return '区块 schema 结构异常!'; return '区块 schema 结构异常!';
} }
this.__debug(`render - ${__schema.fileName}`); this.__debug(`${BlockRenderer.dislayName} render - ${__schema.fileName}`);
this.__generateCtx({}); this.__generateCtx({});
this.__render(); this.__render();

View File

@ -23,7 +23,7 @@ export default function componentRendererFactory() {
if (this.__checkSchema(__schema)) { if (this.__checkSchema(__schema)) {
return '自定义组件 schema 结构异常!'; return '自定义组件 schema 结构异常!';
} }
this.__debug(`render - ${__schema.fileName}`); this.__debug(`${CompRenderer.dislayName} render - ${__schema.fileName}`);
this.__generateCtx({ this.__generateCtx({
component: this, component: this,

View File

@ -36,7 +36,7 @@ export default function pageRendererFactory() {
if (this.__checkSchema(__schema)) { if (this.__checkSchema(__schema)) {
return '页面schema结构异常'; return '页面schema结构异常';
} }
this.__debug(`render - ${__schema.fileName}`); this.__debug(`${PageRenderer.dislayName} render - ${__schema.fileName}`);
this.__bindCustomMethods(this.props); this.__bindCustomMethods(this.props);
this.__initDataSource(this.props); this.__initDataSource(this.props);

View File

@ -48,7 +48,7 @@ export default function tempRendererFactory() {
return '下钻编辑 schema 结构异常!'; return '下钻编辑 schema 结构异常!';
} }
this.__debug(`render - ${__schema.fileName}`); this.__debug(`${TempRenderer.dislayName} render - ${__schema.fileName}`);
return this.__renderContent(this.__renderContextProvider({ __ctx })); return this.__renderContent(this.__renderContextProvider({ __ctx }));
} }

View File

@ -63,6 +63,7 @@ export interface IInfo {
schema: ISchema; schema: ISchema;
Comp: any; Comp: any;
componentInfo?: any; componentInfo?: any;
componentChildren?: any
} }
export interface JSExpression { export interface JSExpression {