##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
), } //后端生成结果:
view3
测试

view2

console.log(Views.view3()) ``` 如上所示定义一个了Views模块,包含三个视图成员函数。 这要比ejs之类的外部模板灵活的多,而且都是强类型可智能感知的,不用担心打错字之类的,也没有什么额外学习成本,毕竟就是原始js语法+xml。 ### 循环: 因为jsx的{}括号内只能写值表达式,并不能写语句,所以一般用map函数构造一个数组来代替for循环语句: ```typescript const view = (list: Array) =>
{list.map(it =>

{it}

)}
//后端生成结果:

1

2

3

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) =>
{show ?

show

: null}
//后端生成结果:

show

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); } } ```