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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
name: RAX_CHUNK_NAME.MethodsBegin, name: RAX_CHUNK_NAME.MethodsBegin,
content: ` content: `
_defineMethods() { _defineMethods() {
return ({ const __$$methods = ({
`, `,
linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod], linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod],
}); });
@ -55,6 +55,17 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
name: RAX_CHUNK_NAME.MethodsEnd, name: RAX_CHUNK_NAME.MethodsEnd,
content: ` 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], linkAfter: [RAX_CHUNK_NAME.MethodsBegin, RAX_CHUNK_NAME.MethodsContent],

View File

@ -1,10 +1,12 @@
// : "__$$" 访
// rax
import { createElement, Component } from 'rax'; import { createElement, Component } from 'rax';
import Page from 'rax-view'; import Page from 'rax-view';
import Text from 'rax-text'; 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'; import __$$projectUtils from '../../utils';
@ -16,7 +18,7 @@ class Home$$Page extends Component {
_context = this._createContext(); _context = this._createContext();
_dataSourceList = this._defineDataSourceList(); _dataSourceList = this._defineDataSourceList();
_dataSourceEngine = createDataSourceEngine(this._dataSourceList, this._context); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceList, this._context);
_utils = this._defineUtils(); _utils = this._defineUtils();
@ -62,6 +64,7 @@ class Home$$Page extends Component {
get props() { get props() {
return self.props; return self.props;
}, },
...this._methods,
}; };
return context; return context;
@ -86,7 +89,18 @@ class Home$$Page extends Component {
} }
_defineMethods() { _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-text": "^1.0.0",
"rax-image": "^1.0.0", "rax-image": "^1.0.0",
"moment": "*", "moment": "*",
"lodash": "*" "lodash": "*",
"universal-toast": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
"build-plugin-rax-app": "^5.0.0", "build-plugin-rax-app": "^5.0.0",

View File

@ -1,3 +1,5 @@
// : "__$$" 访
// rax
import { createElement, Component } from 'rax'; import { createElement, Component } from 'rax';
import View from 'rax-view'; import View from 'rax-view';
@ -6,7 +8,7 @@ import Text from 'rax-text';
import Image from 'rax-image'; 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'; import __$$projectUtils from '../../utils';
@ -30,9 +32,11 @@ class Home$$Page extends Component {
}; };
_methods = this._defineMethods(); _methods = this._defineMethods();
_context = this._createContext(); _context = this._createContext();
_dataSourceList = this._defineDataSourceList(); _dataSourceList = this._defineDataSourceList();
_dataSourceEngine = createDataSourceEngine(this._dataSourceList, this._context); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceList, this._context);
_utils = this._defineUtils(); _utils = this._defineUtils();
@ -57,7 +61,7 @@ class Home$$Page extends Component {
source={{ uri: __$$eval(() => __$$context.state.user.avatar) }} source={{ uri: __$$eval(() => __$$context.state.user.avatar) }}
style={{ width: '32px', height: '32px' }} style={{ width: '32px', height: '32px' }}
/> />
<View> <View onClick={__$$eval(() => __$$context.hello)}>
<Text>{__$$eval(() => __$$context.state.user.name)}</Text> <Text>{__$$eval(() => __$$context.state.user.name)}</Text>
<Text>{__$$eval(() => __$$context.state.user.age)}</Text> <Text>{__$$eval(() => __$$context.state.user.age)}</Text>
</View> </View>
@ -67,7 +71,15 @@ class Home$$Page extends Component {
<Text>=== Orders: ===</Text> <Text>=== Orders: ===</Text>
</View> </View>
{__$$evalArray(() => __$$context.state.orders).map((item, index) => ( {__$$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> <View>
<Image source={{ uri: __$$eval(() => item.coverUrl) }} style={{ width: '80px', height: '60px' }} /> <Image source={{ uri: __$$eval(() => item.coverUrl) }} style={{ width: '80px', height: '60px' }} />
</View> </View>
@ -77,6 +89,11 @@ class Home$$Page extends Component {
</View> </View>
</View> </View>
))} ))}
<View>
<Text>操作提示</Text>
<Text>1. 点击会员名可以弹出 Toast "Hello xxx!"</Text>
<Text>2. 点击订单会记录点击的订单信息并弹出 Toast 提示</Text>
</View>
</View> </View>
); );
} }
@ -109,6 +126,7 @@ class Home$$Page extends Component {
get props() { get props() {
return self.props; return self.props;
}, },
...this._methods,
}; };
return context; return context;
@ -141,11 +159,23 @@ class Home$$Page extends Component {
} }
_defineMethods() { _defineMethods() {
return { const __$$methods = {
hello: function hello() { 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 clone from 'lodash/clone';
import Toast from 'universal-toast';
const formatPrice = function formatPrice(price, unit) { const formatPrice = function formatPrice(price, unit) {
return Number(price).toFixed(2) + 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 { export default {
formatPrice, formatPrice,
recordEvent,
moment, moment,
clone, clone,
Toast,
}; };

View File

@ -55,7 +55,7 @@
methods: { methods: {
hello: { hello: {
type: 'JSExpression', 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: { dataSource: {
@ -132,6 +132,12 @@
}, },
{ {
componentName: 'View', componentName: 'View',
props: {
onClick: {
type: 'JSFunction',
value: 'this.hello',
},
},
children: [ children: [
{ {
componentName: 'Text', componentName: 'Text',
@ -172,6 +178,10 @@
}, },
props: { props: {
style: { flexDirection: 'row' }, style: { flexDirection: 'row' },
onClick: {
type: 'JSFunction',
value: 'function(){ this.utils.recordEvent(`CLICK_ORDER`, this.item.title) }',
},
}, },
children: [ 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: [ utils: [
// 可以直接定义一个函数
{ {
name: 'formatPrice', name: 'formatPrice',
type: 'function', type: 'function',
@ -227,6 +258,16 @@
value: 'function formatPrice(price, unit) { return Number(price).toFixed(2) + unit; }', 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', name: 'moment',
type: 'npm', type: 'npm',
@ -236,6 +277,7 @@
exportName: 'moment', exportName: 'moment',
}, },
}, },
// 可以引入子目录(下例等价于 `import clone from 'lodash/clone';`)
{ {
name: 'clone', name: 'clone',
type: 'npm', type: 'npm',
@ -247,6 +289,16 @@
main: '/clone', main: '/clone',
}, },
}, },
// 支持 TNPM
{
name: 'Toast',
type: 'tnpm',
content: {
package: 'universal-toast',
version: '^1.2.0',
exportName: 'Toast', // TODO: 这个 exportName 是否可以省略?省略后默认是上一层的 name
},
},
], ],
config: { config: {
sdkVersion: '1.0.3', sdkVersion: '1.0.3',