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>
    }
}