mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-14 04:43:14 +00:00
feat: the renderer-core leaf component removes the react.createElement call
This commit is contained in:
parent
0184dcd938
commit
c3ce042c83
@ -3,6 +3,9 @@ import { IPublicTypeNodeSchema, IPublicTypeComponentInstance, IPublicTypeNodeIns
|
|||||||
|
|
||||||
export interface BuiltinSimulatorRenderer {
|
export interface BuiltinSimulatorRenderer {
|
||||||
readonly isSimulatorRenderer: true;
|
readonly isSimulatorRenderer: true;
|
||||||
|
autoRepaintNode?: boolean;
|
||||||
|
components: Record<string, Component>;
|
||||||
|
rerender: () => void;
|
||||||
createComponent(schema: IPublicTypeNodeSchema): Component | null;
|
createComponent(schema: IPublicTypeNodeSchema): Component | null;
|
||||||
getComponent(componentName: string): Component;
|
getComponent(componentName: string): Component;
|
||||||
getClosestNodeInstance(
|
getClosestNodeInstance(
|
||||||
|
|||||||
@ -64,6 +64,7 @@ export interface IDocumentModel extends Omit< IPublicModelDocumentModel, 'select
|
|||||||
dragObject: IPublicTypeDragNodeObject | IPublicTypeNodeSchema | INode | IPublicTypeDragNodeDataObject,
|
dragObject: IPublicTypeDragNodeObject | IPublicTypeNodeSchema | INode | IPublicTypeDragNodeDataObject,
|
||||||
): boolean;
|
): boolean;
|
||||||
|
|
||||||
|
getNodeCount(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocumentModel implements IDocumentModel {
|
export class DocumentModel implements IDocumentModel {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
IPublicModelNode,
|
IPublicModelNode,
|
||||||
IPublicModelExclusiveGroup,
|
IPublicModelExclusiveGroup,
|
||||||
IPublicEnumTransformStage,
|
IPublicEnumTransformStage,
|
||||||
|
IPublicTypeDisposable,
|
||||||
} from '@alilc/lowcode-types';
|
} from '@alilc/lowcode-types';
|
||||||
import { compatStage, isDOMText, isJSExpression, isNode } from '@alilc/lowcode-utils';
|
import { compatStage, isDOMText, isJSExpression, isNode } from '@alilc/lowcode-utils';
|
||||||
import { SettingTopEntry } from '@alilc/lowcode-designer';
|
import { SettingTopEntry } from '@alilc/lowcode-designer';
|
||||||
@ -112,6 +113,10 @@ export interface INode extends IPublicModelNode {
|
|||||||
replaceChild(node: INode, data: any): INode;
|
replaceChild(node: INode, data: any): INode;
|
||||||
|
|
||||||
getSuitablePlace(node: INode, ref: any): any;
|
getSuitablePlace(node: INode, ref: any): any;
|
||||||
|
|
||||||
|
onChildrenChange(fn: (param?: { type: string; node: INode }) => void): IPublicTypeDisposable;
|
||||||
|
|
||||||
|
onPropChange(func: (info: IPublicTypePropChangeOptions) => void): IPublicTypeDisposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1080,7 +1085,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
|||||||
return this.props;
|
return this.props;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChildrenChange(fn: (param?: { type: string; node: Node }) => void): (() => void) | undefined {
|
onChildrenChange(fn: (param?: { type: string; node: Node }) => void): IPublicTypeDisposable {
|
||||||
const wrappedFunc = wrapWithEventSwitch(fn);
|
const wrappedFunc = wrapWithEventSwitch(fn);
|
||||||
return this.children?.onChange(wrappedFunc);
|
return this.children?.onChange(wrappedFunc);
|
||||||
}
|
}
|
||||||
@ -1273,7 +1278,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
|||||||
this.emitter?.emit('propChange', val);
|
this.emitter?.emit('propChange', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
onPropChange(func: (info: IPublicTypePropChangeOptions) => void): Function {
|
onPropChange(func: (info: IPublicTypePropChangeOptions) => void): IPublicTypeDisposable {
|
||||||
const wrappedFunc = wrapWithEventSwitch(func);
|
const wrappedFunc = wrapWithEventSwitch(func);
|
||||||
this.emitter.on('propChange', wrappedFunc);
|
this.emitter.on('propChange', wrappedFunc);
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { BuiltinSimulatorHost, Node, IPublicTypePropChangeOptions } from '@alilc/lowcode-designer';
|
import { INode, IPublicTypePropChangeOptions } from '@alilc/lowcode-designer';
|
||||||
import { GlobalEvent, IPublicEnumTransformStage, IPublicTypeNodeSchema, IPublicTypeEngineOptions } from '@alilc/lowcode-types';
|
import { GlobalEvent, IPublicEnumTransformStage, IPublicTypeNodeSchema, IPublicTypeEngineOptions } from '@alilc/lowcode-types';
|
||||||
import { isReactComponent, cloneEnumerableProperty } from '@alilc/lowcode-utils';
|
import { isReactComponent, cloneEnumerableProperty } from '@alilc/lowcode-utils';
|
||||||
import { debounce } from '../utils/common';
|
import { debounce } from '../utils/common';
|
||||||
@ -16,15 +16,17 @@ export interface IComponentHocProps {
|
|||||||
__tag: any;
|
__tag: any;
|
||||||
componentId: any;
|
componentId: any;
|
||||||
_leaf: any;
|
_leaf: any;
|
||||||
forwardedRef: any;
|
forwardedRef?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IComponentHocState {
|
export interface IComponentHocState {
|
||||||
childrenInState: boolean;
|
childrenInState: boolean;
|
||||||
nodeChildren: any;
|
nodeChildren: any;
|
||||||
nodeCacheProps: any;
|
nodeCacheProps: any;
|
||||||
|
|
||||||
/** 控制是否显示隐藏 */
|
/** 控制是否显示隐藏 */
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
|
||||||
/** 控制是否渲染 */
|
/** 控制是否渲染 */
|
||||||
condition: boolean;
|
condition: boolean;
|
||||||
nodeProps: any;
|
nodeProps: any;
|
||||||
@ -40,15 +42,17 @@ export interface IComponentHoc {
|
|||||||
export type IComponentConstruct = (Comp: types.IBaseRenderComponent, info: IComponentHocInfo) => types.IGeneralConstructor;
|
export type IComponentConstruct = (Comp: types.IBaseRenderComponent, info: IComponentHocInfo) => types.IGeneralConstructor;
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
_leaf: Node | undefined;
|
_leaf: INode | undefined;
|
||||||
|
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
|
||||||
componentId?: number;
|
componentId: number;
|
||||||
|
|
||||||
children?: Node[];
|
children?: INode[];
|
||||||
|
|
||||||
__tag?: number;
|
__tag: number;
|
||||||
|
|
||||||
|
forwardedRef?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RerenderType {
|
enum RerenderType {
|
||||||
@ -61,8 +65,7 @@ enum RerenderType {
|
|||||||
|
|
||||||
// 缓存 Leaf 层组件,防止重新渲染问题
|
// 缓存 Leaf 层组件,防止重新渲染问题
|
||||||
class LeafCache {
|
class LeafCache {
|
||||||
constructor(public documentId: string, public device: string) {
|
|
||||||
}
|
|
||||||
/** 组件缓存 */
|
/** 组件缓存 */
|
||||||
component = new Map();
|
component = new Map();
|
||||||
|
|
||||||
@ -77,6 +80,9 @@ class LeafCache {
|
|||||||
event = new Map();
|
event = new Map();
|
||||||
|
|
||||||
ref = new Map();
|
ref = new Map();
|
||||||
|
|
||||||
|
constructor(public documentId: string, public device: string) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cache: LeafCache;
|
let cache: LeafCache;
|
||||||
@ -155,11 +161,11 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
const curDocumentId = baseRenderer.props?.documentId ?? '';
|
const curDocumentId = baseRenderer.props?.documentId ?? '';
|
||||||
const curDevice = baseRenderer.props?.device ?? '';
|
const curDevice = baseRenderer.props?.device ?? '';
|
||||||
const getNode = baseRenderer.props?.getNode;
|
const getNode = baseRenderer.props?.getNode;
|
||||||
const container: BuiltinSimulatorHost = baseRenderer.props?.__container;
|
const container = baseRenderer.props?.__container;
|
||||||
const setSchemaChangedSymbol = baseRenderer.props?.setSchemaChangedSymbol;
|
const setSchemaChangedSymbol = baseRenderer.props?.setSchemaChangedSymbol;
|
||||||
const editor = host?.designer?.editor;
|
const editor = host?.designer?.editor;
|
||||||
const runtime = adapter.getRuntime();
|
const runtime = adapter.getRuntime();
|
||||||
const { forwardRef } = runtime;
|
const { forwardRef, createElement } = runtime;
|
||||||
const Component = runtime.Component as types.IGeneralConstructor<
|
const Component = runtime.Component as types.IGeneralConstructor<
|
||||||
IComponentHocProps, IComponentHocState
|
IComponentHocProps, IComponentHocState
|
||||||
>;
|
>;
|
||||||
@ -192,14 +198,64 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
recordInfo: {
|
recordInfo: {
|
||||||
startTime?: number | null;
|
startTime?: number | null;
|
||||||
type?: string;
|
type?: string;
|
||||||
node?: Node;
|
node?: INode;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
|
private curEventLeaf: INode | undefined;
|
||||||
|
|
||||||
static displayName = schema.componentName;
|
static displayName = schema.componentName;
|
||||||
|
|
||||||
disposeFunctions: Array<((() => void) | Function)> = [];
|
disposeFunctions: Array<((() => void) | Function)> = [];
|
||||||
|
|
||||||
__component_tag = 'leafWrapper';
|
__component_tag = 'leafWrapper';
|
||||||
|
|
||||||
|
renderUnitInfo: {
|
||||||
|
minimalUnitId?: string;
|
||||||
|
minimalUnitName?: string;
|
||||||
|
singleRender?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 最小渲染单元做防抖处理
|
||||||
|
makeUnitRenderDebounced = debounce(() => {
|
||||||
|
this.beforeRender(RerenderType.MinimalRenderUnit);
|
||||||
|
const schema = this.leaf?.export?.(IPublicEnumTransformStage.Render);
|
||||||
|
if (!schema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextProps = getProps(schema, scope, Comp, componentInfo);
|
||||||
|
const children = getChildren(schema, scope, Comp);
|
||||||
|
const nextState = {
|
||||||
|
nodeProps: nextProps,
|
||||||
|
nodeChildren: children,
|
||||||
|
childrenInState: true,
|
||||||
|
};
|
||||||
|
if ('children' in nextProps) {
|
||||||
|
nextState.nodeChildren = nextProps.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
__debug(`${this.leaf?.componentName}(${this.props.componentId}) MinimalRenderUnit Render!`);
|
||||||
|
this.setState(nextState);
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
constructor(props: IProps, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
// 监听以下事件,当变化时更新自己
|
||||||
|
__debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`);
|
||||||
|
clearRerenderEvent(componentCacheId);
|
||||||
|
this.curEventLeaf = this.leaf;
|
||||||
|
|
||||||
|
cache.ref.set(componentCacheId, {
|
||||||
|
makeUnitRender: this.makeUnitRender,
|
||||||
|
});
|
||||||
|
|
||||||
|
let cacheState = cache.state.get(componentCacheId);
|
||||||
|
if (!cacheState || cacheState.__tag !== props.__tag) {
|
||||||
|
cacheState = this.getDefaultState(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = cacheState;
|
||||||
|
}
|
||||||
|
|
||||||
recordTime = () => {
|
recordTime = () => {
|
||||||
if (!this.recordInfo.startTime) {
|
if (!this.recordInfo.startTime) {
|
||||||
return;
|
return;
|
||||||
@ -216,6 +272,14 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
this.recordInfo.startTime = null;
|
this.recordInfo.startTime = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
makeUnitRender = () => {
|
||||||
|
this.makeUnitRenderDebounced();
|
||||||
|
};
|
||||||
|
|
||||||
|
get autoRepaintNode() {
|
||||||
|
return container?.autoRepaintNode;
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this.recordTime();
|
this.recordTime();
|
||||||
}
|
}
|
||||||
@ -243,27 +307,6 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props: IProps, context: any) {
|
|
||||||
super(props, context);
|
|
||||||
// 监听以下事件,当变化时更新自己
|
|
||||||
__debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`);
|
|
||||||
clearRerenderEvent(componentCacheId);
|
|
||||||
this.curEventLeaf = this.leaf;
|
|
||||||
|
|
||||||
cache.ref.set(componentCacheId, {
|
|
||||||
makeUnitRender: this.makeUnitRender,
|
|
||||||
});
|
|
||||||
|
|
||||||
let cacheState = cache.state.get(componentCacheId);
|
|
||||||
if (!cacheState || cacheState.__tag !== props.__tag) {
|
|
||||||
cacheState = this.getDefaultState(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = cacheState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private curEventLeaf: Node | undefined;
|
|
||||||
|
|
||||||
setState(state: any) {
|
setState(state: any) {
|
||||||
cache.state.set(componentCacheId, {
|
cache.state.set(componentCacheId, {
|
||||||
...this.state,
|
...this.state,
|
||||||
@ -274,23 +317,13 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 由于内部属性变化,在触发渲染前,会执行该函数 */
|
/** 由于内部属性变化,在触发渲染前,会执行该函数 */
|
||||||
beforeRender(type: string, node?: Node): void {
|
beforeRender(type: string, node?: INode): void {
|
||||||
this.recordInfo.startTime = Date.now();
|
this.recordInfo.startTime = Date.now();
|
||||||
this.recordInfo.type = type;
|
this.recordInfo.type = type;
|
||||||
this.recordInfo.node = node;
|
this.recordInfo.node = node;
|
||||||
setSchemaChangedSymbol?.(true);
|
setSchemaChangedSymbol?.(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderUnitInfo: {
|
|
||||||
minimalUnitId?: string;
|
|
||||||
minimalUnitName?: string;
|
|
||||||
singleRender?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
get autoRepaintNode() {
|
|
||||||
return container.autoRepaintNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
judgeMiniUnitRender() {
|
judgeMiniUnitRender() {
|
||||||
if (!this.renderUnitInfo) {
|
if (!this.renderUnitInfo) {
|
||||||
this.getRenderUnitInfo();
|
this.getRenderUnitInfo();
|
||||||
@ -308,7 +341,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
__debug('Cant find minimalRenderUnit ref! This make rerender!');
|
__debug('Cant find minimalRenderUnit ref! This make rerender!');
|
||||||
container.rerender();
|
container?.rerender();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
__debug(`${this.leaf?.componentName}(${this.props.componentId}) need render, make its minimalRenderUnit ${renderUnitInfo.minimalUnitName}(${renderUnitInfo.minimalUnitId})`);
|
__debug(`${this.leaf?.componentName}(${this.props.componentId}) need render, make its minimalRenderUnit ${renderUnitInfo.minimalUnitName}(${renderUnitInfo.minimalUnitId})`);
|
||||||
@ -321,7 +354,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leaf.isRoot()) {
|
if (leaf.isRootNode) {
|
||||||
this.renderUnitInfo = {
|
this.renderUnitInfo = {
|
||||||
singleRender: true,
|
singleRender: true,
|
||||||
...(this.renderUnitInfo || {}),
|
...(this.renderUnitInfo || {}),
|
||||||
@ -347,32 +380,6 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最小渲染单元做防抖处理
|
|
||||||
makeUnitRenderDebounced = debounce(() => {
|
|
||||||
this.beforeRender(RerenderType.MinimalRenderUnit);
|
|
||||||
const schema = this.leaf?.export?.(IPublicEnumTransformStage.Render);
|
|
||||||
if (!schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const nextProps = getProps(schema, scope, Comp, componentInfo);
|
|
||||||
const children = getChildren(schema, scope, Comp);
|
|
||||||
const nextState = {
|
|
||||||
nodeProps: nextProps,
|
|
||||||
nodeChildren: children,
|
|
||||||
childrenInState: true,
|
|
||||||
};
|
|
||||||
if ('children' in nextProps) {
|
|
||||||
nextState.nodeChildren = nextProps.children;
|
|
||||||
}
|
|
||||||
|
|
||||||
__debug(`${this.leaf?.componentName}(${this.props.componentId}) MinimalRenderUnit Render!`);
|
|
||||||
this.setState(nextState);
|
|
||||||
}, 20);
|
|
||||||
|
|
||||||
makeUnitRender = () => {
|
|
||||||
this.makeUnitRenderDebounced();
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: any) {
|
componentWillReceiveProps(nextProps: any) {
|
||||||
let { componentId } = nextProps;
|
let { componentId } = nextProps;
|
||||||
if (nextProps.__tag === this.props.__tag) {
|
if (nextProps.__tag === this.props.__tag) {
|
||||||
@ -420,7 +427,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
// 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决
|
// 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决
|
||||||
if (key === '___loop___') {
|
if (key === '___loop___') {
|
||||||
__debug('key is ___loop___, render a page!');
|
__debug('key is ___loop___, render a page!');
|
||||||
container.rerender();
|
container?.rerender();
|
||||||
// 由于 scope 变化,需要清空缓存,使用新的 scope
|
// 由于 scope 变化,需要清空缓存,使用新的 scope
|
||||||
cache.component.delete(componentCacheId);
|
cache.component.delete(componentCacheId);
|
||||||
return;
|
return;
|
||||||
@ -536,7 +543,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
get leaf(): Node | undefined {
|
get leaf(): INode | undefined {
|
||||||
if (this.props._leaf?.isMock) {
|
if (this.props._leaf?.isMock) {
|
||||||
// 低代码组件作为一个整体更新,其内部的组件不需要监听相关事件
|
// 低代码组件作为一个整体更新,其内部的组件不需要监听相关事件
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -570,13 +577,12 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let LeafWrapper = forwardRef((props: any, ref: any) => (
|
let LeafWrapper = forwardRef((props: any, ref: any) => {
|
||||||
// @ts-ignore
|
return createElement(LeafHoc, {
|
||||||
<LeafHoc
|
...props,
|
||||||
{...props}
|
forwardedRef: ref,
|
||||||
forwardedRef={ref}
|
});
|
||||||
/>
|
});
|
||||||
));
|
|
||||||
|
|
||||||
LeafWrapper = cloneEnumerableProperty(LeafWrapper, Comp);
|
LeafWrapper = cloneEnumerableProperty(LeafWrapper, Comp);
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
|
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
|
||||||
import { IPublicTypeNodeSchema, IPublicTypeNodeData, JSONValue, IPublicTypeCompositeValue } from '@alilc/lowcode-types';
|
import { IPublicTypeNodeSchema, IPublicTypeNodeData, IPublicTypeJSONValue, IPublicTypeCompositeValue } from '@alilc/lowcode-types';
|
||||||
import { isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils';
|
import { isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils';
|
||||||
import adapter from '../adapter';
|
import adapter from '../adapter';
|
||||||
import divFactory from '../components/Div';
|
import divFactory from '../components/Div';
|
||||||
@ -121,6 +121,8 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
let scopeIdx = 0;
|
let scopeIdx = 0;
|
||||||
|
|
||||||
return class BaseRenderer extends Component<IBaseRendererProps, Record<string, any>> {
|
return class BaseRenderer extends Component<IBaseRendererProps, Record<string, any>> {
|
||||||
|
[key: string]: any;
|
||||||
|
|
||||||
static displayName = 'BaseRenderer';
|
static displayName = 'BaseRenderer';
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -139,6 +141,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
__compScopes: Record<string, any> = {};
|
__compScopes: Record<string, any> = {};
|
||||||
__instanceMap: Record<string, any> = {};
|
__instanceMap: Record<string, any> = {};
|
||||||
__dataHelper: any;
|
__dataHelper: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keep track of customMethods added to this context
|
* keep track of customMethods added to this context
|
||||||
*
|
*
|
||||||
@ -155,8 +158,6 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
*/
|
*/
|
||||||
__styleElement: any;
|
__styleElement: any;
|
||||||
|
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
constructor(props: IBaseRendererProps, context: IBaseRendererContext) {
|
constructor(props: IBaseRendererProps, context: IBaseRendererContext) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -242,6 +243,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
super.forceUpdate();
|
super.forceUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* execute method in schema.lifeCycles
|
* execute method in schema.lifeCycles
|
||||||
* @PRIVATE
|
* @PRIVATE
|
||||||
@ -490,6 +492,10 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const _children = getSchemaChildren(schema);
|
const _children = getSchemaChildren(schema);
|
||||||
|
if (!schema.componentName) {
|
||||||
|
logger.error('The componentName in the schema is invalid, please check the schema: ', schema);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 解析占位组件
|
// 解析占位组件
|
||||||
if (schema.componentName === 'Fragment' && _children) {
|
if (schema.componentName === 'Fragment' && _children) {
|
||||||
const tarChildren = isJSExpression(_children) ? this.__parseExpression(_children, scope) : _children;
|
const tarChildren = isJSExpression(_children) ? this.__parseExpression(_children, scope) : _children;
|
||||||
@ -758,7 +764,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
const itemArg = (schema.loopArgs && schema.loopArgs[0]) || DEFAULT_LOOP_ARG_ITEM;
|
const itemArg = (schema.loopArgs && schema.loopArgs[0]) || DEFAULT_LOOP_ARG_ITEM;
|
||||||
const indexArg = (schema.loopArgs && schema.loopArgs[1]) || DEFAULT_LOOP_ARG_INDEX;
|
const indexArg = (schema.loopArgs && schema.loopArgs[1]) || DEFAULT_LOOP_ARG_INDEX;
|
||||||
const { loop } = schema;
|
const { loop } = schema;
|
||||||
return loop.map((item: JSONValue | IPublicTypeCompositeValue, i: number) => {
|
return loop.map((item: IPublicTypeJSONValue | IPublicTypeCompositeValue, i: number) => {
|
||||||
const loopSelf: any = {
|
const loopSelf: any = {
|
||||||
[itemArg]: item,
|
[itemArg]: item,
|
||||||
[indexArg]: i,
|
[indexArg]: i,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import baseRendererFactory from './base';
|
|||||||
import divFactory from '../components/Div';
|
import divFactory from '../components/Div';
|
||||||
import { IRenderComponent, IRendererProps, IRendererState } from '../types';
|
import { IRenderComponent, IRendererProps, IRendererState } from '../types';
|
||||||
import { IPublicTypeNodeSchema, IPublicTypeRootSchema } from '@alilc/lowcode-types';
|
import { IPublicTypeNodeSchema, IPublicTypeRootSchema } from '@alilc/lowcode-types';
|
||||||
|
import logger from '../utils/logger';
|
||||||
|
|
||||||
export default function rendererFactory(): IRenderComponent {
|
export default function rendererFactory(): IRenderComponent {
|
||||||
const { PureComponent, Component, createElement, findDOMNode } = adapter.getRuntime();
|
const { PureComponent, Component, createElement, findDOMNode } = adapter.getRuntime();
|
||||||
@ -168,6 +169,7 @@ export default function rendererFactory(): IRenderComponent {
|
|||||||
}
|
}
|
||||||
// 兼容乐高区块模板
|
// 兼容乐高区块模板
|
||||||
if (schema.componentName !== 'Div' && !isFileSchema(schema)) {
|
if (schema.componentName !== 'Div' && !isFileSchema(schema)) {
|
||||||
|
logger.error('The root component name needs to be one of Page、Block、Component, please check the schema: ', schema);
|
||||||
return '模型结构异常';
|
return '模型结构异常';
|
||||||
}
|
}
|
||||||
debug('entry.render');
|
debug('entry.render');
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { ComponentLifecycle, CSSProperties } from 'react';
|
import type { ComponentLifecycle, CSSProperties } from 'react';
|
||||||
import { BuiltinSimulatorHost } from '@alilc/lowcode-designer';
|
import { BuiltinSimulatorHost, BuiltinSimulatorRenderer } from '@alilc/lowcode-designer';
|
||||||
import { RequestHandler, IPublicTypeNodeSchema, IPublicTypeRootSchema, IPublicTypeJSONObject } from '@alilc/lowcode-types';
|
import { RequestHandler, IPublicTypeNodeSchema, IPublicTypeRootSchema, IPublicTypeJSONObject } from '@alilc/lowcode-types';
|
||||||
|
|
||||||
export type ISchema = IPublicTypeNodeSchema | IPublicTypeRootSchema;
|
export type ISchema = IPublicTypeNodeSchema | IPublicTypeRootSchema;
|
||||||
@ -8,16 +8,16 @@ export type ISchema = IPublicTypeNodeSchema | IPublicTypeRootSchema;
|
|||||||
** Duck typed component type supporting both react and rax
|
** Duck typed component type supporting both react and rax
|
||||||
*/
|
*/
|
||||||
interface IGeneralComponent<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> {
|
interface IGeneralComponent<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> {
|
||||||
|
readonly props: Readonly<P> & Readonly<{ children?: any | undefined }>;
|
||||||
|
state: Readonly<S>;
|
||||||
|
refs: Record<string, any>;
|
||||||
|
context: any;
|
||||||
setState<K extends keyof S>(
|
setState<K extends keyof S>(
|
||||||
state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
|
state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
): void;
|
): void;
|
||||||
forceUpdate(callback?: () => void): void;
|
forceUpdate(callback?: () => void): void;
|
||||||
render(): any;
|
render(): any;
|
||||||
readonly props: Readonly<P> & Readonly<{ children?: any | undefined }>;
|
|
||||||
state: Readonly<S>;
|
|
||||||
refs: Record<string, any>;
|
|
||||||
context: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IGeneralConstructor<
|
export type IGeneralConstructor<
|
||||||
@ -60,20 +60,28 @@ export interface ILocationLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type IRendererAppHelper = Partial<{
|
export type IRendererAppHelper = Partial<{
|
||||||
|
|
||||||
/** 全局公共函数 */
|
/** 全局公共函数 */
|
||||||
utils: Record<string, any>;
|
utils: Record<string, any>;
|
||||||
|
|
||||||
/** 全局常量 */
|
/** 全局常量 */
|
||||||
constants: Record<string, any>;
|
constants: Record<string, any>;
|
||||||
|
|
||||||
/** react-router 的 location 实例 */
|
/** react-router 的 location 实例 */
|
||||||
location: ILocationLike;
|
location: ILocationLike;
|
||||||
|
|
||||||
/** react-router 的 history 实例 */
|
/** react-router 的 history 实例 */
|
||||||
history: IHistoryLike;
|
history: IHistoryLike;
|
||||||
|
|
||||||
/** @deprecated 已无业务使用 */
|
/** @deprecated 已无业务使用 */
|
||||||
match: any;
|
match: any;
|
||||||
|
|
||||||
/** @experimental 内部使用 */
|
/** @experimental 内部使用 */
|
||||||
logParams: Record<string, any>;
|
logParams: Record<string, any>;
|
||||||
|
|
||||||
/** @experimental 内部使用 */
|
/** @experimental 内部使用 */
|
||||||
addons: Record<string, any>;
|
addons: Record<string, any>;
|
||||||
|
|
||||||
/** @experimental 内部使用 */
|
/** @experimental 内部使用 */
|
||||||
requestHandlersMap: Record<string, RequestHandler<{
|
requestHandlersMap: Record<string, RequestHandler<{
|
||||||
data: unknown;
|
data: unknown;
|
||||||
@ -86,25 +94,34 @@ export type IRendererAppHelper = Partial<{
|
|||||||
* @see @todo @承虎
|
* @see @todo @承虎
|
||||||
*/
|
*/
|
||||||
export interface IRendererProps {
|
export interface IRendererProps {
|
||||||
|
|
||||||
/** 符合低代码搭建协议的数据 */
|
/** 符合低代码搭建协议的数据 */
|
||||||
schema: IPublicTypeRootSchema | IPublicTypeNodeSchema;
|
schema: IPublicTypeRootSchema | IPublicTypeNodeSchema;
|
||||||
|
|
||||||
/** 组件依赖的实例 */
|
/** 组件依赖的实例 */
|
||||||
components: Record<string, IGeneralComponent>;
|
components: Record<string, IGeneralComponent>;
|
||||||
|
|
||||||
/** CSS 类名 */
|
/** CSS 类名 */
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
||||||
/** style */
|
/** style */
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
|
|
||||||
/** id */
|
/** id */
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
|
|
||||||
/** 语言 */
|
/** 语言 */
|
||||||
locale?: string;
|
locale?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多语言语料
|
* 多语言语料
|
||||||
* 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode 中 2.6 国际化多语言支持
|
* 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode 中 2.6 国际化多语言支持
|
||||||
* */
|
* */
|
||||||
messages?: Record<string, any>;
|
messages?: Record<string, any>;
|
||||||
|
|
||||||
/** 主要用于设置渲染模块的全局上下文,里面定义的内容可以在低代码中通过 this 来访问,比如 this.utils */
|
/** 主要用于设置渲染模块的全局上下文,里面定义的内容可以在低代码中通过 this 来访问,比如 this.utils */
|
||||||
appHelper?: IRendererAppHelper;
|
appHelper?: IRendererAppHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode
|
* 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode
|
||||||
* 主要在搭建场景中使用,用于提升用户搭建体验。
|
* 主要在搭建场景中使用,用于提升用户搭建体验。
|
||||||
@ -112,33 +129,46 @@ export interface IRendererProps {
|
|||||||
* > 在生产环境下不需要设置
|
* > 在生产环境下不需要设置
|
||||||
*/
|
*/
|
||||||
componentsMap?: { [key: string]: any };
|
componentsMap?: { [key: string]: any };
|
||||||
|
|
||||||
/** 设计模式,可选值:live、design */
|
/** 设计模式,可选值:live、design */
|
||||||
designMode?: string;
|
designMode?: string;
|
||||||
|
|
||||||
/** 渲染模块是否挂起,当设置为 true 时,渲染模块最外层容器的 shouldComponentUpdate 将始终返回false,在下钻编辑或者多引擎渲染的场景会用到该参数。 */
|
/** 渲染模块是否挂起,当设置为 true 时,渲染模块最外层容器的 shouldComponentUpdate 将始终返回false,在下钻编辑或者多引擎渲染的场景会用到该参数。 */
|
||||||
suspended?: boolean;
|
suspended?: boolean;
|
||||||
|
|
||||||
/** 组件获取 ref 时触发的钩子 */
|
/** 组件获取 ref 时触发的钩子 */
|
||||||
onCompGetRef?: (schema: IPublicTypeNodeSchema, ref: any) => void;
|
onCompGetRef?: (schema: IPublicTypeNodeSchema, ref: any) => void;
|
||||||
|
|
||||||
/** 组件 ctx 更新回调 */
|
/** 组件 ctx 更新回调 */
|
||||||
onCompGetCtx?: (schema: IPublicTypeNodeSchema, ref: any) => void;
|
onCompGetCtx?: (schema: IPublicTypeNodeSchema, ref: any) => void;
|
||||||
|
|
||||||
/** 传入的 schema 是否有变更 */
|
/** 传入的 schema 是否有变更 */
|
||||||
getSchemaChangedSymbol?: () => boolean;
|
getSchemaChangedSymbol?: () => boolean;
|
||||||
|
|
||||||
/** 设置 schema 是否有变更 */
|
/** 设置 schema 是否有变更 */
|
||||||
setSchemaChangedSymbol?: (symbol: boolean) => void;
|
setSchemaChangedSymbol?: (symbol: boolean) => void;
|
||||||
|
|
||||||
/** 自定义创建 element 的钩子 */
|
/** 自定义创建 element 的钩子 */
|
||||||
customCreateElement?: (Component: any, props: any, children: any) => any;
|
customCreateElement?: (Component: any, props: any, children: any) => any;
|
||||||
|
|
||||||
/** 渲染类型,标识当前模块是以什么类型进行渲染的 */
|
/** 渲染类型,标识当前模块是以什么类型进行渲染的 */
|
||||||
rendererName?: 'LowCodeRenderer' | 'PageRenderer' | string;
|
rendererName?: 'LowCodeRenderer' | 'PageRenderer' | string;
|
||||||
|
|
||||||
/** 当找不到组件时,显示的组件 */
|
/** 当找不到组件时,显示的组件 */
|
||||||
notFoundComponent?: IGeneralComponent;
|
notFoundComponent?: IGeneralComponent;
|
||||||
|
|
||||||
/** 当组件渲染异常时,显示的组件 */
|
/** 当组件渲染异常时,显示的组件 */
|
||||||
faultComponent?: IGeneralComponent;
|
faultComponent?: IGeneralComponent;
|
||||||
|
|
||||||
/** 设备信息 */
|
/** 设备信息 */
|
||||||
device?: string;
|
device?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @default true
|
* @default true
|
||||||
* JSExpression 是否只支持使用 this 来访问上下文变量
|
* JSExpression 是否只支持使用 this 来访问上下文变量
|
||||||
*/
|
*/
|
||||||
thisRequiredInJSE?: boolean;
|
thisRequiredInJSE?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @default false
|
* @default false
|
||||||
* 当开启组件未找到严格模式时,渲染模块不会默认给一个容器组件
|
* 当开启组件未找到严格模式时,渲染模块不会默认给一个容器组件
|
||||||
@ -162,7 +192,7 @@ export interface IBaseRendererProps {
|
|||||||
__ctx: Record<string, any>;
|
__ctx: Record<string, any>;
|
||||||
__schema: IPublicTypeRootSchema;
|
__schema: IPublicTypeRootSchema;
|
||||||
__host?: BuiltinSimulatorHost;
|
__host?: BuiltinSimulatorHost;
|
||||||
__container?: any;
|
__container?: BuiltinSimulatorRenderer;
|
||||||
config?: Record<string, any>;
|
config?: Record<string, any>;
|
||||||
designMode?: 'design';
|
designMode?: 'design';
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -173,6 +203,7 @@ export interface IBaseRendererProps {
|
|||||||
thisRequiredInJSE?: boolean;
|
thisRequiredInJSE?: boolean;
|
||||||
documentId?: string;
|
documentId?: string;
|
||||||
getNode?: any;
|
getNode?: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备类型,默认值:'default'
|
* 设备类型,默认值:'default'
|
||||||
*/
|
*/
|
||||||
@ -213,13 +244,13 @@ export interface DataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IRuntime {
|
export interface IRuntime {
|
||||||
|
[key: string]: any;
|
||||||
Component: IGeneralConstructor;
|
Component: IGeneralConstructor;
|
||||||
PureComponent: IGeneralConstructor;
|
PureComponent: IGeneralConstructor;
|
||||||
createElement: (...args: any) => any;
|
createElement: (...args: any) => any;
|
||||||
createContext: (...args: any) => any;
|
createContext: (...args: any) => any;
|
||||||
forwardRef: (...args: any) => any;
|
forwardRef: (...args: any) => any;
|
||||||
findDOMNode: (...args: any) => any;
|
findDOMNode: (...args: any) => any;
|
||||||
[key: string]: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRendererModules {
|
export interface IRendererModules {
|
||||||
@ -285,21 +316,22 @@ export interface IBaseRenderComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IRenderComponent {
|
export interface IRenderComponent {
|
||||||
|
displayName: string;
|
||||||
|
defaultProps: IRendererProps;
|
||||||
|
findDOMNode: (...args: any) => any;
|
||||||
|
|
||||||
new(props: IRendererProps, context: any): IGeneralComponent<IRendererProps, IRendererState> & {
|
new(props: IRendererProps, context: any): IGeneralComponent<IRendererProps, IRendererState> & {
|
||||||
[x: string]: any;
|
[x: string]: any;
|
||||||
|
__getRef: (ref: any) => void;
|
||||||
componentDidMount(): Promise<void>;
|
componentDidMount(): Promise<void>;
|
||||||
componentDidUpdate(): Promise<void>;
|
componentDidUpdate(): Promise<void>;
|
||||||
componentWillUnmount(): Promise<void>;
|
componentWillUnmount(): Promise<void>;
|
||||||
componentDidCatch(e: any): Promise<void>;
|
componentDidCatch(e: any): Promise<void>;
|
||||||
shouldComponentUpdate(nextProps: IRendererProps): boolean;
|
shouldComponentUpdate(nextProps: IRendererProps): boolean;
|
||||||
__getRef: (ref: any) => void;
|
|
||||||
isValidComponent(SetComponent: any): any;
|
isValidComponent(SetComponent: any): any;
|
||||||
patchDidCatch(SetComponent: any): void;
|
patchDidCatch(SetComponent: any): void;
|
||||||
createElement(SetComponent: any, props: any, children?: any): any;
|
createElement(SetComponent: any, props: any, children?: any): any;
|
||||||
getNotFoundComponent(): any;
|
getNotFoundComponent(): any;
|
||||||
getFaultComponent(): any;
|
getFaultComponent(): any;
|
||||||
};
|
};
|
||||||
displayName: string;
|
|
||||||
defaultProps: IRendererProps;
|
|
||||||
findDOMNode: (...args: any) => any;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -324,6 +324,7 @@ describe('mini unit render', () => {
|
|||||||
it('change component leaf isRoot is true', () => {
|
it('change component leaf isRoot is true', () => {
|
||||||
const TextNode = new Node(textSchema, {
|
const TextNode = new Node(textSchema, {
|
||||||
isRoot: true,
|
isRoot: true,
|
||||||
|
isRootNode: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
nodeMap.set(textSchema.id, TextNode);
|
nodeMap.set(textSchema.id, TextNode);
|
||||||
@ -356,6 +357,7 @@ describe('mini unit render', () => {
|
|||||||
id: 'rootId',
|
id: 'rootId',
|
||||||
}, {
|
}, {
|
||||||
isRoot: true,
|
isRoot: true,
|
||||||
|
isRootNode: true
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import React, { Component, createElement, PureComponent, createContext } from 'react';
|
import React, { Component, createElement, forwardRef, PureComponent, createContext } from 'react';
|
||||||
const mockGetRenderers = jest.fn();
|
const mockGetRenderers = jest.fn();
|
||||||
const mockGetRuntime = jest.fn();
|
const mockGetRuntime = jest.fn();
|
||||||
const mockParseExpression = jest.fn();
|
const mockParseExpression = jest.fn();
|
||||||
@ -59,6 +59,7 @@ describe('Base Render methods', () => {
|
|||||||
createElement,
|
createElement,
|
||||||
PureComponent,
|
PureComponent,
|
||||||
createContext,
|
createContext,
|
||||||
|
forwardRef,
|
||||||
});
|
});
|
||||||
RendererClass = baseRendererFactory();
|
RendererClass = baseRendererFactory();
|
||||||
})
|
})
|
||||||
|
|||||||
@ -40,6 +40,10 @@ export default class Node {
|
|||||||
|
|
||||||
isRoot = () => this._isRoot;
|
isRoot = () => this._isRoot;
|
||||||
|
|
||||||
|
get isRootNode () {
|
||||||
|
return this._isRoot;
|
||||||
|
};
|
||||||
|
|
||||||
// componentMeta() {
|
// componentMeta() {
|
||||||
// return this.componentMeta;
|
// return this.componentMeta;
|
||||||
// }
|
// }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user