npm package: @game-flux/core
Description: 用于游戏开发的轻量级状态管理库
Author: lvxiaohui
Latest version: 1.0.1

# GameFlux

<p align="center">
  <strong>专为游戏开发设计的轻量级状态管理库</strong>
</p>

<p align="center">
  <a href="#特性">特性</a> •
  <a href="#安装">安装</a> •
  <a href="#快速开始">快速开始</a> •
  <a href="#核心概念">核心概念</a> •
  <a href="#api文档">API</a> •
  <a href="#最佳实践">最佳实践</a>
</p>

---

## 为游戏而生

GameFlux 是专为游戏开发设计的轻量级状态管理库，提供简洁的API和出色的跨平台兼容性。

## 为什么选择 GameFlux？

### 适用场景

- **游戏开发状态管理** - GameFlux 专为游戏优化，支持 ECS
- **跨小程序/多平台** - 完美兼容微信、抖音、H5、原生
- **简单易用 + 完整 TypeScript** - 完整的类型推导和提示
- **时间旅行调试游戏** - 内置完整 DevTools
- **轻量级设计** - 零依赖，体积小巧
- **不可变更新 + 简洁语法** - 内置 Immer-like 实现，写法简洁


## 特性

### 核心特性
- **不可变更新** - 内置轻量级 Immer 实现，写法像可变操作
- **完整TypeScript支持** - 100% TypeScript编写，完美的类型推导
- **模块化设计** - 真正的模块隔离，避免状态混乱
- **零依赖** - 无任何第三方依赖（除序列化库devalue）

### 性能优化
- **Getter缓存** - 智能缓存计算结果，避免重复计算
- **选择性订阅** - 只监听关心的状态变化
- **批量更新** - 支持批量提交mutations
- **懒加载模块** - 按需加载模块，减少初始化开销

### 跨平台兼容
- **H5** - 完美支持浏览器环境
- **微信小程序** - 支持微信、抖音等小程序
- **原生App** - 支持 iOS/Android 原生环境
- **Cocos Creator** - 深度优化 Cocos Creator 集成

### 开发工具
- **时间旅行调试** - 撤销/重做/状态快照
- **日志插件** - 自动记录所有 mutation 和 action
- **持久化插件** - 跨平台本地存储方案
- **插件系统** - 轻松扩展功能

## 安装

```bash
npm install @game-flux/core

# 或者使用 yarn
yarn add @game-flux/core

# 或者使用 pnpm
pnpm add @game-flux/core
```

## 快速开始

### 1. 定义模块

```typescript
import { defineModule } from '@game-flux/core';

// 定义玩家模块
const playerModule = defineModule({
  state: {
    coins: 0,
    level: 1,
    exp: 0
  },
  
  mutations: {
    // 添加金币
    addCoins(state, amount: number) {
      state.coins += amount;  // 像可变操作一样直接修改
    },
    
    // 升级
    levelUp(state) {
      state.level += 1;
      state.exp = 0;
    },
    
    // 添加经验
    addExp(state, amount: number) {
      state.exp += amount;
    }
  },
  
  getters: {
    // 是否富有
    isRich(state) {
      return state.coins > 1000;
    },
    
    // 经验百分比
    expPercentage(state) {
      const required = state.level * 100;
      return Math.floor((state.exp / required) * 100);
    }
  },
  
  actions: {
    // 购买物品
    async buyItem({ state, commit }, { price }: { price: number }) {
      if (state.coins >= price) {
        commit('addCoins', -price);
        return true;
      }
      return false;
    }
  }
});
```

### 2. 创建 Store

```typescript
import { createStore } from '@game-flux/core';

const store = createStore({
  state: {
    loading: false
  },
  
  mutations: {
    setLoading(state, loading: boolean) {
      state.loading = loading;
    }
  },
  
  modules: {
    player: playerModule
  }
});
```

### 3. 使用 Store

```typescript
// 读取状态
console.log(store.state.player.coins);  // 0

// 提交 mutation
store.commit('player/addCoins', 100);
console.log(store.state.player.coins);  // 100

// 使用 getter
console.log(store.getters['player/isRich']);  // false

// 分发 action
await store.dispatch('player/buyItem', { price: 50 });
console.log(store.state.player.coins);  // 50

// 订阅变化
store.subscribe((mutation, state) => {
  console.log('Mutation:', mutation.type);
  console.log('New state:', state);
});
```

## 核心概念

### State（状态）

State 是应用的唯一数据源，采用单一状态树结构。

```typescript
interface RootState {
  loading: boolean;
  player: {
    coins: number;
    level: number;
  };
  game: {
    score: number;
    time: number;
  };
}
```

### Mutations（突变）

Mutation 是修改 state 的唯一方式，必须是同步函数。

```typescript
mutations: {
  // 使用 payload 参数
  addCoins(state, amount: number) {
    state.coins += amount;
  },
  
  // 使用对象参数
  updatePlayer(state, { coins, level }: Partial<PlayerState>) {
    if (coins !== undefined) state.coins = coins;
    if (level !== undefined) state.level = level;
  }
}
```

### Actions（动作）

Action 用于处理异步操作，可以包含任意异步逻辑。

```typescript
actions: {
  async loadPlayerData({ commit }) {
    const data = await fetchPlayerData();
    commit('updatePlayer', data);
  },
  
  async saveProgress({ state }) {
    await saveToServer(state);
  }
}
```

### Getters（计算属性）

Getter 用于从 state 派生出一些状态，会自动缓存。

```typescript
getters: {
  // 简单 getter
  totalScore(state) {
    return state.kills * 100 + state.combos * 50;
  },
  
  // 依赖其他 getter
  rank(state, getters) {
    const score = getters.totalScore;
    return score > 10000 ? 'S' : 'A';
  }
}
```

### Modules（模块）

模块让你可以将 store 分割成多个模块，每个模块拥有自己的 state、mutation、action、getter。

```typescript
const store = createStore({
  modules: {
    player: playerModule,
    game: gameModule,
    ui: uiModule
  }
});

// 访问模块状态
store.state.player.coins;

// 提交模块 mutation
store.commit('player/addCoins', 100);

// 分发模块 action
store.dispatch('player/loadData');

// 访问模块 getter
store.getters['player/isRich'];
```

## 高级特性

### 批量更新

减少重复渲染，提升性能：

```typescript
// 批量提交多个 mutation，只触发一次订阅通知
store.batch(() => {
  store.commit('player/addCoins', 100);
  store.commit('player/addExp', 50);
  store.commit('player/levelUp');
});
```

### 选择性订阅（watch）

只监听关心的状态变化：

```typescript
// 只监听玩家金币变化
const unwatch = store.watch(
  state => state.player.coins,
  (newCoins, oldCoins) => {
    console.log(`金币从 ${oldCoins} 变为 ${newCoins}`);
  }
);

// 取消监听
unwatch();

// 监听多个字段
store.watch(
  state => ({ coins: state.player.coins, level: state.player.level }),
  (newValue, oldValue) => {
    console.log('玩家数据变化:', newValue);
  },
  { deep: true }  // 深度监听
);
```

### 结构共享优化

未修改的部分自动保持引用，提升性能：

```typescript
const oldState = store.state;
store.commit('setLoading', true);  // 只修改 loading
const newState = store.state;

// 未修改的模块保持原引用
oldState.player === newState.player  // true!

// 可以用 === 快速判断组件是否需要更新
if (oldState.player !== newState.player) {
  updatePlayerUI();
}
```

### 严格模式

严格模式可以帮助你在开发阶段检测不规范的状态修改操作。

```typescript
const store = createStore({
  state: { count: 0 },
  mutations: {
    increment(state) {
      state.count++;  // 正确：在 mutation 中修改
    }
  },
  strict: true  // 启用严格模式
});

// 正确：通过 mutation 修改状态
store.commit('increment');  // OK

// 错误：直接修改 state（开发环境会警告）
store.state.count++;  // Warning: 直接修改 state 可能导致状态不一致
```

**严格模式的作用**：
- 检测在 mutation 之外对 state 根级属性的直接修改
- 仅在开发环境生效，不影响生产环境性能
- 通过 Proxy 监听状态对象的修改
- 帮助养成良好的状态管理习惯

**严格模式的限制**：

严格模式只能检测**根级属性**的直接修改，无法检测深层嵌套属性的修改：

```typescript
const store = createStore({
  state: {
    loading: false,
    user: { name: 'Alice', age: 20 }
  },
  strict: true
});

// 会被检测到（根级属性）
store.state.loading = true;  // Warning!

// 无法检测到（深层属性）
store.state.user.name = 'Bob';  // 不会警告，但仍然是错误的做法
store.state.user.age = 25;      // 不会警告，但仍然是错误的做法
```

**推荐方案**：
- 配合 TypeScript 的 `readonly` 类型确保类型安全
- 使用 ESLint 规则禁止直接修改 state
- 团队代码审查时注意状态修改规范
- **最重要**：所有状态修改都通过 `commit` 提交 mutation

**注意事项**：
- 严格模式仅用于开发调试，生产环境会自动禁用
- 启用严格模式会有轻微的性能开销（仅开发环境）
- 所有状态修改必须通过 `commit` 提交 mutation
- 在 action 中修改 state 也会触发警告，应该通过 `commit` 提交 mutation

**使用场景**：
- 大型项目中确保团队成员遵循状态管理规范
- 学习阶段帮助理解单向数据流的概念
- 调试状态不一致问题时快速定位错误修改

## 插件系统

### 持久化插件

```typescript
import { createStore, persistPlugin } from '@game-flux/core';

const store = createStore({
  modules: { player },
  plugins: [
    persistPlugin({
      key: 'my-game-state',
      paths: ['player.coins', 'player.level'],
      storage: 'auto',  // 自动检测环境
      debounce: 1000     // 防抖1秒
    })
  ]
});
```

### 日志插件

```typescript
import { loggerPlugin } from '@game-flux/core';

const store = createStore({
  modules: { player },
  plugins: [
    loggerPlugin({
      collapsed: true,
      filter(mutation) {
        // 过滤掉频繁的 mutation
        return !mutation.type.includes('updateTimer');
      }
    })
  ]
});
```

### 时间旅行调试

```typescript
import { timeTravelPlugin } from '@game-flux/core';

const store = createStore({
  modules: { player },
  plugins: [
    timeTravelPlugin({
      maxHistory: 50
    })
  ]
});

// 使用时间旅行
store.devtools.undo();           // 撤销
store.devtools.redo();           // 重做
store.devtools.jumpTo(10);       // 跳转到第10个状态
const snapshot = store.devtools.exportSnapshot();  // 导出快照
```

## 最佳实践

### 1. 模块化组织

GameFlux 采用扁平化模块设计，推荐使用命名约定表达层级关系：

```typescript
// 推荐：使用点号表达层级
const store = createStore({
  modules: {
    'player': playerModule,
    'player.inventory': inventoryModule,    // 玩家背包
    'player.skills': skillsModule,          // 玩家技能
    'game': gameModule,
    'game.battle': battleModule,            // 战斗模块
    'ui': uiModule
  }
});

// 使用时仍然是扁平的
store.commit('player.inventory/addItem', item);
store.getters['player.skills/learned'];

// 不支持：嵌套模块
// modules: {
//   player: {
//     modules: { inventory: {...} }  // 不支持嵌套模块
//   }
// }
```

**文件组织：**
```
src/
  store/
    index.ts                    # Store 入口
    modules/
      player.ts                 # 玩家模块
      player.inventory.ts       # 玩家背包模块
      player.skills.ts          # 玩家技能模块
      game.ts                   # 游戏模块
      ui.ts                     # UI模块
```

### 2. 类型安全

```typescript
// 定义类型
interface PlayerState {
  coins: number;
  level: number;
}

// 使用类型
const playerModule = defineModule<PlayerState>({
  state: {
    coins: 0,
    level: 1
  },
  // TypeScript 会自动推导参数类型
  mutations: {
    addCoins(state, amount: number) {
      state.coins += amount;
    }
  }
});
```

### 3. 避免在 Getter 中进行重计算

```typescript
// 不推荐 - 每次都创建新数组
getters: {
  sortedItems(state) {
    return [...state.items].sort();
  }
}

// 推荐 - 使用缓存或在 mutation 中排序
mutations: {
  addItem(state, item) {
    state.items.push(item);
    state.items.sort();  // 在修改时排序
  }
}
```

### 4. 异步操作统一在 Action 中处理

```typescript
// 不推荐 - 在组件中处理异步
async function onButtonClick() {
  const data = await api.fetchData();
  store.commit('setData', data);
}

// 推荐 - 在 action 中处理
actions: {
  async loadData({ commit }) {
    const data = await api.fetchData();
    commit('setData', data);
  }
}

// 组件中
async function onButtonClick() {
  await store.dispatch('loadData');
}
```

## Cocos Creator 集成

### 在组件中使用

```typescript
import { Component } from 'cc';
import { store } from './store';

export class PlayerComponent extends Component {
  private unsubscribe?: () => void;
  
  onLoad() {
    // 订阅状态变化
    this.unsubscribe = store.watch(
      state => state.player.coins,
      (coins) => {
        this.updateCoinsDisplay(coins);
      }
    );
  }
  
  onDestroy() {
    // 组件销毁时取消订阅
    this.unsubscribe?.();
  }
  
  onButtonClick() {
    // 提交 mutation
    store.commit('player/addCoins', 100);
  }
  
  private updateCoinsDisplay(coins: number) {
    // 更新 UI
  }
}
```

### 在 ECS 系统中使用

```typescript
import { ecs } from '@flux/core';
import { store } from './store';

export class GameSystem extends ecs.System {
  async run() {
    // 分发 action
    await store.dispatch('game/loadLevel', { levelId: 1 });
    
    // 读取状态
    const score = store.state.game.score;
  }
}
```

## API 文档

### API 快速参考

#### 核心 APIs

| API | 说明 | 返回值 |
|-----|------|--------|
| [`createStore(options)`](#createstoreoptions) | 创建 store 实例 | `Store<S>` |
| [`defineModule(options)`](#definemoduleoptions) | 定义状态模块 | `ModuleOptions<S>` |
| [`produce(base, recipe)`](#producebase-recipe) | 不可变更新助手 | `T` |

#### Store 实例方法

| 方法 | 说明 | 返回值 |
|------|------|--------|
| [`store.state`](#storestate) | 获取当前状态树（只读） | `S` |
| [`store.getters`](#storegetters) | 获取所有 getter 结果 | `Record<string, any>` |
| [`store.commit(type, payload)`](#storecommittype-payload) | 同步提交 mutation | `void` |
| [`store.dispatch(type, payload)`](#storedispatchtype-payload) | 分发 action（异步） | `Promise<any>` |
| [`store.subscribe(subscriber)`](#storesubscribesubscriber) | 订阅所有 mutations | `Unsubscriber` |
| [`store.watch(selector, callback, options)`](#storewatchselector-callback-options) | 监听特定状态变化 | `Unsubscriber` |
| [`store.batch(fn)`](#storebatchfn) | 批量提交 mutations | `void` |
| [`store.replaceState(state)`](#storereplacestate) | 替换整个状态树 | `void` |

#### 插件 APIs

| 插件 | 说明 |
|------|------|
| [`persistPlugin(options)`](#persistplugin) | 状态持久化 |
| [`loggerPlugin(options)`](#loggerplugin) | 日志记录 |
| [`timeTravelPlugin(options)`](#timetravelplugin) | 时间旅行调试 |
| [`createStorageAdapter(type)`](#createstorageadapter) | 创建存储适配器 |

---

### createStore(options)

创建一个 GameFlux store 实例。

**类型签名**
```typescript
function createStore<S = any>(options: StoreOptions<S>): Store<S>
```

**参数**

| 参数 | 类型 | 必填 | 默认值 | 描述 |
|------|------|:----:|--------|------|
| `options.state` | `Object` | 否 | `{}` | 根级状态对象 |
| `options.mutations` | `Object` | 否 | `{}` | 根级 mutation 函数映射 |
| `options.actions` | `Object` | 否 | `{}` | 根级 action 函数映射 |
| `options.getters` | `Object` | 否 | `{}` | 根级 getter 函数映射 |
| `options.modules` | `Object` | 否 | `{}` | 子模块映射 |
| `options.plugins` | `Plugin[]` | 否 | `[]` | 插件数组 |
| `options.strict` | `boolean` | 否 | `false` | 严格模式，禁止在 mutation 外修改 state |
| `options.devMode` | `boolean` | 否 | `false` | 开发模式，开启更多警告 |

**返回值**

| 类型 | 描述 |
|------|------|
| `Store<S>` | store 实例，包含 `state`、`getters`、`commit`、`dispatch` 等方法 |

**示例**
```typescript
const store = createStore({
  state: { loading: false },
  mutations: {
    setLoading(state, loading: boolean) {
      state.loading = loading;
    }
  },
  modules: {
    player: playerModule
  },
  plugins: [persistPlugin(), loggerPlugin()]
});
```

**相关 API**
- [`defineModule()`](#definemoduleoptions) - 定义模块
- [`store.commit()`](#storecommittype-payload) - 提交 mutation

---

### defineModule(options)

定义一个状态模块，用于组织和隔离状态逻辑。

**类型签名**
```typescript
function defineModule<S extends ModuleState>(
  options: ModuleOptions<S>
): ModuleOptions<S>
```

**参数**

| 参数 | 类型 | 必填 | 默认值 | 描述 |
|------|------|:----:|--------|------|
| `options.state` | `Object` | 是 | - | 模块状态对象 |
| `options.mutations` | `Object` | 否 | `{}` | 模块 mutations |
| `options.actions` | `Object` | 否 | `{}` | 模块 actions |
| `options.getters` | `Object` | 否 | `{}` | 模块 getters |

**返回值**

| 类型 | 描述 |
|------|------|
| `ModuleOptions<S>` | 模块配置对象，传递给 `createStore` 的 `modules` 选项 |

**示例**
```typescript
const playerModule = defineModule({
  state: { coins: 0, level: 1 },
  mutations: {
    addCoins(state, amount: number) {
      state.coins += amount;  // 直接修改，内部使用 Immer
    }
  },
  getters: {
    isRich(state) {
      return state.coins > 1000;
    }
  },
  actions: {
    async loadData({ commit }) {
      const data = await fetchPlayerData();
      commit('updatePlayer', data);
    }
  }
});
```

**注意事项**
- 主要用于 TypeScript 类型推导，运行时等同于直接返回对象
- 每个模块的 state、mutations、getters 都是隔离的

**相关 API**
- [`createStore()`](#createstoreoptions) - 创建 store

---

### store.state

获取当前状态树（只读）。

**类型**
```typescript
readonly state: S
```

**示例**
```typescript
console.log(store.state.loading);         // 访问根级状态
console.log(store.state.player.coins);    // 访问模块状态
```

**注意事项**
- 不要直接修改 state，必须通过 mutation
- TypeScript 会将 state 标记为只读

**相关 API**
- [`store.commit()`](#storecommittype-payload) - 修改状态

---

### store.getters

获取所有 getter 的计算结果。

**类型**
```typescript
readonly getters: Record<string, any>
```

**示例**
```typescript
// 访问根级 getter
const ready = store.getters.isReady;

// 访问模块 getter
const isRich = store.getters['player/isRich'];
```

**注意事项**
- Getter 结果会自动缓存，只有依赖的 state 变化时才重新计算
- 访问模块 getter 使用 `'moduleName/getterName'` 格式

**相关 API**
- [`defineModule()`](#definemoduleoptions) - 定义 getters

---

### store.commit(type, payload)

同步提交一个 mutation 来修改状态。这是修改 state 的唯一方式。

**类型签名**
```typescript
commit<P = any>(type: string, payload?: P): void
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `type` | `string` | 是 | Mutation 类型，支持命名空间（如 `'player/addCoins'`）|
| `payload` | `any` | 否 | 传递给 mutation 的数据 |

**返回值**

无返回值（`void`）

**示例**
```typescript
// 根级 mutation
store.commit('setLoading', true);

// 模块 mutation
store.commit('player/addCoins', 100);

// 对象参数
store.commit('player/update', { coins: 100, level: 2 });
```

**注意事项**
- Mutation 必须是同步函数
- 不要在 mutation 中调用异步操作（如 API 请求、setTimeout）
- 异步操作请使用 `store.dispatch()`
- 每次 commit 都会触发所有订阅者

**相关 API**
- [`store.dispatch()`](#storedispatchtype-payload) - 分发 action
- [`store.subscribe()`](#storesubscribesubscriber) - 订阅 mutations
- [`store.batch()`](#storebatchfn) - 批量提交

---

### store.dispatch(type, payload)

分发一个 action，可以包含异步操作。

**类型签名**
```typescript
dispatch<P = any>(type: string, payload?: P): Promise<any>
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `type` | `string` | 是 | Action 类型，支持命名空间 |
| `payload` | `any` | 否 | 传递给 action 的数据 |

**返回值**

| 类型 | 描述 |
|------|------|
| `Promise<any>` | action 函数的返回值 |

**示例**
```typescript
// 异步 action
await store.dispatch('player/loadData');

// 带参数和返回值
const result = await store.dispatch('player/buyItem', { 
  id: 123, 
  price: 100 
});
if (result.success) {
  console.log('购买成功');
}
```

**使用场景**
- API 请求
- 异步数据处理
- 复杂的业务逻辑
- 需要提交多个 mutations

**注意事项**
- Action 内部通过 `commit` 提交 mutation
- 可以返回 Promise 供调用者等待

**相关 API**
- [`store.commit()`](#storecommittype-payload) - 提交 mutation

---

### store.subscribe(subscriber)

订阅所有 mutation 的变化。

**类型签名**
```typescript
subscribe(
  subscriber: (mutation: MutationInfo, state: S, prevState: S) => void
): Unsubscriber
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `subscriber` | `Function` | 是 | 订阅函数，在每次 mutation 后调用 |

**订阅函数参数**

| 参数 | 类型 | 描述 |
|------|------|------|
| `mutation` | `MutationInfo` | Mutation 信息，包含 `type`、`payload`、`timestamp` |
| `state` | `S` | 新状态 |
| `prevState` | `S` | 旧状态 |

**返回值**

| 类型 | 描述 |
|------|------|
| `Unsubscriber` | 取消订阅的函数 |

**示例**
```typescript
const unsubscribe = store.subscribe((mutation, state, prevState) => {
  console.log('Mutation:', mutation.type);
  console.log('Payload:', mutation.payload);
  console.log('State changed:', prevState !== state);
});

// 取消订阅
unsubscribe();
```

**使用场景**
- 持久化插件（监听所有变化并保存）
- 日志记录
- 调试工具
- 状态同步到其他系统

**相关 API**
- [`store.watch()`](#storewatchselector-callback-options) - 选择性订阅
- [`store.commit()`](#storecommittype-payload) - 提交 mutation

---

### store.watch(selector, callback, options)

监听特定状态的变化，只在关心的数据变化时触发（选择性订阅）。

**类型签名**
```typescript
watch<T>(
  selector: (state: S) => T,
  callback: (newValue: T, oldValue: T) => void,
  options?: WatchOptions
): Unsubscriber
```

**参数**

| 参数 | 类型 | 必填 | 默认值 | 描述 |
|------|------|:----:|--------|------|
| `selector` | `Function` | 是 | - | 选择器函数，从 state 中提取要监听的值 |
| `callback` | `Function` | 是 | - | 回调函数，在值变化时调用 |
| `options.immediate` | `boolean` | 否 | `false` | 是否立即执行一次 |
| `options.deep` | `boolean` | 否 | `false` | 深度监听（对象/数组） |

**返回值**

| 类型 | 描述 |
|------|------|
| `Unsubscriber` | 取消监听的函数 |

**示例**
```typescript
// 监听单个字段
const unwatch = store.watch(
  state => state.player.coins,
  (newCoins, oldCoins) => {
    console.log(`金币从 ${oldCoins} 变为 ${newCoins}`);
  }
);

// 监听多个字段
store.watch(
  state => ({ 
    coins: state.player.coins, 
    level: state.player.level 
  }),
  (newValue, oldValue) => {
    updateUI(newValue);
  },
  { immediate: true, deep: true }
);

// 取消监听
unwatch();
```

**使用场景**
- UI 组件监听特定数据
- 性能敏感场景（避免监听所有变化）
- 条件触发逻辑

**性能对比**

| 方法 | 触发频率 | 适用场景 |
|------|----------|----------|
| `subscribe` | 每次 mutation | 日志、持久化 |
| `watch` | 只在关心的数据变化 | UI 更新、条件逻辑 |

**注意事项**
- 相比 `subscribe`，性能更好
- `deep: true` 会进行深度比较，有性能开销

**相关 API**
- [`store.subscribe()`](#storesubscribesubscriber) - 订阅所有 mutations

---

### store.batch(fn)

批量提交多个 mutation，只触发一次订阅通知。

**类型签名**
```typescript
batch(fn: () => void): void
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `fn` | `Function` | 是 | 要执行的批量操作函数 |

**返回值**

无返回值（`void`）

**示例**
```typescript
store.batch(() => {
  store.commit('player/addCoins', 100);
  store.commit('player/addExp', 50);
  store.commit('player/levelUp');
});
// 所有订阅者只会收到一次通知
```

**性能对比**

| 方式 | 订阅触发次数 | 渲染次数 |
|------|--------------|----------|
| 逐个 commit | 3次 | 3次 |
| batch | 1次 | 1次 |

**使用场景**
- 游戏中一次操作涉及多个状态变化
- 批量数据初始化
- 减少UI重复渲染

**注意事项**
- 显著减少重复渲染，提升性能
- batch 内的所有 mutation 仍然是原子性的

**相关 API**
- [`store.commit()`](#storecommittype-payload) - 提交 mutation

---

### store.replaceState(state)

替换整个状态树（用于时间旅行、状态恢复）。

**类型签名**
```typescript
replaceState(state: S): void
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `state` | `S` | 是 | 新的状态对象 |

**返回值**

无返回值（`void`）

**示例**
```typescript
// 恢复保存的状态
const savedState = localStorage.getItem('gameState');
if (savedState) {
  store.replaceState(JSON.parse(savedState));
}

// 时间旅行
store.devtools.jumpTo(5);  // 内部调用 replaceState
```

**使用场景**
- 状态恢复（从本地存储）
- 时间旅行调试
- 服务器同步状态

**注意事项**
- 会触发所有订阅者
- 谨慎使用，可能导致状态不一致
- 建议只在初始化或调试时使用

**相关 API**
- [`timeTravelPlugin()`](#timetravelplugin) - 时间旅行插件

---

### produce(base, recipe)

不可变更新助手函数（Immer-like），让你可以用"可变"的方式写不可变更新。

**类型签名**
```typescript
function produce<T>(
  base: T, 
  recipe: (draft: T) => void
): T
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `base` | `T` | 是 | 原始对象（不会被修改） |
| `recipe` | `Function` | 是 | 修改函数，接收 draft 并"直接修改" |

**返回值**

| 类型 | 描述 |
|------|------|
| `T` | 新的不可变对象（如果有修改）或原对象（如果无修改，结构共享） |

**示例**
```typescript
import { produce } from '@game-flux/core';

const currentState = {
  user: { name: 'Bob', age: 20 },
  items: [1, 2, 3],
  config: { theme: 'dark' }
};

const nextState = produce(currentState, draft => {
  draft.user.name = 'Alice';     // 直接修改
  draft.items.push(4);           // 数组操作
});

// 不可变性
console.log(currentState.user.name);  // 'Bob'（原对象不变）
console.log(nextState.user.name);     // 'Alice'（新对象）

// 结构共享
console.log(currentState.config === nextState.config);  // true（未修改的部分共享引用）
```

**优势**

| 特性 | 传统方式 | produce 方式 |
|------|----------|--------------|
| 语法 | `{ ...state, user: { ...state.user, name: 'Alice' } }` | `draft.user.name = 'Alice'` |
| 可读性 | 一般 | 优秀 |
| 嵌套更新 | 复杂 | 简单 |
| 结构共享 | 手动 | 自动 |

**注意事项**
- GameFlux 内部自动使用 `produce`，mutation 中直接修改即可
- 支持结构共享，未修改的部分保持原引用
- 如果没有任何修改，返回原对象（性能优化）
- 不支持 Map、Set（使用普通对象代替）

**相关 API**
- [`store.commit()`](#storecommittype-payload) - 内部使用 produce

---

## 插件 API 文档

### persistPlugin(options)

状态持久化插件，自动保存和恢复状态。

**类型签名**
```typescript
function persistPlugin<S = any>(options?: PersistOptions): Plugin<S>
```

**参数**

| 参数 | 类型 | 必填 | 默认值 | 描述 |
|------|------|:----:|--------|------|
| `options.key` | `string` | 否 | `'game-flux-state'` | 存储键名 |
| `options.storage` | `StorageAdapter` | 否 | 自动检测 | 存储适配器 |
| `options.paths` | `string[]` | 否 | `undefined` | 要持久化的路径，`undefined` 表示全部 |
| `options.excludePaths` | `string[]` | 否 | `[]` | 要排除的路径 |
| `options.debounce` | `number` | 否 | `1000` | 防抖延迟时间（毫秒），避免频繁保存 |
| `options.serializer` | `'devalue' \| 'json' \| Serializer` | 否 | `'devalue'` | 序列化器：支持 Date/Map/Set/RegExp/循环引用 |

**返回值**

| 类型 | 描述 |
|------|------|
| `Plugin<S>` | 插件实例 |

**示例**
```typescript
import { createStore, persistPlugin, createStorageAdapter } from '@game-flux/core';

const store = createStore({
  state: { player: { coins: 0 }, ui: { theme: 'dark' } },
  plugins: [
    persistPlugin({
      key: 'my-game',
      paths: ['player'],              // 只保存 player
      excludePaths: ['ui.theme'],     // 排除 ui.theme
      debounce: 2000                  // 状态稳定2秒后保存
    })
  ]
});

// 使用自定义存储
const customStorage = createStorageAdapter('cocos');
const store2 = createStore({
  plugins: [persistPlugin({ storage: customStorage })]
});

// 使用 devalue 序列化器（默认，支持特殊类型）
const store3 = createStore({
  state: {
    loginTime: new Date(),
    settings: new Map([['theme', 'dark']]),
    tags: new Set(['vip', 'premium'])
  },
  plugins: [persistPlugin()]  // 默认使用 devalue
});

// 使用 JSON 序列化器（更快，但不支持特殊类型）
const store4 = createStore({
  state: { count: 0, name: 'game' },
  plugins: [persistPlugin({ serializer: 'json' })]
});
```

**序列化器对比**

| 序列化器 | 性能 | 支持类型 | 适用场景 |
|---------|------|---------|---------|
| **devalue**（默认） | 序列化约 5x 慢，反序列化约 2x 慢 | Date, Map, Set, RegExp, 循环引用等 | 状态包含特殊类型 |
| **json** | 最快 | 基本类型、普通对象/数组 | 纯数据状态，追求性能 |

**支持的特殊类型（devalue）**

```typescript
const store = createStore({
  state: {
    // Date 对象
    loginTime: new Date('2024-01-01'),
    
    // Map 对象
    settings: new Map([['theme', 'dark'], ['lang', 'zh']]),
    
    // Set 对象
    tags: new Set(['vip', 'new', 'active']),
    
    // RegExp 对象
    pattern: /[0-9]+/gi,
    
    // 循环引用
    circular: { name: 'test' }
  },
  plugins: [persistPlugin()]  // 自动处理这些类型
});

// 循环引用示例
store.state.circular.self = store.state.circular;
```

**支持的平台**

| 平台 | 存储方式 | 适配器类型 |
|------|----------|-----------|
| Web/H5 | `localStorage` | `'h5'` |
| 微信小程序 | `wx.getStorageSync` | `'wechat'` |
| Cocos Creator | `sys.localStorage` | `'cocos'` |
| 自定义 | 实现 `StorageAdapter` 接口 | 自定义对象 |

**注意事项**
- 首次创建 store 时会自动从存储恢复状态
- 使用 `debounce` 选项避免频繁写入影响性能
- 大型状态树建议使用 `paths` 只持久化必要部分

**相关 API**
- [`createStorageAdapter()`](#createstorageadapter) - 创建存储适配器

---

### loggerPlugin(options)

日志记录插件，用于调试和监控。

**类型签名**
```typescript
function loggerPlugin<S = any>(options?: LoggerOptions): Plugin<S>
```

**参数**

| 参数 | 类型 | 必填 | 默认值 | 描述 |
|------|------|:----:|--------|------|
| `options.collapsed` | `boolean` | 否 | `true` | 是否折叠日志组 |
| `options.logMutations` | `boolean` | 否 | `true` | 是否记录 mutations |
| `options.logActions` | `boolean` | 否 | `true` | 是否记录 actions |
| `options.logState` | `boolean` | 否 | `true` | 是否记录状态变化 |
| `options.filter` | `(mutation) => boolean` | 否 | `undefined` | 过滤函数，返回 `true` 才记录 |

**返回值**

| 类型 | 描述 |
|------|------|
| `Plugin<S>` | 插件实例 |

**示例**
```typescript
import { createStore, loggerPlugin } from '@game-flux/core';

const store = createStore({
  plugins: [
    loggerPlugin({
      collapsed: false,               // 展开日志
      logActions: true,
      filter: (mutation) => {
        // 只记录 player 模块的变化
        return mutation.type.startsWith('player/');
      }
    })
  ]
});
```

**日志输出示例**
```
GameFlux Mutation @ 12:34:56.789
  ├─ Type: player/addCoins
  ├─ Payload: 100
  ├─ Prev State: { player: { coins: 0 } }
  └─ Next State: { player: { coins: 100 } }
```

**注意事项**
- 仅在开发环境使用，生产环境建议禁用
- 使用 `filter` 减少日志噪音
- 大量日志可能影响性能

**相关 API**
- [`timeTravelPlugin()`](#timetravelplugin) - 时间旅行插件

---

### timeTravelPlugin(options)

时间旅行调试插件，支持撤销/重做、状态快照。

**类型签名**
```typescript
function timeTravelPlugin<S = any>(options?: TimeTravelOptions): Plugin<S>
```

**参数**

| 参数 | 类型 | 必填 | 默认值 | 描述 |
|------|------|:----:|--------|------|
| `options.maxHistory` | `number` | 否 | `50` | 最大历史记录数 |
| `options.enabled` | `boolean` | 否 | `true` | 是否启用 |

**返回值**

| 类型 | 描述 |
|------|------|
| `Plugin<S>` | 插件实例，包含时间旅行 API |

**示例**
```typescript
import { createStore, timeTravelPlugin } from '@game-flux/core';

const timeTravel = timeTravelPlugin({ maxHistory: 100 });

const store = createStore({
  state: { step: 0 },
  mutations: {
    increment(state) { state.step++; }
  },
  plugins: [timeTravel]
});

// 进行一些操作
store.commit('increment'); // step = 1
store.commit('increment'); // step = 2
store.commit('increment'); // step = 3

// 时间旅行
timeTravel.undo();         // step = 2
timeTravel.undo();         // step = 1
timeTravel.redo();         // step = 2
timeTravel.jumpTo(0);      // step = 0

// 获取历史记录
const history = timeTravel.getHistory();
console.log(history.length); // 4

// 导出/导入快照
const snapshot = timeTravel.exportSnapshot();
localStorage.setItem('snapshot', JSON.stringify(snapshot));

const saved = JSON.parse(localStorage.getItem('snapshot'));
timeTravel.importSnapshot(saved);
```

**时间旅行 API**

| 方法 | 描述 | 返回值 |
|------|------|--------|
| `undo()` | 撤销到上一个状态 | `void` |
| `redo()` | 重做到下一个状态 | `void` |
| `jumpTo(index)` | 跳转到指定历史索引 | `void` |
| `getHistory()` | 获取历史记录 | `HistoryEntry[]` |
| `clearHistory()` | 清空历史记录 | `void` |
| `exportSnapshot()` | 导出当前快照 | `Snapshot` |
| `importSnapshot(snapshot)` | 导入快照 | `void` |

**使用场景**
- 游戏回放系统
- 调试和测试
- 教学演示
- 错误恢复

**注意事项**
- 每次 mutation 都会保存到历史记录
- 历史记录超过 `maxHistory` 时会自动清理最早的记录
- 仅开发环境使用，生产环境会消耗大量内存

**相关 API**
- [`store.replaceState()`](#storereplacestate) - 替换状态
- [`loggerPlugin()`](#loggerplugin) - 日志插件

---

### createStorageAdapter(type)

创建跨平台存储适配器。

**类型签名**
```typescript
function createStorageAdapter(
  type: 'h5' | 'wechat' | 'cocos' | StorageAdapter
): StorageAdapter
```

**参数**

| 参数 | 类型 | 必填 | 描述 |
|------|------|:----:|------|
| `type` | `string \| StorageAdapter` | 是 | 平台类型或自定义适配器 |

**返回值**

| 类型 | 描述 |
|------|------|
| `StorageAdapter` | 存储适配器实例 |

**示例**
```typescript
import { createStorageAdapter } from '@game-flux/core';

// 预设适配器
const h5Storage = createStorageAdapter('h5');
const wechatStorage = createStorageAdapter('wechat');
const cocosStorage = createStorageAdapter('cocos');

// 自定义适配器
const customStorage = createStorageAdapter({
  getItem(key: string): string | null {
    return myCustomStorage.get(key);
  },
  setItem(key: string, value: string): void {
    myCustomStorage.set(key, value);
  },
  removeItem(key: string): void {
    myCustomStorage.delete(key);
  }
});
```

**StorageAdapter 接口**

```typescript
interface StorageAdapter {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
}
```

**平台适配器实现**

| 平台 | `getItem` | `setItem` | `removeItem` |
|------|-----------|-----------|--------------|
| H5 | `localStorage.getItem` | `localStorage.setItem` | `localStorage.removeItem` |
| 微信小程序 | `wx.getStorageSync` | `wx.setStorageSync` | `wx.removeStorageSync` |
| Cocos Creator | `sys.localStorage.getItem` | `sys.localStorage.setItem` | `sys.localStorage.removeItem` |

**注意事项**
- 自动检测当前平台并使用合适的适配器
- 所有值都会自动进行 JSON 序列化/反序列化
- 某些平台有存储大小限制（如微信小程序 10MB）

**相关 API**
- [`persistPlugin()`](#persistplugin) - 持久化插件

---

### TypeScript 类型系统

GameFlux 提供完整的类型推导，无需手动标注类型。

#### 自动类型推导

```typescript
// state 类型自动推导
const playerModule = defineModule({
  state: { coins: 0, level: 1, items: [] as string[] },
  mutations: {
    addCoins(state, amount: number) {
      state.coins += amount;  // state: { coins: number, level: number, items: string[] }
      state.level = '10';     // TypeScript 报错：不能将 string 赋值给 number
    }
  },
  getters: {
    isRich(state) {
      return state.coins > 1000;  // 返回类型自动推导为 boolean
    }
  }
});

// 使用时也有完整类型提示
store.state.player.coins;  // 类型: number
store.getters['player/isRich'];  // 类型: boolean

// commit 时有 payload 类型检查
store.commit('player/addCoins', 100);  // 正确
store.commit('player/addCoins', '100');  // TypeScript 报错：参数类型不匹配
```

#### 显式类型标注（可选）

如果需要更严格的类型约束：

```typescript
interface PlayerState {
  coins: number;
  level: number;
  items: string[];
}

const playerModule = defineModule<PlayerState>({
  state: {
    coins: 0,
    level: 1,
    items: []
  },
  mutations: {
    // state 类型是 PlayerState，amount 需要手动标注
    addCoins(state, amount: number) {
      state.coins += amount;
    }
  }
});
```

#### 高级类型

```typescript
// ActionContext 类型
import type { ActionContext } from '@game-flux/core';

type PlayerContext = ActionContext<RootState, PlayerState>;

const actions = {
  async buyItem(context: PlayerContext, itemId: number) {
    const { state, commit, dispatch, getters } = context;
    // 所有属性都有完整类型
  }
};
```

#### 导出的类型

```typescript
// 核心类型
export type {
  Store,              // Store 实例接口
  StoreOptions,       // createStore 的配置类型
  ModuleOptions,      // defineModule 的配置类型
  MutationHandler,    // Mutation 函数类型
  ActionHandler,      // Action 函数类型
  ActionContext,      // Action 上下文类型
  GetterHandler,      // Getter 函数类型
  Subscriber,         // 订阅函数类型
  Unsubscriber,       // 取消订阅函数类型
  Plugin,             // 插件接口
  WatchCallback,      // watch 回调函数类型
  WatchOptions        // watch 选项类型
};

// 导出的函数
export {
  createStore,          // 创建 store
  defineModule,         // 定义模块
  produce               // 不可变更新助手
};

// 导出的插件
export {
  persistPlugin,        // 持久化插件
  loggerPlugin,         // 日志插件
  timeTravelPlugin,     // 时间旅行插件
  createStorageAdapter  // 创建存储适配器
};
```

## 常见问题

### Q: 为什么选择不可变更新？

**A:** 
1. **性能优化** - 可以用 `===` 快速判断状态是否变化
2. **时间旅行** - 可以随时回到历史状态
3. **调试友好** - 状态变化可追溯
4. **避免副作用** - 防止意外修改共享状态

### Q: GameFlux 的架构设计理念是什么？

**A:** GameFlux 遵循 **YAGNI（You Aren't Gonna Need It）原则**，专注游戏场景：

**架构特点**：
- **简化优于完美** - Getter 缓存使用简单的 Map，而非复杂的依赖追踪
- **类型安全优先** - `watch` 只支持函数选择器，提供完整的 TypeScript 类型推导
- **性能至上** - 避免过度抽象，直接在 Store 内部实现核心功能

**实现细节**：
```typescript
// 推荐：函数选择器（类型安全）
store.watch(
  state => state.player.coins,  // IDE 自动补全，编译时检查
  (newVal, oldVal) => console.log(newVal)
)

// 不支持：字符串路径（无类型检查，易出错）
// store.watch('player.coins', callback)
```

这种设计在保持简洁的同时，提供了游戏开发所需的所有核心功能。

### Q: 如何在小程序中使用？

**A:** GameFlux 完全兼容小程序环境：

```typescript
// 微信小程序
import { createStore, persistPlugin } from '@game-flux/core';

const store = createStore({
  modules: { player },
  plugins: [
    persistPlugin({
      storage: 'wechat'  // 自动使用 wx.storage
    })
  ]
});

// 在页面中使用
Page({
  onLoad() {
    store.subscribe(() => {
      this.setData({ playerData: store.state.player });
    });
  }
});
```

### Q: mutation 可以是异步的吗？

**A:** 不可以。mutation 必须是同步的，异步操作请使用 action：

```typescript
// 错误 - mutation 中不要用异步
mutations: {
  async addCoins(state, amount) {  // 不要这样做！
    await delay(1000);
    state.coins += amount;
  }
}

// 正确 - 在 action 中处理异步
actions: {
  async addCoinsWithDelay({ commit }, amount) {
    await delay(1000);
    commit('addCoins', amount);
  }
}
```

### Q: 如何调试状态变化？

**A:** 使用日志插件或 DevTools：

```typescript
// 方式1：日志插件
const store = createStore({
  plugins: [loggerPlugin()]
});

// 方式2：时间旅行
const store = createStore({
  plugins: [timeTravelPlugin()]
});

store.devtools.undo();  // 撤销到上一个状态
```





## 贡献

欢迎提交 Issue 和 Pull Request！


## 许可证

MIT License © 2024 GameFlux Team

---

<p align="center">
  Made for Game Developers
</p>