# 插件系统 (Plugin System)

- **Summary**: Five 插件系统基于 `BasePlugin` 命名空间构建，提供了一套标准化的状态管理、生命周期控制和事件通信机制。
- **Schema**: 核心类型定义位于 `BasePlugin` 命名空间。
- **Concepts**: BasePlugin, State, Lifecycle, Controller.
- **Configuration**: 插件特定的参数配置。
- **Examples**: 标准插件开发示例。

## Schema

> **Definition**: [BasePlugin](../../plugins/BasePlugin/index.d.ts)

`BasePlugin` 是一个命名空间，包含 `Controller` (抽象基类), `State` (状态接口), `EventMap` (事件映射) 等核心定义。

```typescript
export namespace BasePlugin {
  export interface State {
    enabled: boolean
    visible?: boolean
    config?: unknown
  }
  export abstract class Controller<PluginState, PluginEventMap, PluginServerData, PluginData> {
    public five: Five
    public abstract state: PluginState
    public hooks: Subscribe<PluginEventMap>
    // ...
  }
}
```

## Concepts

### Directory Structure (目录结构)

一个标准的插件通常包含以下文件：

```text
PluginName/
  ├── index.ts        # 插件入口，导出工厂函数和类型
  ├── Controller.ts   # 插件控制器，核心逻辑实现
  └── typing.ts       # 类型定义
```

### BasePlugin

所有插件推荐继承 `BasePlugin.Controller`。它定义了规范的泛型接口：

- `PluginState`: 插件状态类型，继承自 `BasePlugin.State`。
- `PluginEventMap`: 插件事件类型，继承自 `BasePlugin.EventMap`。
- `PluginServerData`: 服务端原始数据类型。
- `PluginData`: 解析后的插件数据类型。

### State (状态)

插件的状态驱动其行为。`BasePlugin.State` 定义了最基本的状态：

- `enabled`: 是否启用。
- `visible`: UI 是否可见。
- `config`: 配置项。

### Lifecycle (生命周期)

插件的主要生命周期方法：

- **constructor**: 初始化配置和初始状态。
- **load**: 加载和解析数据。
- **enable**: 启用插件，注册事件监听，添加副作用。
- **disable**: 禁用插件，移除事件监听，清理副作用。
- **dispose**: 销毁插件，彻底清理资源。

## Configuration

### BasePlugin Configuration

`BasePlugin` 控制器构造函数接受可选的配置对象：

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `staticPrefix` | `string` | `'//vr-image-4.realsee-cdn.cn'` | 静态资源前缀。 |
| `i18n` | `(key: string) => string` | `undefined` | 国际化转换函数。 |

## Examples

### Plugin Development Walkthrough (插件开发指南)

以创建一个 `MyPlugin` 为例。

#### 1. 定义类型 (typing.ts)

定义插件所需的参数、状态、数据和事件类型。

```typescript
import { BasePlugin } from '../BasePlugin'

// 1. 定义参数
export interface Params extends BasePlugin.Config {
  initialState?: Partial<State>
  config?: Config
}

// 2. 定义配置
export interface Config {
  color?: string
}

// 3. 定义状态 (继承 BasePlugin.State)
export interface State extends BasePlugin.State {
  activeId?: string
}

// 4. 定义数据类型
export interface ServerData { /* 服务端数据结构 */ }
export interface PluginData { /* 解析后的数据结构 */ }

// 5. 定义事件 (继承 BasePlugin.EventMap)
export interface EventMap extends BasePlugin.EventMap<State, PluginData> {
  click: (id: string) => void
}
```

#### 2. 实现控制器 (Controller.ts)

继承 `BasePlugin.Controller` 并实现核心逻辑。

```typescript
import { Five } from '@realsee/five'
import { BasePlugin } from '../BasePlugin'
import * as MyPluginType from './typing'

export class Controller extends BasePlugin.Controller<
  MyPluginType.State,
  MyPluginType.EventMap,
  MyPluginType.ServerData,
  MyPluginType.PluginData
> {
  public state: MyPluginType.State

  constructor(five: Five, params?: MyPluginType.Params) {
    super(five)
    this.state = {
      enabled: true,
      visible: true,
      ...params?.initialState
    }
    if (this.state.enabled) {
      this._enable()
    }
  }

  public async load(serverData: MyPluginType.ServerData) {
    // 解析数据
  }

  public enable() {
    if (this.state.enabled) return
    this.state.enabled = true
    this._enable()
    this.hooks.emit('stateChange', { state: this.state, userAction: true })
  }

  public disable() {
    if (!this.state.enabled) return
    this.state.enabled = false
    this._disable()
    this.hooks.emit('stateChange', { state: this.state, userAction: true })
  }

  public dispose = () => {
    this.disable()
    this.hooks.emit('dispose')
  }

  private _enable() {
    // five.scene.add(...)
    // five.on('tap', this.onTap)
  }

  private _disable() {
    // five.scene.remove(...)
    // five.off('tap', this.onTap)
  }
}
```

#### 3. 导出插件 (index.ts)

提供一个工厂函数。

```typescript
import { Five } from '@realsee/five'
import { Controller } from './Controller'
import * as MyPluginType from './typing'

export const MyPlugin = (five: Five, params?: MyPluginType.Params) =>
  new Controller(five, params)

export type { MyPluginType, Controller as MyPluginController }
```

## Common Pitfalls (最佳实践)

1. **状态驱动**: 始终通过修改 `state` 来驱动插件行为，而不是直接操作 DOM 或 3D 对象。
2. **清理资源**: 在 `disable` 和 `dispose` 中务必清理所有事件监听和副作用，防止内存泄漏。
3. **类型安全**: 充分利用 TypeScript 泛型，确保事件和状态的类型安全。

---

```yaml
tags: [插件, 插件开发, 扩展, 自定义功能, 状态管理, plugin, baseplugin, development, guide, extension, lifecycle]
```
