EcmaScript 6,TypeScript 和 JSX
EcmaScript 6
Tabris.js 2支持所有ES5和大多数ES6 / ES7(又名ES2015 / ES2016)特性,并且无需像Babel这样的编译器。包括:
- Arrow functions
- Classes
- const
- Default parameters (除了iOS 9)
- Destructuring assignment
- Exponentiation operator(除了iOS 9)
- for…of
- Generators (除了9)
- Iterators
- let
- Map
- Methods
- Object property shorthands
- Promise
- Reflect (除了iOS 9)
- Rest parameters (除了iOS 9)
- Proxy (除了iOS 9)
- set and get literals
- Set
- Spread operator
- Symbol
- Template literals
- Typed Arrays and ArrayBuffer
- WeakMap
- WeakSet
不支持的是(不使用编译器时):
- async/await: 使用
then
替代。 - import/export: 使用
require
方法和exports
对象替代。
TypeScript
Tabris.js 2针对TypeScript 2进行了优化。TypeScript是一个类型安全的JavaScript/EcmaScript超集,并且还支持ES6模块语法 (import
和 export
语句) 和 async
/await
。可以在typescriptlang.org上找到有关TypeScript的完整指南。对于IDE,我们推荐支持tslint扩展名的Visual Studio Code,但也有很多其它合适的TypeScript IDE。
在执行之前,必须将“TypeScript”文件“编译”为JavaScript。当使用tabris init
命令生成新的Tabris.js应用程序时,已经包含了编译器,因此不需要额外的安装步骤。在给定的选项中,只需选择TypeScript App
即可。应用生成后,执行npm start
,以JavaScript的形式向你的Tabris开发者app提供TypeScript代码。在Visual Studio Code中,你也可以使用预先配置的启动
任务。
只要任务仍在运行,你的TypeScript代码(src
中的任何.ts
文件)的更改将自动由TypeScript编译器检测。不需要重新启动。
注意类型安全
在TypeScript中,并不是所有的API,甚至不是所有的Tabris.js API都是完全类型安全的。 因此建议遵循以下一般准则:
转换:避免显式转换,因为它可以静默失败。使用type guards替代。
隐式的“any”类型:在TypeScript中any
类型的值本质上和JavaScript值相同。编译器将接受对该值的任何操作,包括将其分配给确定类型的变量。如果你不给变量、字段或参数指定类型,并且不能通过赋值来推断,则可能会出现隐式的any
类型。 始终给出函数参数的类型。字段和变量只有在声明时赋了值才是安全的。
控件属性访问:不要使用widget.set(key, value)
或 widget.get(key)
。而是直接像这样访问属性:widget.key = value
。你还可以安全地使用属性对象:比如widget.set({key: value})
和 new Widget({key: value})
。
控件事件处理:不要使用widget.on(event, handler)
。而是使用widget.on({event: handler})
。
控件apply方法:仅使用widget.apply
设置基础Widget
类的属性,比如layoutData
。
选择器API 和 WidgetCollection:默认情况下,控件的find
、children
、sibling
方法返回一个“混合的”WidgetCollection。这意味着你必须执行类型检查和转换以安全地从集合中获取控件,你也可以使用控件类(构造函数)作为选择器,这会使TypeScript“得到”的集合只包含该类型的实例。在这种情况下,不需要转换。例如: widget.children(Button).first('.myButton')
返回一个button控件(或者 null),而不会是其他内容。应该注意的是,这种WidgetCollection的set
方法仍然不是类型感知的。你可以使用forEach
方法来安全地为集合中的所有控件设置属性。
NPM模块:tabris
模块是自动类型安全的,但不是所有可以通过npm
安装的模块都是如此。你可能需要为每个安装的npm模块手动安装声明文件。
接口
在TypeScript中使用tabris模块导出tabris API使用的接口。它们是:
- 属性/参数类型:
Image
、Color
、Font
、LayoutData
、Bounds
、Transformation
、margin
、dimension
、offset
、BoxDimensions
、ImageData
、Selector
和AnimationOptions
- 通用事件对象:
EventObject<T>
,其中T
是目标事件属性的类型。用于没有特定类型属性的事件。也是所有其他事件接口的基础。 - change事件对象:
PropertyChangedEvent<T, U>
,事件属性和U
是value
属性的类型。用于所有与命名方案{propertyName}Changed
匹配的事件,比如BackgroundChanged
。 - 特殊事件对象:所有其他事件使用遵循命名方案
{TargetType}{EventName}Event
的特定接口,比如PickerSelectEvent
。你可能想要使用这些来定义不使用参数解构的监听器。 - 属性映射:这些是
set
方法和控件构造函数使用的接口。你可能希望在扩展控件类时使用它们来定义自己的属性映射。它们遵循命名方案{TargetType}Properties
,比如CompositeProperties
。 - 监听器映射:这些是
on
和off
方法使用的接口。你可能希望在扩展控件类时使用它们来定义自己的监听器映射。它们遵循命名方案{TargetType}Events
比如CompositeEvents
。
JSX
JSX是JavaScript / TypeScript语法的扩展,可以将代码与类似XML的声明进行混合。Tabris 2支持使用类型安全的JSX,在任何基于TypeScript的项目中开箱即用。你需要做的只是命名你的文件为.tsx
而不是.ts
。然后,你就可以使用JSX表达式创建控件了。例如
ui.contentView.append(
<composite left={0} top={0} right={0} bottom={0}>
<button centerX={0} top={100} text='Show Message' onSelect={handleButtonSelect}/>
<textView centerX={0} top='prev() 50' font='24px'/>
</composite>
);
与如下代码效果相同
ui.contentView.append(
new Composite({left: 0, top: 0, right: 0, bottom: 0}).append(
new Button({centerX: 0, top: 100, text: 'Show Message'}).on({select: handleButtonSelect}),
new TextView({centerX: 0, top: 'prev() 50', font: '24px'})
)
);
Tabris.js TypeScript app中的JSX遵循如下具体规则:
- 每个JSX元素都是构造函数调用。如果直接嵌入代码中,则需要彼此分隔(见下文)。
- 名称以小写字母开始的元素是内置元素。包括内置在Tabris.js中的所有可实例化控件,以及
WidgetCollection
。这些元素的类型不需要显式导入。 - 名称以大写字母开始的元素是用户自定义元素,即扩展Tabris.js控件的任何类。这些元素需要导入。
- 属性可以是字符串(使用单引号或双引号)或JavaScript / TypeScript表达式(使用花括号)。
- 与控件属性名称相同的标签属性,用来通过构造函数设置该控件的属性。
- 遵循
on{EventType}
命名方案的的属性用于向该事件注册监听器。 - 每个元素可以有任何数量的子元素(如果该类型支持子元素),所有子元素都以给定的顺序添加到其父元素。
- 要支持对用户定义的元素进行属性检查,需要在自定义控件上添加名为
jsxProperties
的字段。该类型必须与第一个构造函数参数的类型相匹配。你不必为该字段赋任何值。 - 虽然JSX表达式本身是类型安全的,但是它们的返回类型不是(是
any
类型),因此请按照上面的转换说明进行操作。一般认为在widget.append()
中使用未经检查的JSX表达式是安全的,因为所有JSX元素都是可append
类型。
注意,这是不合法的:
ui.contentView.append(
<button centerX={0} top={100} text='Show Message'/>
<textView centerX={0} top='prev() 50' font='24px'/>
);
直接嵌套在代码中的JSX元素必须像表达式一样分隔,比如下面这种情况用逗号分隔:
ui.contentView.append(
<button centerX={0} top={100} text='Show Message'/>,
<textView centerX={0} top='prev() 50' font='24px'/>
);
为了避免这种情况,你可以将控件包裹在WidgetCollection
中。这个例子与上面的例子效果相同:
ui.contentView.append(
<widgetCollection>
<button centerX={0} top={100} text='Show Message'/>
<textView centerX={0} top='prev() 50' font='24px'/>
</widgetCollection>
);
在TypeScript之外使用JSX
如果要在不编写TypeScript的情况下使用JSX,仍然可以使用TypeScript编译器将.jsx
文件转换为.js
。简单创建一个TypeScript app,并在tsconfig.json
的compilerOptions
对象中添加一个条目"allowJs": true
。然后将include
对象中的文件名从.ts
和 .tsx
修改为 .js
何 .jsx
。如果你使用any
类型,你可能还需要调整你的校验设置。