diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts
index 9baa5726d..fec7eedf6 100644
--- a/packages/designer/src/component-meta.ts
+++ b/packages/designer/src/component-meta.ts
@@ -85,6 +85,10 @@ export class ComponentMeta {
return this._isContainer! || this.isRootComponent();
}
+ get isMinimalRenderUnit(): boolean {
+ return this._isMinimalRenderUnit || false;
+ }
+
private _isModal?: boolean;
get isModal(): boolean {
@@ -128,6 +132,8 @@ export class ComponentMeta {
private _title?: TitleContent;
+ private _isMinimalRenderUnit?: boolean;
+
get title(): string | I18nData | ReactElement {
// TODO: 标记下。这块需要康师傅加一下API,页面正常渲染。
// string | i18nData | ReactElement
@@ -220,6 +226,7 @@ export class ComponentMeta {
this._isModal = !!component.isModal;
this._descriptor = component.descriptor;
this._rootSelector = component.rootSelector;
+ this._isMinimalRenderUnit = component.isMinimalRenderUnit;
if (component.nestingRule) {
const { parentWhitelist, childWhitelist } = component.nestingRule;
this.parentWhitelist = buildFilter(parentWhitelist);
diff --git a/packages/ignitor/public/favicon.png b/packages/ignitor/public/favicon.png
deleted file mode 100644
index 307ffbd82..000000000
Binary files a/packages/ignitor/public/favicon.png and /dev/null differ
diff --git a/packages/ignitor/public/index.html b/packages/ignitor/public/index.html
deleted file mode 100644
index 1d64096b5..000000000
--- a/packages/ignitor/public/index.html
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
-
-
- LowCodeEngine Editor DEMO
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/ignitor/public/preview.html b/packages/ignitor/public/preview.html
deleted file mode 100644
index c8f2cfdcb..000000000
--- a/packages/ignitor/public/preview.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
- LowCodeEngine DEMO
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json
index 41fe3cbe6..d2562e16e 100644
--- a/packages/renderer-core/package.json
+++ b/packages/renderer-core/package.json
@@ -29,7 +29,8 @@
"react-is": "^16.10.1",
"serialize-javascript": "^1.7.0",
"socket.io-client": "^2.2.0",
- "whatwg-fetch": "^3.0.0"
+ "whatwg-fetch": "^3.0.0",
+ "zen-logger": "^1.1.4"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
diff --git a/packages/renderer-core/src/hoc/index.tsx b/packages/renderer-core/src/hoc/index.tsx
index e8745f5e9..b8ab424c5 100644
--- a/packages/renderer-core/src/hoc/index.tsx
+++ b/packages/renderer-core/src/hoc/index.tsx
@@ -1,3 +1,4 @@
+import { cloneEnumerableProperty } from '@ali/lowcode-utils';
import adapter from '../adapter';
export function compWrapper(Comp: any) {
@@ -8,15 +9,16 @@ export function compWrapper(Comp: any) {
// }
render() {
- const { forwardRef } = this.props;
+ const { forwardRef, ...rest } = this.props;
+
return createElement(Comp, {
- ...this.props,
+ ...rest,
ref: forwardRef,
});
}
}
- return forwardRef((props: any, ref: any) => {
+ return cloneEnumerableProperty(forwardRef((props: any, ref: any) => {
return createElement(Wrapper, { ...props, forwardRef: ref });
- });
+ }), Comp);
}
diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx
index 41b3b22ff..951ceea27 100644
--- a/packages/renderer-core/src/hoc/leaf.tsx
+++ b/packages/renderer-core/src/hoc/leaf.tsx
@@ -1,21 +1,16 @@
import { BuiltinSimulatorHost, Node, PropChangeOptions } from '@ali/lowcode-designer';
import { GlobalEvent, TransformStage } from '@ali/lowcode-types';
-import { isReactComponent } from '@ali/lowcode-utils';
+import { isReactComponent, cloneEnumerableProperty } from '@ali/lowcode-utils';
import { EngineOptions } from '@ali/lowcode-editor-core';
import adapter from '../adapter';
import * as types from '../types/index';
-const compDefaultPropertyNames = [
- '$$typeof',
- 'render',
- 'defaultProps',
- 'props',
-];
export interface IComponentHocInfo {
schema: any;
baseRenderer: types.IBaseRendererInstance;
componentInfo: any;
+ scope: any;
}
type DesignMode = Pick['designMode'];
@@ -45,6 +40,7 @@ enum RerenderType {
ChildChanged = 'ChildChanged',
PropsChanged = 'PropsChanged',
VisibleChanged = 'VisibleChanged',
+ MinimalRenderUnit = 'MinimalRenderUnit',
}
// 缓存 Leaf 层组件,防止重新渲染问题
@@ -63,6 +59,8 @@ class LeafCache {
* 订阅事件缓存,导致 rerender 的订阅事件
*/
event = new Map();
+
+ ref = new Map();
}
let cache: LeafCache;
@@ -119,6 +117,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
schema,
baseRenderer,
componentInfo,
+ scope,
}: IComponentHocInfo) {
const {
__debug,
@@ -152,8 +151,8 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
getNode,
});
- if (curDocumentId && cache.component.has(schema.componentName)) {
- return cache.component.get(schema.componentName);
+ if (curDocumentId && cache.component.has(schema.id)) {
+ return cache.component.get(schema.id);
}
class LeafHoc extends Component {
@@ -259,9 +258,71 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
setSchemaChangedSymbol?.(true);
}
- // get isInWhitelist() {
- // return whitelist.includes(schema.componentName);
- // }
+ renderUnitInfo: {
+ minimalUnitId?: string,
+ minimalUnitName?: string;
+ singleRender?: boolean,
+ };
+
+ shouldRenderSingleNode(): boolean {
+ if (!this.renderUnitInfo) {
+ this.getRenderUnitInfo();
+ }
+
+ const renderUnitInfo = this.renderUnitInfo;
+
+ if (renderUnitInfo.singleRender) {
+ return true;
+ }
+
+ const ref = cache.ref.get(renderUnitInfo.minimalUnitId);
+
+ if (!ref) {
+ __debug('Cant find minimalRenderUnit ref! This make rerender!');
+ container.rerender();
+ return false;
+ }
+ __debug(`${this.leaf?.componentName}(${this.props.componentId}) need render, make its minimalRenderUnit ${renderUnitInfo.minimalUnitName}(${renderUnitInfo.minimalUnitId})`);
+ ref.makeUnitRender();
+
+ return false;
+ }
+
+ getRenderUnitInfo(leaf = this.leaf) {
+ if (leaf?.isRoot()) {
+ this.renderUnitInfo = {
+ singleRender: true,
+ ...(this.renderUnitInfo || {}),
+ };
+ }
+ if (leaf?.componentMeta.isMinimalRenderUnit) {
+ this.renderUnitInfo = {
+ minimalUnitId: leaf.id,
+ minimalUnitName: leaf.componentName,
+ singleRender: false,
+ };
+ }
+ if (leaf?.parent) {
+ this.getRenderUnitInfo(leaf.parent);
+ }
+ }
+
+ makeUnitRender() {
+ this.beforeRender(RerenderType.MinimalRenderUnit);
+ const nextProps = getProps(this.leaf?.export?.(TransformStage.Render) as types.ISchema, scope, Comp, componentInfo);
+ const children = getChildren(this.leaf?.export?.(TransformStage.Render) as types.ISchema, scope, Comp);
+ const nextState = {
+ 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);
+ }
componentWillReceiveProps(nextProps: any) {
let { _leaf, componentId } = nextProps;
@@ -291,14 +352,10 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
const dispose = leaf?.onPropChange?.((propChangeInfo: PropChangeOptions) => {
const {
key,
+ newValue = null,
} = propChangeInfo;
const node = leaf;
- // if (this.isInWhitelist) {
- // container.rerender();
- // return;
- // }
-
// 如果循坏条件变化,从根节点重新渲染
// 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决
if (key === '___loop___') {
@@ -306,15 +363,24 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
container.rerender();
return;
}
-
+ if (!this.shouldRenderSingleNode()) {
+ return;
+ }
this.beforeRender(RerenderType.PropsChanged);
- __debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onPropsChange event`);
- const nextProps = getProps(node?.export?.(TransformStage.Render) as types.ISchema, Comp, componentInfo);
- this.setState(nextProps.children ? {
- nodeChildren: nextProps.children,
- nodeProps: nextProps,
+ const nodeProps = getProps(node?.export?.(TransformStage.Render) as types.ISchema, scope, Comp, componentInfo);
+ const preNodeProps = this.state.nodeProps;
+ const newNodeProps = {
+ ...preNodeProps,
+ [key as string]: newValue,
+ ...nodeProps,
+ };
+ __debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onPropsChange event`, newNodeProps);
+ this.setState('children' in nodeProps ? {
+ nodeChildren: nodeProps.children,
+ nodeProps: newNodeProps,
+ childrenInState: true,
} : {
- nodeProps: nextProps,
+ nodeProps: newNodeProps,
});
});
@@ -330,12 +396,11 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
return;
}
- // if (this.isInWhitelist) {
- // container.rerender();
- // return;
- // }
+ if (!this.shouldRenderSingleNode()) {
+ return;
+ }
- __debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onVisibleChange event`);
+ __debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onVisibleChange(${flag}) event`);
this.beforeRender(RerenderType.VisibleChanged);
this.setState({
visible: flag,
@@ -354,16 +419,15 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
type,
node,
} = param || {};
- // if (this.isInWhitelist) {
- // container.rerender();
- // return;
- // }
+ if (!this.shouldRenderSingleNode()) {
+ return;
+ }
this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node);
- __debug(`${schema.componentName}[${this.props.componentId}] component trigger onChildrenChange event`);
// TODO: 缓存同级其他元素的 children。
// 缓存二级 children Next 查询筛选组件有问题
// 缓存一级 children Next Tab 组件有问题
- const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, Comp); // this.childrenMap
+ const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, scope, Comp); // this.childrenMap
+ __debug(`${schema.componentName}[${this.props.componentId}] component trigger onChildrenChange event`, nextChild);
this.setState({
nodeChildren: nextChild,
childrenInState: true,
@@ -391,7 +455,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
}
get children(): any {
- if (this.state.nodeChildren) {
+ if (this.state.childrenInState) {
return this.state.nodeChildren;
}
if (this.props.children && !Array.isArray(this.props.children)) {
@@ -413,7 +477,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
}
const {
- ref,
+ forwardedRef,
...rest
} = this.props;
@@ -422,33 +486,27 @@ export function leafWrapper(Comp: types.IBaseRenderer, {
...(this.state.nodeProps || {}),
children: [],
__id: this.props.componentId,
- ref: this.props.forwardedRef,
+ ref: forwardedRef,
};
return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null);
}
}
- const LeafWrapper = forwardRef((props: any, ref: any) => (
+ let LeafWrapper = forwardRef((props: any, ref: any) => (
// @ts-ignore
-
+ cache.ref.set(props.componentId, _ref)}
+ />
));
- 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];
- });
- }
+ LeafWrapper = cloneEnumerableProperty(LeafWrapper, Comp);
LeafWrapper.displayName = (Comp as any).displayName;
- if (curDocumentId) {
- cache.component.set(schema.componentName, LeafWrapper);
- }
+ cache.component.set(schema.id, LeafWrapper);
return LeafWrapper;
}
\ No newline at end of file
diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx
index 8fcbf8e86..67a0d7542 100644
--- a/packages/renderer-core/src/renderer/base.tsx
+++ b/packages/renderer-core/src/renderer/base.tsx
@@ -1,5 +1,4 @@
import classnames from 'classnames';
-import Debug from 'debug';
import { create as createDataSourceEngine } from '@ali/lowcode-datasource-engine/interpret';
import adapter from '../adapter';
import divFactory from '../components/Div';
@@ -21,7 +20,7 @@ import {
transformStringToFunction,
checkPropTypes,
getI18n,
- acceptsRef,
+ canAcceptsRef,
getFileCssName,
capitalizeFirstLetter,
DataHelper,
@@ -31,6 +30,7 @@ import {
import { IRendererProps, ISchema, IInfo, ComponentModel, IRenderer } from '../types';
import { compWrapper } from '../hoc';
import { IComponentConstruct, IComponentHoc, leafWrapper } from '../hoc/leaf';
+import logger from '../utils/logger';
export default function baseRenererFactory() {
const { BaseRenderer: customBaseRenderer } = adapter.getRenderers();
@@ -44,7 +44,6 @@ export default function baseRenererFactory() {
const VisualDom = visualDomFactory();
const AppContext = contextFactory();
- const debug = Debug('renderer:base');
const DESIGN_MODE = {
EXTEND: 'extend',
BORDER: 'border',
@@ -71,7 +70,6 @@ export default function baseRenererFactory() {
this.__beforeInit(props);
this.__init(props);
this.__afterInit(props);
- this.__initDebug();
this.__debug(`constructor - ${props?.__schema?.fileName}`);
}
@@ -88,7 +86,7 @@ export default function baseRenererFactory() {
__afterInit(/* props: IRendererProps */) { }
static getDerivedStateFromProps(props: IRendererProps, state: any) {
- debug('getDerivedStateFromProps');
+ logger.log('getDerivedStateFromProps');
const func = props?.__schema?.lifeCycles?.getDerivedStateFromProps;
if (func) {
@@ -347,34 +345,31 @@ export default function baseRenererFactory() {
__createDom = () => {
const { __schema, __ctx, __components = {} } = this.props;
- const self: any = {};
- self.__proto__ = __ctx || this;
+ const scope: any = {};
+ scope.__proto__ = __ctx || this;
if (!this._self) {
- this._self = self;
+ this._self = scope;
}
const _children = this.getSchemaChildren(__schema);
let Comp = __components[__schema.componentName];
- return this.__createVirtualDom(_children, self, ({
+ if (!Comp) {
+ this.__debug(`${__schema.componentName} is invalid!`);
+ }
+
+ return this.__createVirtualDom(_children, scope, ({
schema: __schema,
- Comp: this.__getHocComp(Comp, __schema),
+ Comp: this.__getHocComp(Comp, __schema, scope),
} as IInfo));
};
- private get self() {
- const { __ctx } = this.props;
- const self: any = {};
- self.__proto__ = __ctx || this;
-
- return self;
- }
// 将模型结构转换成react Element
// schema 模型结构
// self 为每个渲染组件构造的上下文,self是自上而下继承的
// parentInfo 父组件的信息,包含schema和Comp
// idx 若为循环渲染的循环Index
- __createVirtualDom = (schema: ISchema, self: any, parentInfo: IInfo, idx: string | number = ''): any => {
+ __createVirtualDom = (schema: ISchema, scope: any, parentInfo: IInfo, idx: string | number = ''): any => {
const { engine } = this.context || {};
try {
if (!schema) return null;
@@ -388,28 +383,28 @@ export default function baseRenererFactory() {
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
if (isJSExpression(schema)) {
- return parseExpression(schema, self);
+ return parseExpression(schema, scope);
}
if (isI18n(schema)) {
- return parseI18n(schema, self);
+ return parseI18n(schema, scope);
}
if (isJSSlot(schema)) {
- return this.__createVirtualDom(schema.value, self, parentInfo);
+ return this.__createVirtualDom(schema.value, scope, parentInfo);
}
if (typeof schema === 'string') return schema;
if (typeof schema === 'number' || typeof schema === 'boolean') {
return String(schema);
}
if (Array.isArray(schema)) {
- if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
- return schema.map((item, idy) => this.__createVirtualDom(item, self, parentInfo, item?.__ctx?.lceKey ? '' : String(idy)));
+ if (schema.length === 1) return this.__createVirtualDom(schema[0], scope, parentInfo);
+ return schema.map((item, idy) => this.__createVirtualDom(item, scope, parentInfo, item?.__ctx?.lceKey ? '' : String(idy)));
}
// FIXME
const _children = this.getSchemaChildren(schema);
// 解析占位组件
if (schema.componentName === 'Flagment' && _children) {
- const tarChildren = isJSExpression(_children) ? parseExpression(_children, self) : _children;
- return this.__createVirtualDom(tarChildren, self, parentInfo);
+ const tarChildren = isJSExpression(_children) ? parseExpression(_children, scope) : _children;
+ return this.__createVirtualDom(tarChildren, scope, parentInfo);
}
if (schema.$$typeof) {
@@ -429,26 +424,26 @@ export default function baseRenererFactory() {
}
if (schema.loop != null) {
- const loop = parseData(schema.loop, self);
+ const loop = parseData(schema.loop, scope);
if ((Array.isArray(loop) && loop.length > 0) || isJSExpression(loop)) {
return this.__createLoopVirtualDom(
{
...schema,
loop,
},
- self,
+ scope,
parentInfo,
idx,
);
}
}
- const condition = schema.condition == null ? true : parseData(schema.condition, self);
+ const condition = schema.condition == null ? true : parseData(schema.condition, scope);
if (!condition) return null;
let scopeKey = '';
// 判断组件是否需要生成scope,且只生成一次,挂在this.__compScopes上
if (Comp.generateScope) {
- const key = parseExpression(schema.props.key, self);
+ const key = parseExpression(schema.props.key, scope);
if (key) {
// 如果组件自己设置key则使用组件自己的key
scopeKey = key;
@@ -469,8 +464,8 @@ export default function baseRenererFactory() {
// 如果组件有设置scope,需要为组件生成一个新的scope上下文
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
- compSelf.__proto__ = self;
- self = compSelf;
+ compSelf.__proto__ = scope;
+ scope = compSelf;
}
// 容器类组件的上下文通过props传递,避免context传递带来的嵌套问题
@@ -488,7 +483,7 @@ export default function baseRenererFactory() {
otherProps.__tag = Math.random();
}
const componentInfo: any = {};
- const props: any = this.__getComponentProps(schema, Comp, {
+ const props: any = this.__getComponentProps(schema, scope, Comp, {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
}) || {};
@@ -498,11 +493,12 @@ export default function baseRenererFactory() {
schema,
componentInfo,
baseRenderer: this,
+ scope,
});
});
- // 对于可以获取到ref的组件做特殊处理
- if (!acceptsRef(Comp)) {
+ // 对于不可以接收到 ref 的组件需要做特殊处理
+ if (!canAcceptsRef(Comp)) {
Comp = compWrapper(Comp);
components[schema.componentName] = Comp;
}
@@ -522,7 +518,7 @@ export default function baseRenererFactory() {
}
if (schema?.__ctx?.lceKey) {
if (!isFileSchema(schema)) {
- engine?.props?.onCompGetCtx(schema, self);
+ engine?.props?.onCompGetCtx(schema, scope);
}
props.key = props.key || `${schema.__ctx.lceKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if (typeof idx === 'number' && !props.key) {
@@ -535,7 +531,7 @@ export default function baseRenererFactory() {
props.key = props.__id;
}
- let child: any = parentInfo.componentChildren || this.__getSchemaChildrenVirtualDom(schema, Comp);
+ let child: any = parentInfo.componentChildren || this.__getSchemaChildrenVirtualDom(schema, scope, Comp);
const renderComp = (props: any) => engine.createElement(Comp, props, child);
// 设计模式下的特殊处理
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
@@ -568,7 +564,7 @@ export default function baseRenererFactory() {
return engine.createElement(engine.getFaultComponent(), {
error: e,
schema,
- self,
+ self: scope,
parentInfo,
idx,
});
@@ -594,7 +590,7 @@ export default function baseRenererFactory() {
.map((d: IComponentHoc) => d.hoc);
}
- __getSchemaChildrenVirtualDom = (schema: ISchema, Comp: any, childrenMap?: Map) => {
+ __getSchemaChildrenVirtualDom = (schema: ISchema, scope: any, Comp: any, childrenMap?: Map) => {
let _children = this.getSchemaChildren(schema);
let children: any = [];
@@ -605,8 +601,8 @@ export default function baseRenererFactory() {
_children.forEach((_child: any) => {
const _childVirtualDom = this.__createVirtualDom(
- isJSExpression(_child) ? parseExpression(_child, this.self) : _child,
- this.self,
+ isJSExpression(_child) ? parseExpression(_child, scope) : _child,
+ scope,
{
schema,
Comp,
@@ -625,11 +621,11 @@ export default function baseRenererFactory() {
return null;
};
- __getComponentProps = (schema: ISchema, Comp: any, componentInfo?: any) => {
+ __getComponentProps = (schema: ISchema, scope: any, Comp: any, componentInfo?: any) => {
if (!schema) {
return {};
}
- return this.__parseProps(schema?.props, this.self, '', {
+ return this.__parseProps(schema?.props, scope, '', {
schema,
Comp,
componentInfo: {
@@ -639,7 +635,7 @@ export default function baseRenererFactory() {
}) || {};
};
- __createLoopVirtualDom = (schema: ISchema, self: any, parentInfo: IInfo, idx: number | string) => {
+ __createLoopVirtualDom = (schema: ISchema, scope: any, parentInfo: IInfo, idx: number | string) => {
if (isFileSchema(schema)) {
console.warn('file type not support Loop');
return null;
@@ -652,7 +648,7 @@ export default function baseRenererFactory() {
[itemArg]: item,
[indexArg]: i,
};
- loopSelf.__proto__ = self;
+ loopSelf.__proto__ = scope;
return this.__createVirtualDom(
{
...schema,
@@ -670,7 +666,7 @@ export default function baseRenererFactory() {
return engine?.props?.designMode === 'design';
}
- __parseProps = (props: any, self: any, path: string, info: IInfo): any => {
+ __parseProps = (props: any, scope: any, path: string, info: IInfo): any => {
const { schema, Comp, componentInfo = {} } = info;
const propInfo = getValue(componentInfo.props, path);
// FIXME! 将这行逻辑外置,解耦,线上环境不要验证参数,调试环境可以有,通过传参自定义
@@ -683,7 +679,7 @@ export default function baseRenererFactory() {
const parseReactNode = (data: any, params: any) => {
if (isEmpty(params)) {
- return checkProps(this.__createVirtualDom(data, self, ({ schema, Comp } as IInfo)));
+ return checkProps(this.__createVirtualDom(data, scope, ({ schema, Comp } as IInfo)));
}
return checkProps(function () {
const args: any = {};
@@ -696,8 +692,8 @@ export default function baseRenererFactory() {
}
});
}
- args.__proto__ = self;
- return self.__createVirtualDom(data, args, { schema, Comp });
+ args.__proto__ = scope;
+ return scope.__createVirtualDom(data, args, { schema, Comp });
});
};
@@ -713,7 +709,7 @@ export default function baseRenererFactory() {
return checkProps(props);
}
if (isJSExpression(props)) {
- props = parseExpression(props, self);
+ props = parseExpression(props, scope);
// 只有当变量解析出来为模型结构的时候才会继续解析
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
}
@@ -726,7 +722,7 @@ export default function baseRenererFactory() {
if (i18nProp) {
props = i18nProp;
} else {
- return parseI18n(props, self);
+ return parseI18n(props, scope);
}
}
@@ -768,10 +764,10 @@ export default function baseRenererFactory() {
);
}
if (Array.isArray(props)) {
- return checkProps(props.map((item, idx) => this.__parseProps(item, self, path ? `${path}.${idx}` : `${idx}`, info)));
+ return checkProps(props.map((item, idx) => this.__parseProps(item, scope, path ? `${path}.${idx}` : `${idx}`, info)));
}
if (typeof props === 'function') {
- return checkProps(props.bind(self));
+ return checkProps(props.bind(scope));
}
if (props && typeof props === 'object') {
if (props.$$typeof) return checkProps(props);
@@ -781,7 +777,7 @@ export default function baseRenererFactory() {
res[key] = val;
return;
}
- res[key] = this.__parseProps(val, self, path ? `${path}.${key}` : key, info);
+ res[key] = this.__parseProps(val, scope, path ? `${path}.${key}` : key, info);
});
return checkProps(res);
}
@@ -802,15 +798,7 @@ export default function baseRenererFactory() {
return this.__instanceMap[filedId];
}
- __initDebug = () => {
- this.__logger = Debug(`renderer:${this.__namespace || 'base'}`);
- };
-
- __debug = (msg = '') => {
- if (this.__logger) {
- this.__logger(`${this.__namespace}.${msg}`);
- }
- };
+ __debug = logger.log;
__renderContextProvider = (customProps?: object, children?: any) => {
customProps = customProps || {};
@@ -829,12 +817,13 @@ export default function baseRenererFactory() {
return createElement(AppContext.Consumer, {}, children);
};
- __getHocComp(Comp: any, schema: any) {
+ __getHocComp(Comp: any, schema: any, scope: any) {
this.componentHoc.forEach((ComponentConstruct: IComponentConstruct) => {
Comp = ComponentConstruct(Comp || Div, {
schema,
componentInfo: {},
baseRenderer: this,
+ scope,
});
});
@@ -843,8 +832,11 @@ export default function baseRenererFactory() {
__renderComp(Comp: any, ctxProps: object) {
const { __schema } = this.props;
- Comp = this.__getHocComp(Comp, __schema);
- const data = this.__parseProps(__schema?.props, this.self, '', {
+ const { __ctx } = this.props;
+ const scope: any = {};
+ scope.__proto__ = __ctx || this;
+ Comp = this.__getHocComp(Comp, __schema, scope);
+ const data = this.__parseProps(__schema?.props, scope, '', {
schema: __schema,
Comp,
componentInfo: {},
diff --git a/packages/renderer-core/src/types/index.ts b/packages/renderer-core/src/types/index.ts
index 49b8c895d..8bee549f9 100644
--- a/packages/renderer-core/src/types/index.ts
+++ b/packages/renderer-core/src/types/index.ts
@@ -141,7 +141,7 @@ export interface IRenderer {
__createLoopVirtualDom: (schema: any, self: any, parentInfo: IInfo, idx: number | string) => any;
__parseProps: (props: any, self: any, path: string, info: IInfo) => any;
__initDebug: () => void;
- __debug: (msg: string) => void;
+ __debug: (...args: any[]) => void;
__renderContextProvider: (customProps?: object, children?: any) => any;
__renderContextConsumer: (children: any) => any;
__renderContent: (children: any) => any;
diff --git a/packages/renderer-core/src/utils/common.ts b/packages/renderer-core/src/utils/common.ts
index 928ddbfd8..78bead5ac 100644
--- a/packages/renderer-core/src/utils/common.ts
+++ b/packages/renderer-core/src/utils/common.ts
@@ -202,8 +202,8 @@ export function getI18n(key: string, values = {}, locale = 'zh-CN', messages = {
* 判断当前组件是否能够设置ref
* @param {*} Comp 需要判断的组件
*/
-export function acceptsRef(Comp: any) {
- return Comp?.$$typeof === REACT_FORWARD_REF_TYPE || Comp?.prototype?.isReactComponent || Comp?.prototype?.setState;
+export function canAcceptsRef(Comp: any) {
+ return Comp?.$$typeof === REACT_FORWARD_REF_TYPE || Comp?.prototype?.isReactComponent || Comp?.prototype?.setState || Comp._forwardRef;
}
/**
diff --git a/packages/renderer-core/src/utils/logger.ts b/packages/renderer-core/src/utils/logger.ts
new file mode 100644
index 000000000..1feb9f6c8
--- /dev/null
+++ b/packages/renderer-core/src/utils/logger.ts
@@ -0,0 +1,2 @@
+import Logger from 'zen-logger';
+export default new Logger({ level: 'warn', bizName: 'renderer' });
\ No newline at end of file
diff --git a/packages/types/src/metadata.ts b/packages/types/src/metadata.ts
index 77476d758..c266755a1 100644
--- a/packages/types/src/metadata.ts
+++ b/packages/types/src/metadata.ts
@@ -29,6 +29,8 @@ export interface ComponentConfigure {
descriptor?: string;
nestingRule?: NestingRule;
+ isMinimalRenderUnit?: boolean;
+
rootSelector?: string;
// copy, move, remove | *
disableBehaviors?: string[] | string;
diff --git a/packages/utils/src/clone-enumerable-property.ts b/packages/utils/src/clone-enumerable-property.ts
new file mode 100644
index 000000000..414f8dccd
--- /dev/null
+++ b/packages/utils/src/clone-enumerable-property.ts
@@ -0,0 +1,22 @@
+const excludePropertyNames = [
+ '$$typeof',
+ 'render',
+ 'defaultProps',
+ 'props',
+ 'length',
+ 'prototype',
+ 'name',
+ 'caller',
+ 'callee',
+ 'arguments',
+];
+
+export function cloneEnumerableProperty(target: any, origin: any) {
+ const compExtraPropertyNames = Object.keys(origin).filter(d => !excludePropertyNames.includes(d));
+
+ compExtraPropertyNames.forEach((d: string) => {
+ (target as any)[d] = origin[d];
+ });
+
+ return target;
+}
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 891c1c54f..087591d99 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -23,3 +23,4 @@ export * from './app-helper';
export * from './misc';
export * from './schema';
export * from './node-helper';
+export * from './clone-enumerable-property';
diff --git a/packages/utils/src/is-react.ts b/packages/utils/src/is-react.ts
index 7261b6a07..02ef50fa6 100644
--- a/packages/utils/src/is-react.ts
+++ b/packages/utils/src/is-react.ts
@@ -1,4 +1,5 @@
import { ComponentClass, Component, FunctionComponent, ComponentType, createElement } from 'react';
+import { cloneEnumerableProperty } from './clone-enumerable-property';
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
@@ -20,11 +21,12 @@ export function isReactComponent(obj: any): obj is ComponentType {
}
export function wrapReactClass(view: FunctionComponent) {
- const ViewComponentClass = class extends Component {
+ let ViewComponentClass = class extends Component {
render() {
return createElement(view, this.props);
}
} as any;
+ ViewComponentClass = cloneEnumerableProperty(ViewComponentClass, view);
ViewComponentClass.displayName = view.displayName;
return ViewComponentClass;
}
diff --git a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts
index 5817a7b5d..953e85062 100644
--- a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts
+++ b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts
@@ -132,6 +132,7 @@ export interface OldPrototypeConfig {
componentName: string; // =>
docUrl?: string; // =>
defaultProps?: any; // => ?
+ isMinimalRenderUnit?: boolean; // => false
/**
* extra actions on the outline of current selected node
* by default we have: remove / clone
@@ -702,6 +703,9 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
devMode,
schema,
isTopFixed,
+
+ // render
+ isMinimalRenderUnit,
} = oldConfig;
let {
canResizing, // resizing
@@ -732,6 +736,7 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
isModal,
isFloating,
descriptor,
+ isMinimalRenderUnit,
};
if (canOperating === false) {