# 组件化思维

> Component 作为基类、生命周期(onAdd/onRemove)、组合以及一个带 bind 的自定义组件。

Source: https://crossfly.dev/zh/docs/learn/thinking-in-components — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 3. 组件化思维

> **你将学到:** `Component` 作为一切的基类、生命周期、组合以及如何创建一个可复用的组件。**前置条件:** [窗口与导航](/docs/learn/windows-and-navigation)。

## 完整示例(复制、粘贴、运行)
一个可复用组件(`CardTarefa`),在 `onAdd` 中搭建一次,并通过 `bind(...)` 用数据重新绑定。该窗口用不同数据创建了三个卡片。可直接编译运行。
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      CardTarefa c1 = new CardTarefa(); add(LAY_AUTO, c1); c1.bind(new Tarefa("Comprar pão"));
      CardTarefa c2 = new CardTarefa(); add(LAY_AUTO, c2); c2.bind(new Tarefa("Pagar conta"));
      CardTarefa c3 = new CardTarefa(); add(LAY_AUTO, c3); c3.bind(new Tarefa("Estudar CrossFly"));
   }
}

class Tarefa                                             // 最小模型
{
   final String titulo;
   Tarefa(String titulo) { this.titulo = titulo; }
}

class CardTarefa extends Component
{
   private final Label titulo = new Label();

   @Override
   public void onAdd(boolean firstTime) throws Exception  // 构建结构一次
   {
      super.onAdd(firstTime);
      setLayout(new HorizontalLayout());
      add(LAY_FILL, titulo);
   }

   public void bind(Tarefa t)                             // 按数据重新绑定（开销小）
   {
      titulo.setText(t.titulo);
      repaint();
   }
}
```
> 下面的片段只展示**核心部分**(放在 `onShow()` 里的内容)。要运行,请把它们放进上面的 `MainWindow` 骨架里。

## 一切皆 `Component`,容器 = Component + Layout
`Label` 是一个 `Component`。`Button` 是一个 `Component`。任何 `Component` 在接收一个 `Layout` 后都会成为**容器**,而 `add(...)` 会把排布委托给该 layout。
```java
Component painel = new Component();
painel.setLayout(new VerticalLayout());
painel.add(LAY_AUTO, new Label("Título"));
```

## 生命周期
| 钩子 | 何时 | 用途 |
|---|---|---|
| `onAdd(boolean firstTime)` | 进入树时 | 初始化(仅首次需要的资源 → `firstTime`) |
| `onRemove()` | 离开时 | 释放定时器/监听器 |

## 使用 `bind(...)` 的可复用组件
可回收"卡片"的模式:**只搭建一次**结构(在 `onAdd` 中),然后按数据**重新绑定**:
```java
public class CardTarefa extends Component {
   private final Label titulo = new Label();
   @Override public void onAdd(boolean firstTime) throws Exception {
      super.onAdd(firstTime);
      setLayout(new HorizontalLayout());
      add(LAY_FILL, titulo);
   }
   public void bind(Tarefa t) {      // 按数据重新绑定（开销小，会被多次调用）
      titulo.setText(t.titulo);
      repaint();
   }
}
```
这正是 `VirtualListView` 用来回收视图的模式(见[加载数据](/docs/learn/loading-data))。

## 本步骤所构建的
可复用的 **`CardTarefa`** 组件 —— 搭建一次,按任务重新绑定。

## 回顾
`Component` 是基类;容器 = `Component` + `Layout`;在 `onAdd(firstTime)` 中初始化,在 `onRemove()` 中释放;自定义组件将**搭建**(一次)与**绑定**(`bind`,多次)分离开来。

## 下一步
→ [布局](/docs/learn/layouts)

## 另见
- 概念:[一切皆 Component](/docs/concepts/everything-is-a-component) · [生命周期](/docs/concepts/lifecycle)
