test: 💍 完善 Rax 出码的示例, 解决 methods 的上下文绑定的问题

This commit is contained in:
牧毅 2020-08-13 11:22:09 +08:00
parent a1a3b68287
commit 6cd07524b6
9 changed files with 139 additions and 15 deletions

View File

@ -19,7 +19,11 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `import { createElement, Component } from 'rax';`,
content: `
// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。
// 例外rax 框架的导出名和各种组件名除外。
import { createElement, Component } from 'rax';
`,
linkAfter: [],
});

View File

@ -67,6 +67,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
get props() {
return self.props;
},
...this._methods,
};
return context;

View File

@ -32,7 +32,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `
import { createDataSourceEngine } from '@ali/lowcode-datasource-engine';
import { create as __$$createDataSourceEngine } from '@ali/lowcode-datasource-engine';
`,
linkAfter: [],
});
@ -43,7 +43,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `
_dataSourceList = this._defineDataSourceList();
_dataSourceEngine = createDataSourceEngine(this._dataSourceList, this._context);`,
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceList, this._context);`,
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
});

View File

@ -44,7 +44,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
name: RAX_CHUNK_NAME.MethodsBegin,
content: `
_defineMethods() {
return ({
const __$$methods = ({
`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod],
});
@ -55,6 +55,17 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
name: RAX_CHUNK_NAME.MethodsEnd,
content: `
});
// 为所有的方法绑定上下文
Object.entries(__$$methods).forEach(([methodName, method]) => {
if (typeof method === 'function') {
__$$methods[methodName] = (...args) => {
return method.apply(this._context, args);
}
}
});
return __$$methods;
}
`,
linkAfter: [RAX_CHUNK_NAME.MethodsBegin, RAX_CHUNK_NAME.MethodsContent],

View File

@ -1,10 +1,12 @@
// : "__$$" 访
// rax
import { createElement, Component } from 'rax';
import Page from 'rax-view';
import Text from 'rax-text';
import { createDataSourceEngine } from '@ali/lowcode-datasource-engine';
import { create as __$$createDataSourceEngine } from '@ali/lowcode-datasource-engine';
import __$$projectUtils from '../../utils';
@ -16,7 +18,7 @@ class Home$$Page extends Component {
_context = this._createContext();
_dataSourceList = this._defineDataSourceList();
_dataSourceEngine = createDataSourceEngine(this._dataSourceList, this._context);
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceList, this._context);
_utils = this._defineUtils();
@ -62,6 +64,7 @@ class Home$$Page extends Component {
get props() {
return self.props;
},
...this._methods,
};
return context;
@ -86,7 +89,18 @@ class Home$$Page extends Component {
}
_defineMethods() {
return {};
const __$$methods = {};
//
Object.entries(__$$methods).forEach(([methodName, method]) => {
if (typeof method === 'function') {
__$$methods[methodName] = (...args) => {
return method.apply(this._context, args);
};
}
});
return __$$methods;
}
}

View File

@ -17,7 +17,8 @@
"rax-text": "^1.0.0",
"rax-image": "^1.0.0",
"moment": "*",
"lodash": "*"
"lodash": "*",
"universal-toast": "^1.2.0"
},
"devDependencies": {
"build-plugin-rax-app": "^5.0.0",

View File

@ -1,3 +1,5 @@
// : "__$$" 访
// rax
import { createElement, Component } from 'rax';
import View from 'rax-view';
@ -6,7 +8,7 @@ import Text from 'rax-text';
import Image from 'rax-image';
import { createDataSourceEngine } from '@ali/lowcode-datasource-engine';
import { create as __$$createDataSourceEngine } from '@ali/lowcode-datasource-engine';
import __$$projectUtils from '../../utils';
@ -30,9 +32,11 @@ class Home$$Page extends Component {
};
_methods = this._defineMethods();
_context = this._createContext();
_dataSourceList = this._defineDataSourceList();
_dataSourceEngine = createDataSourceEngine(this._dataSourceList, this._context);
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceList, this._context);
_utils = this._defineUtils();
@ -57,7 +61,7 @@ class Home$$Page extends Component {
source={{ uri: __$$eval(() => __$$context.state.user.avatar) }}
style={{ width: '32px', height: '32px' }}
/>
<View>
<View onClick={__$$eval(() => __$$context.hello)}>
<Text>{__$$eval(() => __$$context.state.user.name)}</Text>
<Text>{__$$eval(() => __$$context.state.user.age)}</Text>
</View>
@ -67,7 +71,15 @@ class Home$$Page extends Component {
<Text>=== Orders: ===</Text>
</View>
{__$$evalArray(() => __$$context.state.orders).map((item, index) => (
<View style={{ flexDirection: 'row' }}>
<View
style={{ flexDirection: 'row' }}
onClick={__$$eval(
() =>
function () {
__$$context.utils.recordEvent(`CLICK_ORDER`, item.title);
},
)}
>
<View>
<Image source={{ uri: __$$eval(() => item.coverUrl) }} style={{ width: '80px', height: '60px' }} />
</View>
@ -77,6 +89,11 @@ class Home$$Page extends Component {
</View>
</View>
))}
<View>
<Text>操作提示</Text>
<Text>1. 点击会员名可以弹出 Toast "Hello xxx!"</Text>
<Text>2. 点击订单会记录点击的订单信息并弹出 Toast 提示</Text>
</View>
</View>
);
}
@ -109,6 +126,7 @@ class Home$$Page extends Component {
get props() {
return self.props;
},
...this._methods,
};
return context;
@ -141,11 +159,23 @@ class Home$$Page extends Component {
}
_defineMethods() {
return {
const __$$methods = {
hello: function hello() {
console.log('Hello world!');
this.utils.Toast.show(`Hello ${this.state.user.name}!`);
console.log(`Hello ${this.state.user.name}!`);
},
};
//
Object.entries(__$$methods).forEach(([methodName, method]) => {
if (typeof method === 'function') {
__$$methods[methodName] = (...args) => {
return method.apply(this._context, args);
};
}
});
return __$$methods;
}
}

View File

@ -2,14 +2,25 @@ import moment from 'moment';
import clone from 'lodash/clone';
import Toast from 'universal-toast';
const formatPrice = function formatPrice(price, unit) {
return Number(price).toFixed(2) + unit;
};
const recordEvent = function recordEvent(eventName, eventDetail) {
this.utils.Toast.show(`[EVENT]: ${eventName} ${eventDetail}`);
console.log(`[EVENT]: ${eventName} (detail: %o) (user: %o)`, eventDetail, this.state.user);
};
export default {
formatPrice,
recordEvent,
moment,
clone,
Toast,
};

View File

@ -55,7 +55,7 @@
methods: {
hello: {
type: 'JSExpression',
value: 'function hello(){ console.log("Hello world!"); }',
value: 'function hello(){\n this.utils.Toast.show(`Hello ${this.state.user.name}!`);\n console.log(`Hello ${this.state.user.name}!`); }',
},
},
dataSource: {
@ -132,6 +132,12 @@
},
{
componentName: 'View',
props: {
onClick: {
type: 'JSFunction',
value: 'this.hello',
},
},
children: [
{
componentName: 'Text',
@ -172,6 +178,10 @@
},
props: {
style: { flexDirection: 'row' },
onClick: {
type: 'JSFunction',
value: 'function(){ this.utils.recordEvent(`CLICK_ORDER`, this.item.title) }',
},
},
children: [
{
@ -215,10 +225,31 @@
},
],
},
{
componentName: 'View',
children: [
{
componentName: 'Text',
props: {},
children: '操作提示:',
},
{
componentName: 'Text',
props: {},
children: '1. 点击会员名,可以弹出 Toast "Hello xxx!"',
},
{
componentName: 'Text',
props: {},
children: '2. 点击订单,会记录点击的订单信息,并弹出 Toast 提示',
},
],
},
],
},
],
utils: [
// 可以直接定义一个函数
{
name: 'formatPrice',
type: 'function',
@ -227,6 +258,16 @@
value: 'function formatPrice(price, unit) { return Number(price).toFixed(2) + unit; }',
},
},
// 在 utils 里面也可以用 this 访问当前上下文:
{
name: 'recordEvent',
type: 'function',
content: {
type: 'JSFunction',
value: 'function recordEvent(eventName, eventDetail) { \n this.utils.Toast.show(`[EVENT]: ${eventName} ${eventDetail}`);\n console.log(`[EVENT]: ${eventName} (detail: %o) (user: %o)`, eventDetail, this.state.user); }',
},
},
// 也可以直接从 npm 包引入 (下例等价于 `import moment from 'moment';`)
{
name: 'moment',
type: 'npm',
@ -236,6 +277,7 @@
exportName: 'moment',
},
},
// 可以引入子目录(下例等价于 `import clone from 'lodash/clone';`)
{
name: 'clone',
type: 'npm',
@ -247,6 +289,16 @@
main: '/clone',
},
},
// 支持 TNPM
{
name: 'Toast',
type: 'tnpm',
content: {
package: 'universal-toast',
version: '^1.2.0',
exportName: 'Toast', // TODO: 这个 exportName 是否可以省略?省略后默认是上一层的 name
},
},
],
config: {
sdkVersion: '1.0.3',