Template

模板引擎使编写类型安全、快速和安全的HTML模板成为可能。它基于TSX,只要你使用文件扩展名`.tsx`并相应调整`tsconfig.json`,就可以使用。

重要的是,它与React不兼容。一旦要使用React,`@deepkit/template`就不兼容了。Deepkit的模板引擎只用于SSR(服务器端渲染)。

Installation

在你的tsconfig中,你必须调整以下设置:jsx`和`jsxImportSource

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2020",
    "moduleResolution": "node",

    "jsx": "react-jsx",
    "jsxImportSource": "@deepkit/template"
  }
}

现在你可以在你的控制器中直接使用TSX。

#!/usr/bin/env ts-node-script
import { App } from '@deepkit/app';
import { FrameworkModule } from '@deepkit/framework';
import { http } from '@deepkit/http';

@http.controller('my-base-url/')
class MyPage {
    @http.GET('hello-world')
    helloWorld() {
        return <div style="color: red">Hello World</div>;
    }
}

new App({
    controllers: [MyPage],
    imports: [
        new FrameworkModule({
            debug: true,
        })
    ]
}).run();

如果你在路由方法中返回这样的TSX,HTTP内容类型会自动设置为`text/html; charset=utf-8`。

Components

你可以按照你在React中习惯的方式构造你的模板。要么将你的布局模块化为几个功能或类组件。

Function Components

最简单的方法是使用一个返回TSX的函数。

async function Website(props: {title: string, children?: any}) {
    return <html>
        <head>
            <title>{props.title}</title>
        </head>
        <body>
            {props.children}
        </body>
    </html>;
}

class MyPage {
    @http.GET('hello-world')
    helloWorld() {
        return <Website title="Hello world">
            <h1>Great page</h1>
        </Website>;
    }
}
$ curl http://localhost:8080/hello-world
<html><head><title>Hello world</title></head><body><h1>Great page</h1></body></html>

函数组件可以是异步的(与React不同)。这与你可能知道的其他模板引擎,如React,是一个重要的区别。

所有的函数都可以访问依赖注入容器,并且可以引用从第三个参数开始的任何依赖关系。

class Database {
    users: any[] = [{ username: 'Peter' }];
}

function UserList(props: {}, children: any, database: Database) {
    return <div>{database.users.length}</div>;
}

class MyPage {
    @http.GET('list')
    list() {
        return <UserList/>
    }
}

new App({
    controllers: [MyPage],
    providers: [Database],
    imports: [new FrameworkModule()]
}).run();

Class Components

另一种编写组件的方式是类组件。它们在依赖注入容器中被处理和实例化,因此可以访问在容器中注册的所有服务。这使得在你的组件中直接访问数据源(如数据库)成为可能,例如。

class UserList {
    constructor(
        protected props: {},
        protected children: any,
        protected database: SQLiteDatabase) {
    }

    async render() {
        const users = await this.database.query(User).find();

        return <div class="users">
            {users.map((user) => <UserDetail user={user}/>)}
        </div>;
    }
}

class MyPage {
    @http.GET('')
    listUsers() {
        return <UserList/>;
    }
}

第一个构造函数参数是为类组件保留的。`props`可以任意定义,`children`总是 "any",然后是可选的依赖关系,你可以任意选择。由于类组件在依赖注入容器中被实例化,你可以访问你的所有服务。

Dynamic HTML

模板引擎已经自动清理了所有使用的变量,所以你可以安全地在模板中直接使用用户输入。要渲染动态HTML,你可以使用html函数。

import { html } from '@deepkit/template';
helloWorld() {
    const yes = "<b>yes!</b>";
    return <div style="color: red">Hello World. {html(yes)}</div>;
}

Optimisation

模板引擎试图优化生成的JSX代码,这样NodeJS/V8就更容易生成HTML字符串了。为了正确地工作,你应该把你的所有组件从主app.tsx文件移到单独的文件中。一个结构可以是这样的。

.
├── app.ts
└── views
    ├── user-detail.tsx
    ├── user-list.tsx
    └── website.tsx