Skip to content

Commit da7f77e

Browse files
authored
feat: added thisRequiredInJSE API to control whether JSExpression expression access context must use this (alibaba#702)
1 parent 2022308 commit da7f77e

File tree

11 files changed

+154
-22
lines changed

11 files changed

+154
-22
lines changed

packages/designer/src/builtin-simulator/host.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
228228
return this.get('requestHandlersMap') || null;
229229
}
230230

231+
get thisRequiredInJSE(): any {
232+
return engineConfig.get('thisRequiredInJSE') ?? true;
233+
}
234+
231235
@computed get componentsAsset(): Asset | undefined {
232236
return this.get('componentsAsset');
233237
}

packages/editor-core/src/config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ const VALID_ENGINE_OPTIONS = {
133133
type: 'object',
134134
description: '数据源引擎的请求处理器映射',
135135
},
136+
thisRequiredInJSE: {
137+
type: 'boolean',
138+
description: 'JSExpression 是否只支持使用 this 来访问上下文变量',
139+
},
136140
};
137141
export interface EngineOptions {
138142
/**
@@ -248,6 +252,12 @@ export interface EngineOptions {
248252
* 数据源引擎的请求处理器映射
249253
*/
250254
requestHandlersMap?: RequestHandlersMap;
255+
256+
/**
257+
* @default true
258+
* JSExpression 是否只支持使用 this 来访问上下文变量,假如需要兼容原来的 'state.xxx',则设置为 false
259+
*/
260+
thisRequiredInJSE?: boolean;
251261
}
252262

253263
const getStrictModeValue = (engineOptions: EngineOptions, defaultValue: boolean): boolean => {

packages/rax-simulator-renderer/src/renderer-view.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ class Renderer extends Component<{
219219
onCompGetRef={(schema: any, ref: any) => {
220220
documentInstance.mountInstance(schema.id, ref);
221221
}}
222+
thisRequiredInJSE={host.thisRequiredInJSE}
222223
documentId={document.id}
223224
getNode={(id: string) => documentInstance.getNode(id) as any}
224225
rendererName="PageRenderer"

packages/react-simulator-renderer/src/renderer-view.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ class Renderer extends Component<{
189189
setSchemaChangedSymbol={this.setSchemaChangedSymbol}
190190
getNode={(id: string) => documentInstance.getNode(id) as Node}
191191
rendererName="PageRenderer"
192+
thisRequiredInJSE={host.thisRequiredInJSE}
192193
customCreateElement={(Component: any, props: any, children: any) => {
193194
const { __id, ...viewProps } = props;
194195
viewProps.componentId = __id;

packages/renderer-core/src/renderer/base.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getValue,
1313
parseData,
1414
parseExpression,
15+
parseThisRequiredExpression,
1516
parseI18n,
1617
isEmpty,
1718
isSchema,
@@ -83,11 +84,13 @@ export default function baseRendererFactory(): IBaseRenderComponent {
8384
getLocale: any;
8485
setLocale: any;
8586
styleElement: any;
87+
parseExpression: any;
8688
[key: string]: any;
8789

8890
constructor(props: IBaseRendererProps, context: IBaseRendererContext) {
8991
super(props, context);
9092
this.context = context;
93+
this.parseExpression = props?.thisRequiredInJSE ? parseThisRequiredExpression : parseExpression;
9194
this.__beforeInit(props);
9295
this.__init(props);
9396
this.__afterInit(props);
@@ -112,7 +115,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
112115

113116
if (func) {
114117
if (isJSExpression(func) || isJSFunction(func)) {
115-
const fn = parseExpression(func, this);
118+
const fn = props.thisRequiredInJSE ? parseThisRequiredExpression(func, this) : parseExpression(func, this);
116119
return fn(props, state);
117120
}
118121

@@ -193,7 +196,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
193196
if (fn) {
194197
// TODO, cache
195198
if (isJSExpression(fn) || isJSFunction(fn)) {
196-
fn = parseExpression(fn, this);
199+
fn = this.parseExpression(fn, this);
197200
}
198201
if (typeof fn !== 'function') {
199202
console.error(`生命周期${method}类型不符`, fn);
@@ -219,7 +222,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
219222
this.__customMethodsList = customMethodsList;
220223
forEach(__schema.methods, (val: any, key: string) => {
221224
if (isJSExpression(val) || isJSFunction(val)) {
222-
val = parseExpression(val, this);
225+
val = this.parseExpression(val, this);
223226
}
224227
if (typeof val !== 'function') {
225228
console.error(`自定义函数${key}类型不符`, val);
@@ -414,7 +417,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
414417
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
415418

416419
if (isJSExpression(schema)) {
417-
return parseExpression(schema, scope);
420+
return this.parseExpression(schema, scope);
418421
}
419422
if (isI18nData(schema)) {
420423
return parseI18n(schema, scope);
@@ -434,7 +437,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
434437
const _children = this.getSchemaChildren(schema);
435438
// 解析占位组件
436439
if (schema?.componentName === 'Fragment' && _children) {
437-
const tarChildren = isJSExpression(_children) ? parseExpression(_children, scope) : _children;
440+
const tarChildren = isJSExpression(_children) ? this.parseExpression(_children, scope) : _children;
438441
return this.__createVirtualDom(tarChildren, scope, parentInfo);
439442
}
440443

@@ -496,7 +499,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
496499
let scopeKey = '';
497500
// 判断组件是否需要生成scope,且只生成一次,挂在this.__compScopes上
498501
if (Comp.generateScope) {
499-
const key = parseExpression(schema.props?.key, scope);
502+
const key = this.parseExpression(schema.props?.key, scope);
500503
if (key) {
501504
// 如果组件自己设置key则使用组件自己的key
502505
scopeKey = key;
@@ -647,7 +650,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
647650

648651
_children.forEach((_child: any) => {
649652
const _childVirtualDom = this.__createVirtualDom(
650-
isJSExpression(_child) ? parseExpression(_child, scope) : _child,
653+
isJSExpression(_child) ? this.parseExpression(_child, scope) : _child,
651654
scope,
652655
{
653656
schema,
@@ -754,7 +757,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
754757
return checkProps(props);
755758
}
756759
if (isJSExpression(props)) {
757-
props = parseExpression(props, scope);
760+
props = this.parseExpression(props, scope);
758761
// 只有当变量解析出来为模型结构的时候才会继续解析
759762
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
760763
}

packages/renderer-core/src/renderer/renderer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export default function rendererFactory(): IRenderComponent {
6060
schema: {} as RootSchema,
6161
onCompGetRef: () => { },
6262
onCompGetCtx: () => { },
63+
thisRequiredInJSE: true,
6364
};
6465

6566
static findDOMNode = findDOMNode;

packages/renderer-core/src/types/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ export interface IRendererProps {
128128
faultComponent?: IGeneralComponent;
129129
/** 设备信息 */
130130
device?: string;
131+
/**
132+
* @default true
133+
* JSExpression 是否只支持使用 this 来访问上下文变量
134+
*/
135+
thisRequiredInJSE?: boolean;
131136
}
132137

133138
export interface IRendererState {
@@ -148,12 +153,13 @@ export interface IBaseRendererProps {
148153
__host?: BuiltinSimulatorHost;
149154
__container?: any;
150155
config?: Record<string, any>;
151-
designMode?: 'live' | 'design';
156+
designMode?: 'design';
152157
className?: string;
153158
style?: CSSProperties;
154159
id?: string | number;
155160
getSchemaChangedSymbol?: () => boolean;
156161
setSchemaChangedSymbol?: (symbol: boolean) => void;
162+
thisRequiredInJSE?: boolean;
157163
documentId?: string;
158164
getNode?: any;
159165
/**

packages/renderer-core/src/utils/common.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export function transformStringToFunction(str: string) {
242242
* @param self scope object
243243
* @returns funtion
244244
*/
245-
export function parseExpression(str: any, self: any) {
245+
export function parseExpression(str: any, self: any, thisRequired = false) {
246246
try {
247247
const contextArr = ['"use strict";', 'var __self = arguments[0];'];
248248
contextArr.push('return ');
@@ -259,14 +259,18 @@ export function parseExpression(str: any, self: any) {
259259
if (inSameDomain() && (window.parent as any).__newFunc) {
260260
return (window.parent as any).__newFunc(tarStr)(self);
261261
}
262-
const code = `with($scope || {}) { ${tarStr} }`;
262+
const code = `with(${thisRequired ? '{}' : '$scope || {}'}) { ${tarStr} }`;
263263
return new Function('$scope', code)(self);
264264
} catch (err) {
265-
logger.error('parseExpression.error', err, str, self);
265+
logger.error('parseExpression.error', err, str, self?.__self ?? self);
266266
return undefined;
267267
}
268268
}
269269

270+
export function parseThisRequiredExpression(str: any, self: any) {
271+
return parseExpression(str, self, true);
272+
}
273+
270274
/**
271275
* capitalize first letter
272276
* @param word string to be proccessed

packages/renderer-core/tests/renderer/__snapshots__/renderer.test.tsx.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,24 @@ exports[`JSExpression JSExpression props with loop 1`] = `
994994
</div>
995995
`;
996996

997+
exports[`JSExpression JSExpression props with loop, and thisRequiredInJSE is true 1`] = `
998+
<div
999+
className="lce-page"
1000+
style={Object {}}
1001+
>
1002+
<div
1003+
className="div-ut"
1004+
forwardRef={[Function]}
1005+
name1="1"
1006+
/>
1007+
<div
1008+
className="div-ut"
1009+
forwardRef={[Function]}
1010+
name1="2"
1011+
/>
1012+
</div>
1013+
`;
1014+
9971015
exports[`JSExpression JSFunction props 1`] = `
9981016
<div
9991017
className="lce-page"

packages/renderer-core/tests/renderer/renderer.test.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ describe('JSExpression', () => {
145145
]
146146
};
147147

148-
getComp(schema, components.Div).then(({ component, inst }) => {
148+
getComp(schema, components.Div, {
149+
thisRequiredInJSE: false,
150+
}).then(({ component, inst }) => {
149151
// expect(inst[0].props.visible).toBeTruthy();
150152
expect(inst.length).toEqual(2);
151153
[1, 2].forEach((i) => {
@@ -157,6 +159,50 @@ describe('JSExpression', () => {
157159
});
158160
});
159161

162+
it('JSExpression props with loop, and thisRequiredInJSE is true', (done) => {
163+
const schema = {
164+
componentName: 'Page',
165+
props: {},
166+
state: {
167+
isShowDialog: true,
168+
},
169+
children: [
170+
{
171+
componentName: "Div",
172+
loop: [
173+
{
174+
name: '1',
175+
},
176+
{
177+
name: '2'
178+
}
179+
],
180+
props: {
181+
className: "div-ut",
182+
name1: {
183+
type: 'JSExpression',
184+
value: 'this.item.name',
185+
},
186+
name2: {
187+
type: 'JSExpression',
188+
value: 'item.name',
189+
},
190+
}
191+
}
192+
]
193+
};
194+
195+
getComp(schema, components.Div).then(({ component, inst }) => {
196+
expect(inst.length).toEqual(2);
197+
[0, 1].forEach((i) => {
198+
expect(inst[i].props[`name1`]).toBe(i + 1 + '');
199+
expect(inst[i].props[`name2`]).toBe(undefined);
200+
})
201+
componentSnapshot = component;
202+
done();
203+
});
204+
});
205+
160206
// it('JSFunction props with loop', (done) => {
161207
// const schema = {
162208
// componentName: 'Page',

0 commit comments

Comments
 (0)