ESX是将HTML标签块转成JS表达式来创建DOM虚拟节点。ESX与JSX相比增加了命名空间、流程控制、迭代。通过使用命名空间可以快速的引用组件或者定义相关的操作。ESX 表达式只能出现在Web组件中,如果出现在其它代码中可能会有异常情况。
ESX语法是按照VUE模版的语法来实现的,在使用上相对于模版更加灵活。
基础写法
带有指定命名空间的写法,指定命名空间可以很清晰的知道意图,当然可以根据你的习惯来选择
package com;
import web.components.Component
class Person extends Component{
createNode(){
return <span>Hello, world!</span>
}
@Override
render(){
return <div xmlns:local="components"
xmlns:d="@directives"
xmlns:s="@slots"
xmlns:ui="web.ui">
<div d:if={true}>
{this.createNode()}
</div>
<div>
<ui:Button>This Is Button</ui:Button>
</div>
</div>
}
}
隐式命名空间的写法,与上面的功能一致
package com;
import web.components.Component
class Person extends Component{
createNode(){
return <span>Hello, world!</span>
}
@Override
render(){
return <div>
<div d:if={true}>
{this.createNode()}
</div>
<div>
<ui:Button>This Is Button</ui:Button>
</div>
</div>
}
}
命名空间
命名空间只能定义在根元素,使用命名空间可以明确定义每个元素或者属性的类型、功能、事件。在编译时也需要知道每个元素或者属性应该怎么编译,所以命名空间就显得尤为重要,同时也增加了代码的灵活性。当然也考虑到开发的复杂程度所以增加了常用的隐式命名空间。
定义命名空间
语法:xmlns:Name="Identifier"
xmlns:是一个固定前缀,用来标识是在定义命名空间,而不是定义元素属性
Name: 是定义命名空间的引用名
Identifier: 可以是一个命名空间、类或者是内置的标识符
<div xmlns:d="@directives" />
<d:if condition={true}>
<div>此段代码引用了'd'命名空间,这个命名空间被标识为了一个流程控制指令</div>
</d:if>
</div>
{/*以上定义了一个名为'd'的命名空间,其值为'@directives'*/}
内置标识符(Identifier
)
内置标识符主要是在编译阶段区分如何编译代码。
指令:@directives
事件:@events
原生事件:@natives
插槽:@slots
双向绑定:@binding
隐式声明的命名空间
单字声明
xmlns:e="@events"
xmlns:s="@slots"
xmlns:d="@directives"
xmlns:b="@binding"
xmlns:n="@natives"
语义声明xmlns:on="@events"
xmlns:slot="@slots"
xmlns:bind="@binding"
xmlns:native="@natives"
xmlns:directive="@directives"
指令
流程控制
@directives:if,elseif,else
根据指定的条件创建元素的虚拟节点
包裹元素写法
if,elseif
condition 属性是一个表达式必须指定
<d:if condition={this.one}>
<div>1</div>
</d:if>
<d:elseif condition={this.two}>
<div>2</div>
</d:elseif>
<d:else>
<div>...</div>
</d:else>
元素属性上的写法
if,elseif 属性值的表达式必须指定
<div d:if={this.one}>1</div>
<div d:elseif={this.two}>2</div>
<div d:else>...</div>
迭代
@directives:for,each
根据指定的可迭代数据循环创建元素的虚拟节点。
for 和 each 的区别是 for 可用于任何可迭代对象而 each 只能用于数组。性能上each会优于for
包裹元素写法
name: 表达式,必须
item: 迭代的值,可选
key: 迭代的键值,通常是一个数组的索引下标。 可选
<d:for name="this.list" item="item" key="key">
<div>{item}</div>
</d:for>
<d:each name="[1,2,3]" item="item" key="key">
<div>{item}</div>
</d:for>
元素属性上的写法
for,each:
(item, key) in expression
item: 迭代值
key: 迭代健值
expression: 一个实现迭代的表达式
<div d:for="(item, key) in this.list">{item}</div>
<div d:each="(item, key) in [1,2,3]">{item}</div>
显示
@directives:show
设置一个DOM元素的display是否为可见状态
包裹元素写法
<d:show condition={true}>
<div>show</div>
</d:show>
元素属性上的写法
<div d:show={true}>show</div>
自定义
@directives:custom
实现自定义指令应该先定义指令组件,指令组件需要继承 web.components.Directive 类。
package com;
import web.components.Directive;
class MyDirective implements Directive {
created(el){
el.focus();
}
}
包裹元素写法
name 是指令组件类名
value 是传递给指令组件的属性值
<d:custom name={MyDirective} value={true}>
<div>MyDirective 1</div>
<div>MyDirective 2</div>
</d:custom>
元素属性上的写法
以对象表达式的方式传递给自定义指令
<div d:custom ={{name:MyDirective,value:true}}>MyDirective</div>
事件
@events:on
on:eventName=function
eventName 元素或者组件本身支持的事件名
事件指令只能定义在元素本身
<div on:click={()=>console.log('click')}>show</div>
双向绑定
@binding:bind
bind:eventName=expression
双向绑定的事件必须在绑定的组件中实现了触发对应的事件名。通常在表单的组件中实现的双向绑定事件名为 value , 因为在表单的组件中会把value的属性名转换为底层特有的专用名
package com;
import web.components.Component
class Person extends Component{
@Reactive
private count:number = 1
onClick(){
this.count ++;
this.emit('input', this.count)
}
@Override
render(){
return <div xmlns:ui="web.ui">
<ui:Button on:click={onClick}>Click {this.count}</ui:Button>
</div>
}
}
<Person bind:input={this.count}>binding</Person>
{/* 当 Person 组件点击时会同步 count 的值到当前组件中 */}
插槽
@slots:slot
在组件中定义插槽
package com;
import web.components.Component
class Person extends Component{
private data = {
title:'title is Person'
};
@Override
render(){
return <div xmlns:slot="@slots">
<div class='head'>
<slot:head props={this.data}>
<h3>default head</h3>
</slot:head>
</div>
<div><slot:default /></div>
</div>
}
}
向插槽传递内容
package com;
import web.components.Component;
import com.Person;
class Child extends Component{
@Override
render(){
return <div xmlns:slot="@slots">
<Person>
<slot:head props='props'>
<div>{props.title}</div>
</slot:head>
<div>this is default content</div>
</Person>
<div>child</div>
</div>
}
}