Bobril 框架与 TSX 使用指南

在最近一年中,Bobril 框架以其类似 React 的方式发展,带来了对 TSX(Type Safe JSX)的全面支持,包括有状态的类组件和无状态的函数式组件。TSX 是一种扩展 TypeScript 的模板语言,用于在一个地方定义 UI 和逻辑,生成 Bobril 元素。本文将介绍如何使用这两种类型的组件以及 TSX 创建一个 TODO 应用。

Bobril快速入门

首先,需要准备项目。创建项目的方式与之前的示例类似:

PowerShell npm i bobril-build -g npm init npm i bobril bobx --save

然后添加 index.tsx 文件并运行:

PowerShell bb

函数式组件

最简单的组件是函数式组件。它不保留任何状态,只是反映输入数据。它由一个函数定义,该函数有一个参数用于数据,并返回 b.IBobrilNode。为了遵循 TSX 组件的规则,函数的名称必须以大写字母开头。数据定义方式与通常相同。

创建 components/listItem.tsx 并编写以下代码:

import * as b from "bobril"; export interface IItem { id: number; text: string; done: boolean; } export interface IItemData extends IItem { index: number; onItemChecked(index: number, value: boolean): void; } export function ListItem(data: IItemData): b.IBobrilNode { return (
  • data.onItemChecked(data.index, value)} /> {data.text}
  • ); } const strikeOut = b.styleDef({ textDecoration: "line-through" });

    可以看到,组件的 TSX 定义非常简单。可以使用原生元素并用数据填充其属性作为 {expression}。在这个组件中,还可以看到 Bobril 键和样式的定义。

    这样的组件可以作为 TSX 元素使用。将以下代码放入 components/list.tsx:

    import * as b from "bobril"; import { ListItem, IItem } from "./listItem"; export interface IListData { items: IItem[]; onItemChecked(index: number, value: boolean): void; } export function List(data: IListData): b.IBobrilNode { return (
      {data.items.map((item, index) => ( ))}
    ); } const noBullets = b.styleDef({ listStyleType: "none" });

    这是一个很好的例子,展示了如何使用内联 TypeScript 函数 map 生成内容。这个函数将输入数据映射成 TSX 元素列表。

    接下来可以看到的是使用展开运算符 {...item} 将数据作为属性提供给子节点。

    类组件

    第二种组件类型是有状态的类组件。因为它是一个类,所以允许保留内部状态。

    创建 components/form.tsx 中的表单组件:

    import * as b from "bobril"; import { observable } from "bobx"; export interface IFormData { onSubmit(value: string): void; } export class Form extends b.Component { @observable private _value: string = ""; render(): b.IBobrilChildren { return ( <> this.updateValue(newValue)} onKeyUp={ev => ev.which === 13 && this.submit()} style={spaceOnRight} /> ); } private updateValue(newValue: string): void { this._value = newValue; } private submit(): boolean { this.data.onSubmit(this._value); this._value = ""; return true; } } const spaceOnRight = b.styleDef({ marginRight: 5 });

    可以看到它与一般组件定义非常相似。它有一个 render() 方法返回 b.IBobrilChildren。数据可以通过 this.data 访问。它必须派生自 b.Component。它还保留了 this._value 作为可观察属性的内部状态。

    片段

    在上面的代码中,可以看到 <>... 元素。它被称为片段,用于将子元素包装到一个虚拟节点中。它基本上用于定义返回多个根元素的组件。

    插槽

    有时,需要为布局创建特殊的组件。如果数据定义包含属性 children: b.IBobrilChildren,则可以简单地以表达式的形式作为 TSX 内容 {data.children} 添加。

    如果需要组合更复杂的布局,则可以使用 Slots 模式。

    创建 components/layout.tsx 并编写以下代码:

    import * as b from "bobril"; export interface ILayoutData { children: { header: b.IBobrilChildren; body: b.IBobrilChildren; footer: b.IBobrilChildren; }; } export function Layout(data: ILayoutData): b.IBobrilNode { return ( <>
    {data.children.header}
    {data.children.body}
    {data.children.footer}
    ); }

    ILayoutData 的 children 具有复杂类型而不是 b.IBobrilChildren。它允许通过多个特定子属性定义内容。TypeScript 将确保正确使用它,因此它是类型安全的。

    BobX 存储

    接下来需要一个 BobX 存储,就像之前文章中所知道的那样。在 store.ts 中定义一个:

    import { observable } from "bobx"; import { IItem } from "./components/listItem"; export class TodoStore { @observable private _todos: IItem[] = []; get list(): IItem[] { return this._todos; } add(text: string): void { this._todos.push({ id: Date.now(), text, done: false }); } edit(index: number, value: boolean): void { this._todos[index].done = value; } }

    组合页面

    最后,可以在 index.tsx 中组合逻辑和组件:

    import * as b from "bobril"; import { Layout } from "./components/layout"; import { List } from "./components/list"; import { Form } from "./components/form"; import { TodoStore } from "./store"; class Todo extends b.Component { todos = new TodoStore(); render(): b.IBobrilChildren { return ( {{ header: , body: ( this.todos.edit(index, value)} /> ), footer:
    this.todos.add(text)} /> }}
    ); } } b.init(() => );

    可以看到它在 todos 属性中存储了内部状态,并通过表达式与对象用于插槽内容定义的方式从组件中创建布局。

    沪ICP备2024098111号-1
    上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485