mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-10 09:56:20 +00:00
Merge commit 'd53ba538b1c3f9989d911ac96dc5f3eadb561a3c' into def_releases_2021092316393939_ali-lowcode_ali-lowcode-engine/1.0.69
This commit is contained in:
commit
4e22fc5c09
@ -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})/`,
|
||||||
|
|||||||
@ -198,7 +198,7 @@ export class DocumentModel {
|
|||||||
* 根据 id 获取节点
|
* 根据 id 获取节点
|
||||||
*/
|
*/
|
||||||
getNodeCount(): number {
|
getNodeCount(): number {
|
||||||
return this._nodesMap.size;
|
return this._nodesMap?.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
72
packages/designer/tests/bugs/prop-variable-jse.test.ts
Normal file
72
packages/designer/tests/bugs/prop-variable-jse.test.ts
Normal 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',
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
6
packages/designer/tests/bugs/why.md
Normal file
6
packages/designer/tests/bugs/why.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
背景:
|
||||||
|
在 UT 的基础上,希望借助一些 Bug 修复来完成场景测试,从而进一步增强稳定性。
|
||||||
|
至少在真正的 E2E 测试来临之前,我们保证不会重复犯两次相同的错误。
|
||||||
|
|
||||||
|
做法:
|
||||||
|
Bugs 文件夹每个文件记录一个 bug 修复的场景测试~
|
||||||
@ -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 } });
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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('');
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user