feat: the renderer-core leaf component removes the react.createElement call

This commit is contained in:
liujuping 2023-02-07 17:18:47 +08:00 committed by 林熠
parent 0184dcd938
commit c3ce042c83
10 changed files with 160 additions and 98 deletions

View File

@ -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(

View File

@ -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 {

View File

@ -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 () => {

View File

@ -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);

View File

@ -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,

View File

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

View File

@ -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;
} }

View File

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

View File

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

View File

@ -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;
// } // }