标签
在React开发中,NornJ提供了一种可扩展的特殊组件语法,称为标签。这种语法最常见的使用场景就是流程控制语句:
const Test = props => (
<if condition={props.isTest}>
<i>success</i>
<else>
<i>fail</i>
</else>
</if>
);
上例中的<if>、<else>等都是标签语法。
与React组件的区别
简单地说,NornJ标签可以实现以下几种普通React组件无法实现的功能:
- 延迟渲染子节点
- 生成子节点可用的新变量
下面我们用实例分别解释下这些特性。
延迟渲染子节点
从上面<if>的例子我们不难想到,其实用React的组件语法不是也是可以实现么?确实可以实现,比如react-if项目:
const Foo = ({ data }) => (
<div>
<If condition={false}>
<Then>{() =>
renderData(data)
}</Then>
<Else>
Nothing to see here
</Else>
</If>
</div>
);
但是可以看出,react-if需要一个额外的<Then>标签;而且文档中也注明了,如果不在<Then>或<Else>中写一个返回子节点的函数是会存在性能消耗的。因为在并不确定condition的值之前,所有的分支节点都没必要进行提前渲染。
然而NornJ的<if>标签则不存在上述问题,因为它的本质并不是React组件而是一个模板函数,由配套的babel插件进行了转换:
const test = props => (
nj`
<#if condition=${props.isTest}>
#${() => <i>success</i>}
<#else>
#${() => <i>fail</i>}
</#else>
</#if>
`()
);
从上面可以看出,<i>success</i>等其实是包在函数内并没立即执行的。
另外,这并不是
NornJ标签最终的运行时代码,NornJ配套的babel插件还会做进一步的模板预编译以提高性能。
生成子节点可用的新变量
例如<each>循环:
const Test = props => (
<div>
<each of={[1, 2, 3]}>
<i>{item}</i>
</each>
</div>
);
上例中使用NornJ的each标签实现了循环数组[1, 2, 3],然后在子节点中使用新生成的item变量渲染循环中每一项的值。而常规JSX写法则必须使用函数才能实现:
const Test = props => (
<div>
{[1, 2, 3].map(item => <i>{item}</i>}
</div>
);
上述虽然是官方常规写法,但是标签中插入的额外花括号、函数体等,可能或多或少会影响一点代码整体的可读性
。
下面是NornJ已有内置的标签:
if
示例:
const Test = props => (
<div>
This is a if tag demo.
<if condition={props.type}>
test if tag
<span>test1</span>
</if>
</div>
);
如上,如果if标签的condition参数计算结果为true,则会渲染if标签内的子节点;如为false则不会渲染if标签内的任何东西。
- if标签的参数列表:
| 参数名称 | 类型 | 作用 |
|---|---|---|
| condition | Boolean | if标签子节点的渲染条件 |
if标签还包含else、elseif等子标签。
else
示例:
const Test = props => (
<div>
This is a if tag demo.
<if condition={props.type}>
test if tag
<span>test1</span>
<else>
<span>test2</span>
</else>
</if>
</div>
);
上例中如果if标签的condition参数值为false,则会渲染else标签内的子节点;否则会渲染if标签内除了else标签外的其他内容:
ReactDOM.render(<Test type={false} />, document.body);
/* 以上渲染内容为:
<div>
This is a if tag demo.
<span>test2</span>
</div>
*/
elseif
elseif标签可以实现多分支流程:
const Test = props => (
<div>
<if condition={props.num > 100}>
100
<elseif condition={props.num > 50}>
50
</elseif>
<elseif condition={props.num > 20}>
20
</elseif>
<else>
0
</else>
</if>
<div>
);
ReactDOM.render(<Test num={30} />, document.body);
/* 以上渲染内容为:
<div>20</div>
*/
- elseif标签的参数列表:
| 参数名称 | 类型 | 作用 |
|---|---|---|
| condition | Boolean | elseif标签子节点的渲染条件 |
each
each标签可以实现循环:
<each of={numbers}> //要循环的数组
<i>num: {item}</i> //item表示使用数组项
<i>no: {index}</i> //index表示使用数组项索引值
</each>
在循环中内嵌if标签:
<each of={numbers}>
<if condition={first}>show first<br/></if> //first表示数组第一项
<if condition={last}>show last</if> //last表示数组最后一项
</each>
如要循环的数组为空,则可以渲染empty标签的内容:
<each of={numbers}>
<span>test {item.no}</span>
<empty>
<span>no data</span>
</empty>
</each>
each标签的参数列表:
| 参数名称 | 类型 | 作用 |
|---|---|---|
| of | 数组 | 要循环的数组 |
| item | String | 循环中生成的每项变量名,可以改变 |
| index | String | 循环中生成的每项索引值变量名,可以改变 |
| first | String | 循环中生成的第一项变量名,可以改变 |
| last | String | 循环中生成的最后一项变量名,可以改变 |
for
for标签是each标签的别名,用法是完全一样的:
<for of={numbers}>
<span>test {item.no}</span>
<empty>
<span>no data</span>
</empty>
</for>
switch
switch标签也可以实现多分支流程:
const Test = props => (
<div>
<switch value={props.num}>
<case value={50}>
50
</case>
<case value={30}>
30
</case>
<default>
0
</default>
</switch>
<div>
);
ReactDOM.render(<Test num={30} />, document.body);
/* 以上渲染内容为:
<div>30</div>
*/
switch和case标签的参数列表:
| 参数名称 | 类型 | 作用 |
|---|---|---|
| value | Any | 在switch标签的value参数传入要判断值; 然后其会和case标签中的value值进行 ===判断;所有case都不匹配时则渲染default标签的子节点 |
with
with标签主要用于在JSX中创建新的变量:
<each of={[1, 2, 3]}>
<with num={item} i={index}>
<span>test-{num}-{i}</span>
</with>
</each>
MobxObserver
开发新的标签
NornJ的标签都是支持可扩展的,也就是说与React组件一样可以自行封装各种新功能。例如实现一个customIf标签:
<customIf condition={foo}>
test if
<else>
test else
</else>
</customIf>
每个标签都是一个函数,使用nj.registerExtension方法注册:
nj.registerExtension('customIf', options => {
const { props, children } = options;
if (props.condition) {
return children(); //输出标签的子节点,即"test if"
}
else {
return props['else']; //输出else标签的子节点,即"test else"
}
});
- 可以一次定义多个全局标签:
nj.registerExtension({
customIf: options => {...},
customSwitch: options => {...}
});
标签扩展函数的options参数
| 参数名称 | 类型 | 作用 |
|---|---|---|
| children | Function | 返回需要渲染的标签子节点 |
| props | Object | 当前标签的各行内属性值(即<tag a=1 b=2>中的a和b) |