##4.tsx模板
tsx源自于facebook react框架的jsx语法。简单说就是强类型版的jsx。
在了解jsx之前,需要先了解:**内部dsl**与**外部dsl**,这两概念。
jsx属于内部dsl,而[ejs](https://github.com/tj/ejs),[pug(前jade)](https://github.com/pugjs/pug)之类的则属于外部dsl。
本来js是不具备构建dsl能力的,但因为内部dsl实在是比外部dsl强大好用太多,所以facebook就为js扩展出了jsx语法。
### 前后端共用模板:
一个简单的例子(以下代码需要写在tsx文件里,最终仍是被编译成js):
```typescript
import * as React from 'dfv/src/public/dfvReact'
/**
* 在xml标签里使用{}括号来执行原生js值表达式
*/
const view =
{Date.now()}
console.log(view);
```
以上代码在服务器端运行会生成字符串:`1495705255377
`,在浏览器端运行会生成一个原始dom对象:`HTMLDivElement`,并且innerHtml的值为1495705255377。
### 对视图进行模块划分:
我们可以使用原生js语法对视图进行组合规划:
```typescript
import * as React from 'dfv/src/public/dfvReact'
export const Views = {
view1: (body: string) =>
{body}
,
view2: (body: HTMLElement) =>
{body}
测试
{Views.view1("view2")}
,
view3: () =>
Views.view2(
view3
),
}
//后端生成结果:
console.log(Views.view3())
```
如上所示定义一个了Views模块,包含三个视图成员函数。
这要比ejs之类的外部模板灵活的多,而且都是强类型可智能感知的,不用担心打错字之类的,也没有什么额外学习成本,毕竟就是原始js语法+xml。
### 循环:
因为jsx的{}括号内只能写值表达式,并不能写语句,所以一般用map函数构造一个数组来代替for循环语句:
```typescript
const view = (list: Array) =>
//后端生成结果:
console.log(view(["1", "2", "3"]))
```
如果了解函数式编程的话以上代码非常简单,不然你可能需要补习一下函数式编程基础:forEach(),map(),filter(),reduce();
当然用传统方式来写for循环的话也可以,只是不怎么好看:
```typescript
const view = (list: Array) => {
let arr = Array();
for (let str of list) {
arr.push(
{it}
);
}
return (
{arr}
)
}
```
因为JS里的if语句不是值表达式,所以也要用?:三元表达式来代替:
```typescript
const view = (show:boolean) =>
//后端生成结果:
view(true)
```
### 前端模板的双向绑定:
与React的虚拟dom不太同,本框架使用的是精确双向绑定:
```typescript
import {valid} from "dfv/src/public/valid";
import {dfvBind} from "dfv/src/public/dfvBind";
//将一个对象转换为可绑定对象(这样会为每个属性添加get set函数)
let dat = valid.bindAble({a: 1, b: 2, c: [1, 2, 3]});
let view = () =>
{dfvBind(e => dat.a)}
{dfvBind(e => dat.a + dat.b)}
dat.a)}/>
{dfvBind(e => dat.c.map(it =>
sss:{it}
))}
```
如上所示,使用dfvBind函数来绑定一个表达式,当表达式内的值发生变化时,会触发dom元素的重绘。
在浏览器里修改了input的value值之后,也会同步给dat.a。
但是数组有些例外,需要通过调用push与pop,或者:`dat.c=dat.c`这种方式来触发重绘。
### 数据验证:
通过调用valid.checkAsync()来进行数据验证。
```typescript
//TestDat对象使用的装饰器规则和控制器章节的入参验证类完全一样,前后端代码可以相互复用。
export class TestDat {
@valid.stringNotEmpty()
a = "";
@valid.stringNotEmpty()
b = "";
c = "";
}
//创建可绑定对象
let dat = valid.bindAble(new TestDat());
let view = () =>
dat.a)}/>
dat.b)}/>
async function check() {
/**
* 验证dat
*/
let res = await valid.checkAsync(dat);
if (res.ok) {
alert("验证通过")
}
else {
alert("验证失败:" + res.msg);
}
}
```