语法约束
由于 Rax 转小程序链路是通过 AST 语法转换的方式将使用 Rax JSX 语法的项目转换到小程序语法的项目,所以受限于实现,我们对开发者使用的语法进行了部分限制(随着转译器的能力增强,限制会越来越少,本篇文档也将持续更新)。
使用限制
静态资源
由于无法识别在 json
文件中配置的静态资源,所以开发者需要指定静态资源的目录,build.json
中的具体配置如下:
{
"plugins": [
[
"rax-plugin-app",
{
"targets": [
"miniapp"
],
"miniapp": {
"constantDir": [
"src/constant"
]
}
}
]
]
}
Context
支持 Context 使用,但不支持以下能力:
- Consumer API,例如以下写法:
<MyContext.Consumer> {value => /* 基于 context 值进行渲染*/} </MyContext.Consumer>
- 向上追溯最近的 Provider。当组件订阅了 Context 对象时,其会从最先渲染的 Provider 中读取到 Provider 的 value 而不是向上往父组件找离自己最近的 Provider。
- class.contextType,例如以下写法:
定义 contextType 请使用以下写法:class MyClass extends Rax.Component { } MyClass.contextType = MyContext;
class MyClass extends Rax.Component { static contextType = MyContext }
JSX 语法
基础语法
引号
由于小程序中变量绑定需要通过 "{{}}"
,双引号来包裹,所以在 JSX 中尽量不要使用双引号,以至于产生冲突。
组件导出方式
为了方便编译器找到导出的组件,我们对导出组件做了如下限制:
- 默认导出的必须是组件,即
export default MyComponent;
- 当导出的组件需要被某个函数处理的时候,函数的第一个参数必须是组件,即
export default handleComponent(MyComponent);
扩展运算符
支持给组件属性的值中使用扩展运算符:
<View xxx = {...object}></View>
不支持直接在组件上使用扩展运算符:
<View {...props}></View>
模板字符串
JSX 上暂时不能使用模板字符串
Render Props
组件的 props
无法传递 JSX 模板,如以下用法:
<CustomComponent renderItem={<View>item</View>} />
Ref
目前支持通过 useRef
和字符串的方式创建 Ref
。
支付宝小程序需要开启 component2。
组件文件
一个组件文件只支持导出一个组件,例如不支持以下用法:
export function Title() {
return <View>Title</View>;
}
export function Content() {
return <View>Content</View>;
}
组件渲染
一般情况下,目前一个组件文件中只支持定义并使用一个组件,例如不支持以下用法:
function Title() {
return <View>Title</View>;
}
export default function Home() {
return <Title />;
}
但是为了满足部分这方面的需求,转译器支持在 Class Component
中如下用法:
export default class Home extends Component {
renderTitle(title) {
return <View>{title}</View>;
}
render() {
return (
<View>{this.renderTitle('Title')}</View>
)
}
}
条件渲染
由于条件渲染存在较大的灵活性,所以目前支持以下条件渲染的方式:
通过变量赋值的形式:
export default class Home extends Component {
state = {
status: false
};
render() {
const { status } = this.state;
let result;
if (status) {
result = <View>status 1</View>
} else {
result = <View>status 2</View>
}
return (
<View>
{result}
</View>
);
}
}
并且支持使用 else if
,但是需要注意的是,在写 if
表达式的时候,需要在后面标准的加上 {}
,代表代码块。
通过三元表达式:
export default class Home extends Component {
state = {
status: false,
};
render() {
const { status } = this.state;
return (
<View>{status <View>status 1</View> : <View>status 2</View>}</View>
);
}
}
并且支持使用多层三元表达式。
通过 JSX+ 语法中的 x-if
:
export default class Home extends Component {
state = {
status: false,
};
render() {
const { status } = this.state;
return (
<View>
<View x-if={status}>
<View>status 1</View>
</View>
<View x-else>status 2</View>
</View>
);
}
}
通过逻辑表达式:
export default class Home extends Component {
state = {
status: false,
};
render() {
const { status } = this.state;
return (
<View>{status && <View>status 1</View>}</View>
);
}
}
循环渲染
循环渲染最大的限制在于你必须保证你循环遍历的变量的类型是数组。
其它限制循环渲染写法的原因产生于两个方面:
- 循环自定义组件时,需要去派发传递给自定义组件的属性(即
props
)
- 循环渲染中会产生新的作用域与作用域中的变量
基于以上原因循环渲染只支持以下用法:
使用 JSX 中常用到的 map
,简单用法例如:
export default class Home extends Component {
state = {
list: [],
};
render() {
const { list } = this.state;
return (
<View>
{list.map((item, index) => {
return <View>{item.title}</View>;
})}
</View>
);
}
}
但是,在某些复杂场景下,一些用法就不支持了:
需要在 JSX 中的复杂表达式里使用到循环产生的临时变量,如以下写法是不支持的:
export default class Home extends Component {
state = {
list: [],
};
render() {
const { list } = this.state;
return (
<View>
{list.map((item, index) => {
return (
<View
style={{
marginRight: index < 2 18 : 0,
}}
></View>
);
})}
</View>
);
}
}
如果有这样的需求,建议使用以下写法来避免转移失败,在 map 函数体内将复杂表达式赋值给一个变量,然后在 jsx 中直接使用变量的形式:
export default class Home extends Component {
state = {
list: [],
};
render() {
const { list } = this.state;
return (
<View>
{list.map((item, index) => {
const marginRight = index < 2 18 : 0;
return (
<View
style={{
marginRight,
}}
></View>
);
})}
</View>
);
}
}
多层循环的 JSX 中使用到临时变量,如以下用法是不支持的:
export default class Home extends Component {
state = {
list: [['a'], ['b']],
};
render() {
const { list } = this.state;
return (
<View>
{list.map(item => {
return item.map(item2 => {
return <View>{item2}</View>;
});
})}
</View>
);
}
}
使用 JSX+ 中的 x-for
:
在这种用法中,开发者得到的便利不只是可以少写一些代码,更重要的是,这种用法是支持多层循环渲染的,比如,你可以这样写:
export default class Home extends Component {
state = {
list: [['a'], ['b']],
};
render() {
const { list } = this.state;
return (
<View>
<View x-for={item in list}>
<View x-for={item2 in item}>{item2}</View>
</View>
</View>
);
}
}
事件绑定
事件名
所有的事件名必须是 on
开头。
在循环中使用
在循环中绑定事件的时候尽量在 JSX 中直接使用循环中产生的临时变量,例如:
正确做法:
<View>
{
list.map(item => {
const url = item.url;
return <View onClick={() => handleClick(url)}></View>
});
}
</View>
错误做法:
<View>
{
list.map(item => {
const url = item.url;
const handleClick = () => {
console.log(url);
}
return <View onClick={handleClick}></View>
});
}
</View>