diff --git a/packages/rax-simulator-renderer/src/renderer-view.tsx b/packages/rax-simulator-renderer/src/renderer-view.tsx index 7a597ca14..711053e80 100644 --- a/packages/rax-simulator-renderer/src/renderer-view.tsx +++ b/packages/rax-simulator-renderer/src/renderer-view.tsx @@ -209,6 +209,7 @@ class Renderer extends Component<{ onCompGetRef={(schema: any, ref: any) => { documentInstance.mountInstance(schema.id, ref); }} + getNode={(id: string) => documentInstance.getNode(id) as any} customCreateElement={(Component: any, props: any, children: any) => { const { __id, ...viewProps } = props; viewProps.componentId = __id; diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx index ce9a79f91..3c0978aa3 100644 --- a/packages/react-simulator-renderer/src/renderer-view.tsx +++ b/packages/react-simulator-renderer/src/renderer-view.tsx @@ -4,7 +4,7 @@ import cn from 'classnames'; import { Node } from '@ali/lowcode-designer'; import LowCodeRenderer from '@ali/lowcode-react-renderer'; import { observer } from 'mobx-react'; -import { getClosestNode } from '@ali/lowcode-utils'; +import { getClosestNode, isFromVC } from '@ali/lowcode-utils'; import { GlobalEvent } from '@ali/lowcode-types'; import { SimulatorRendererContainer, DocumentInstance } from './renderer'; import { host } from './host'; @@ -132,6 +132,10 @@ class Renderer extends Component<{ startTime: number | null = null; componentDidUpdate() { + this.recordTime(); + } + + recordTime() { if (this.startTime) { const time = Date.now() - this.startTime; const nodeCount = host.designer.currentDocument?.getNodeCount?.(); @@ -144,6 +148,10 @@ class Renderer extends Component<{ } } + componentDidMount() { + this.recordTime(); + } + render() { const { documentInstance, rendererContainer: renderer } = this.props; const { container } = documentInstance; @@ -166,11 +174,14 @@ class Renderer extends Component<{ device={device} suspended={renderer.suspended} self={renderer.scope} + getNode={(id: string) => documentInstance.getNode(id) as Node} customCreateElement={(Component: any, props: any, children: any) => { const { __id, ...viewProps } = props; viewProps.componentId = __id; const leaf = documentInstance.getNode(__id) as Node; - viewProps._leaf = leaf; + if (isFromVC(leaf?.componentMeta)) { + viewProps._leaf = leaf; + } viewProps._componentName = leaf?.componentName; // 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动 if ( diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index 31efffef4..1cc0d07f5 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -4,7 +4,12 @@ import { EngineOptions } from '@ali/lowcode-editor-core'; import adapter from '../adapter'; import * as types from '../types/index'; -const compDefaultPropertyNames = ['$$typeof', 'render', 'defaultProps']; +const compDefaultPropertyNames = [ + '$$typeof', + 'render', + 'defaultProps', + 'props', +]; export interface IComponentHocInfo { schema: any; @@ -31,6 +36,8 @@ interface IProps { componentId?: number; children?: Node[]; + + __tag?: number; } enum RerenderType { @@ -55,10 +62,20 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } = baseRenderer; const engine = baseRenderer.context.engine; const host: BuiltinSimulatorHost = baseRenderer.props.__host; + const getNode = baseRenderer.props?.getNode; const container: BuiltinSimulatorHost = baseRenderer.props.__container; const editor = host?.designer?.editor; const { Component } = adapter.getRuntime(); + /** 部分没有渲染的 node 节点进行兜底处理 or 渲染方式没有渲染 LeafWrapper */ + const leaf = getNode(schema.id); + + const wrapDisposeFunctions: Function[] = [ + leaf?.onPropsChange?.(() => container.rerender()), + leaf?.onChildrenChange?.(() => container.rerender()), + leaf?.onVisibleChange?.(() => container.rerender()), + ]; + class LeafWrapper extends Component { recordInfo: { startTime?: number | null; @@ -96,9 +113,11 @@ export function leafWrapper(Comp: types.IBaseRenderer, { this.initOnPropsChangeEvent(); this.initOnChildrenChangeEvent(); this.initOnVisibleChangeEvent(); + wrapDisposeFunctions.forEach(d => d && d()); this.state = { nodeChildren: null, childrenInState: false, + __tag: props.__tag, }; } @@ -109,8 +128,25 @@ export function leafWrapper(Comp: types.IBaseRenderer, { this.recordInfo.node = node; } + get isInWhitelist() { + return whitelist.includes(schema.componentName); + } + + static getDerivedStateFromProps(props: any, state: any) { + if (props.__tag === state.__tag) { + return null; + } + + return { + nodeChildren: props.children, + nodeProps: props.nodeProps, + childrenInState: true, + __tag: props.__tag, + }; + } + shouldComponentUpdate() { - if (whitelist.includes(schema.componentName)) { + if (this.isInWhitelist) { __debug(`${schema.componentName} is in leaf Hoc whitelist`); container.rerender(); return false; @@ -130,6 +166,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, { // 如果循坏条件变化,从根节点重新渲染 // 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决 if (key === '___loop___') { + __debug('key is ___loop___, render a page!'); container.rerender(); return; } @@ -218,25 +255,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } get leaf(): Node | undefined { - return this.props._leaf; - } - - 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[0]); - return; - } - map.set(d.props.componentId, d); - }); - - return map; + return this.props._leaf || getNode(this.props.componentId); } get visible(): boolean { @@ -268,6 +287,8 @@ export function leafWrapper(Comp: types.IBaseRenderer, { if (typeof Comp === 'object') { const compExtraPropertyNames = Object.getOwnPropertyNames(Comp).filter(d => !compDefaultPropertyNames.includes(d)); + __debug(`${schema.componentName} extra property names: ${compExtraPropertyNames.join(',')}`); + compExtraPropertyNames.forEach((d: string) => { (LeafWrapper as any)[d] = Comp[d]; }); diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx index a31d35fec..f6da10f87 100644 --- a/packages/renderer-core/src/renderer/base.tsx +++ b/packages/renderer-core/src/renderer/base.tsx @@ -342,16 +342,10 @@ export default function baseRenererFactory() { } const _children = this.getSchemaChildren(__schema); let Comp = __components[__schema.componentName]; - this.componentHoc.forEach((ComponentConstruct: IComponentConstruct) => { - Comp = ComponentConstruct(Comp || Div, { - schema: __schema, - componentInfo: {}, - baseRenderer: this, - }); - }); + return this.__createVirtualDom(_children, self, ({ schema: __schema, - Comp, + Comp: this.__getHocComp(Comp, __schema), } as IInfo)); }; @@ -478,6 +472,9 @@ export default function baseRenererFactory() { if (engine?.props?.designMode) { otherProps.__designMode = engine.props.designMode; } + if (this._designModeIsDesign) { + otherProps.__tag = Math.random(); + } const componentInfo: any = {}; const props: any = this.__getComponentProps(schema, Comp, { ...componentInfo, @@ -494,7 +491,6 @@ export default function baseRenererFactory() { }); } - // 对于可以获取到ref的组件做特殊处理 if (!acceptsRef(Comp) && !this.__hoc_components[schema.componentName]) { Comp = compWrapper(Comp); @@ -605,8 +601,8 @@ export default function baseRenererFactory() { _children.forEach((_child: any) => { const _childVirtualDom = this.__createVirtualDom( - isJSExpression(_child) ? parseExpression(_child, self) : _child, - self, + isJSExpression(_child) ? parseExpression(_child, this.self) : _child, + this.self, { schema, Comp, @@ -813,7 +809,6 @@ export default function baseRenererFactory() { __renderContextProvider = (customProps?: object, children?: any) => { customProps = customProps || {}; children = children || this.__createDom(); - this.__hoc_components = {}; return createElement(AppContext.Provider, { value: { ...this.context, @@ -828,26 +823,37 @@ export default function baseRenererFactory() { return createElement(AppContext.Consumer, {}, children); }; + __getHocComp(Comp: any, schema: any) { + if (!this.__hoc_components[schema.componentName]) { + this.componentHoc.forEach((ComponentConstruct: IComponentConstruct) => { + Comp = ComponentConstruct(Comp || Div, { + schema, + componentInfo: {}, + baseRenderer: this, + }); + }); + } else { + Comp = this.__hoc_components[schema.componentName]; + this.__hoc_components[schema.componentName] = Comp; + } + + return Comp; + } + __renderComp(Comp: any, ctxProps: object) { const { __schema } = this.props; + Comp = this.__getHocComp(Comp, __schema); const data = this.__parseProps(__schema?.props, this.self, '', { schema: __schema, Comp, componentInfo: {}, }); - this.__hoc_components = {}; const { className } = data; const { engine } = this.context || {}; if (!engine) { return null; } - this.componentHoc.forEach((ComponentConstruct: IComponentConstruct) => { - Comp = ComponentConstruct(Comp || Div, { - schema: __schema, - componentInfo: {}, - baseRenderer: this, - }); - }); + const child = engine.createElement( Comp, { diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 08133d26a..ccf2bd652 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -61,6 +61,14 @@ export function arrShallowEquals(arr1: any[], arr2: any[]): boolean { return arr1.every(item => arr2.includes(item)); } +/** + * 判断当前 meta 是否从 vc prototype 转换而来 + * @param meta + */ + export function isFromVC(meta: ComponentMeta) { + return !!meta?.getMetadata()?.experimental; +} + export function executePendingFn(fn: () => void, timeout: number = 2000) { return setTimeout(fn, timeout); }