From 5a6c3891b51fa720957ad6e92c51c3ebb5813141 Mon Sep 17 00:00:00 2001
From: unanmed <1319491857@qq.com>
Date: Tue, 20 Oct 2026 14:59:45 +0800
Subject: [PATCH] =?UTF-8?q?docs(10-18):=20=E6=94=B6=E5=8F=A3=E9=AB=98?=
=?UTF-8?q?=E7=BA=A7=20I=20=E4=BA=92=E9=93=BE=E4=B8=8E=E6=9E=84=E5=BB=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/advanced/data-stores.md | 2 ++
docs/advanced/declarative-vs-imperative.md | 12 +++++-------
docs/advanced/hooks-in-depth.md | 2 ++
docs/advanced/plugin-system.md | 6 +++---
docs/advanced/replay-consistency.md | 2 ++
docs/advanced/saveable-content.md | 4 +++-
6 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/docs/advanced/data-stores.md b/docs/advanced/data-stores.md
index 528a8ae..1097ea9 100644
--- a/docs/advanced/data-stores.md
+++ b/docs/advanced/data-stores.md
@@ -282,3 +282,5 @@ console.log(raw, state.tileStore.getType(num));
- `EnemyStore` 不在 `IDataCommon` 上,不要假设主对象能直接取到。
- Store 注册没有逐对象模板,只有「在加载期集中登记」的机制骨架。
- 不懂代码时优先在已有位置补条目,不要急着写新类或扩展主对象。
+
+返回[高级 I 导览](./index.md)。
diff --git a/docs/advanced/declarative-vs-imperative.md b/docs/advanced/declarative-vs-imperative.md
index 861f489..20b5314 100644
--- a/docs/advanced/declarative-vs-imperative.md
+++ b/docs/advanced/declarative-vs-imperative.md
@@ -108,7 +108,7 @@ hero.hp = hero.hp + 50;
引擎在哪个时机执行:存档与读档时。你只需要在初始化阶段注册一次,例如 `state.addSaveableContent('@my/xxx', content)`,之后引擎会在存档、读档时按注册表逐个调用。
-为什么这比命令式更稳:存与读写在同一个接口里,不会出现「存的时候漏了一个字段、读的时候又忘了恢复」。注册一次之后,压缩级别、调用顺序、跨对象引用都由存档系统统一管理。存档的完整约束会在 `高级教程 I · 存档一致性` 中展开。
+为什么这比命令式更稳:存与读写在同一个接口里,不会出现「存的时候漏了一个字段、读的时候又忘了恢复」。注册一次之后,压缩级别、调用顺序、跨对象引用都由存档系统统一管理。存档的完整约束见[可存档内容](./saveable-content.md)。
### 钩子
@@ -190,12 +190,10 @@ calculate(handler) {
- 只在该时刻生效、且不需要回放的脚本:如果一段脚本的效果本来就只应该发生一次,写成命令式没有问题。
- 需要立即得到返回值再决定下一步的流程:比如先判断能否使用道具,再决定是否扣除数量。
-但要记住一条边界:这些入口动作必须计入录像。玩家按下的操作进入录像,回放时重新走一遍命令式接口,派生结果再由声明式规则重算。录像一致性的具体要求见 `高级教程 I · 录像一致性`。
+但要记住一条边界:这些入口动作必须计入录像。玩家按下的操作进入录像,回放时重新走一遍命令式接口,派生结果再由声明式规则重算。录像一致性的具体要求见[录像一致性](./replay-consistency.md)。
可以用一句话概括:一次性动作用命令式,持续生效的规则用声明式。
-
-
## 模板:声明式扩展的通用骨架
下面给的是机制本身的通用骨架,不对应任何一种具体效果。具体效果只用来帮助理解,不要把它当成模板照抄。
@@ -255,12 +253,10 @@ state.someSystem.useXxx(new XxxHandler(state));
- 你要替换整套策略,例如整条伤害公式都换掉;
- 你要新增一组可独立复用的扩展,例如一个新的光环转换器;
-- 你要新增一个系统。此时优先通过插件实现,而不是直接改内置包,见 `高级教程 I · 插件系统`。
+- 你要新增一个系统。此时优先通过插件实现,而不是直接改内置包,见[插件系统](./plugin-system.md)。
如果你不怎么懂代码,建议直接改内置,而不是自己写新的类。自己写类要同时处理接口签名、注册时机与上下文刷新,改动面比在内置方法里加一个分支大得多,出错也更难排查。
-
-
## 常见误区
- 把声明式当成不用写逻辑。声明式只是把「什么时候执行」交给引擎,「怎么算」仍然要你写清楚。
@@ -306,3 +302,5 @@ if (system && info) {
- 持续生效的规则用声明式;玩家直接产生的一次性动作用命令式,但要计入录像。
- 计算类扩展必须无副作用;状态修改放到引擎规定的时机里。
- 改内置优先;确实需要独立扩展时再自己写类,新增系统优先考虑插件。
+
+返回[高级 I 导览](./index.md)。
diff --git a/docs/advanced/hooks-in-depth.md b/docs/advanced/hooks-in-depth.md
index dda80a4..8151145 100644
--- a/docs/advanced/hooks-in-depth.md
+++ b/docs/advanced/hooks-in-depth.md
@@ -433,3 +433,5 @@ map.addHook(new TargetLayerResizeHooks(state, targetLayer));
- 不要用 alias 判断身份;对象引用才唯一。
- `addHook` 返回控制器,`removeHook` 要传原实例,控制器可 `unload`;`awake` 在加入已加载列表前调用,`destroy` 在移除时调用。
- `addHook` 不去重,`forEachHook` 顺序不保证,异步钩子用 `Promise.all` 并发等待。
+
+返回[高级 I 导览](./index.md)。
diff --git a/docs/advanced/plugin-system.md b/docs/advanced/plugin-system.md
index 8f7c99d..49ca1ba 100644
--- a/docs/advanced/plugin-system.md
+++ b/docs/advanced/plugin-system.md
@@ -293,12 +293,10 @@ export class MyPlugin implements IDataPluginDefine {
1. 类名与 id:`MyPlugin` 与 `my-plugin` 换成你的名字,`id` 全局唯一。
2. dependencies:填依赖插件的 id 字符串;没有就保持空数组,不要填插件对象。
-3. awake 里的装配:换成你自己的对象。需要进存档的对象必须实现 `ISaveableContent`(`saveState` / `loadState`),细节见 `高级教程 I · 可存档内容`。
+3. awake 里的装配:换成你自己的对象。需要进存档的对象必须实现 `ISaveableContent`(`saveState` / `loadState`),细节见[可存档内容](./saveable-content.md)。
4. 存档 id:`@plugin/my-plugin` 换成你自己的存档 id,与插件 id 分开命名。
5. initialize / afterLoad:用不到就留空方法;用到时按需加逻辑。
-
-
注意:`awake` 里的异步工作不会被等待;`initialize` 与 `afterLoad` 里启动的异步任务必须 `await`,否则阶段会提前结束。注册时只改 `list.ts` 的数组,不要另外找地方调用插件方法。
## 示例:读一个现有插件
@@ -449,3 +447,5 @@ console.log(holder?.status === PluginStatus.Loaded, holder?.plugin);
- 三个阶段都按依赖顺序走;循环依赖、缺失依赖会连同下游一起被阻断。
- 插件 id 是对外契约,不要随意改名或复用;它与可存档 id 是两套命名。
- 优先改内置;需要独立成组的功能时再写插件。
+
+返回[高级 I 导览](./index.md)。
diff --git a/docs/advanced/replay-consistency.md b/docs/advanced/replay-consistency.md
index cebfbf8..e427752 100644
--- a/docs/advanced/replay-consistency.md
+++ b/docs/advanced/replay-consistency.md
@@ -262,3 +262,5 @@ endReplaySafetyCollection();
- 收集区间目前只有测试在开启;需要时手动 `beginReplaySafetyCollection` / `endReplaySafetyCollection`。
- 读档期间录像被临时禁用,执行器不在保护范围内。
- 不懂代码时优先改内置加一行标记,不要急着写新类。
+
+返回[高级 I 导览](./index.md)。
diff --git a/docs/advanced/saveable-content.md b/docs/advanced/saveable-content.md
index 9101d6b..29bec78 100644
--- a/docs/advanced/saveable-content.md
+++ b/docs/advanced/saveable-content.md
@@ -173,7 +173,7 @@ export interface ISaveableExecutor {
关键在时机:执行器在该对象的 `loadState` 返回后立即运行,不是等所有对象都读完之后统一运行。此时它前面的对象已经恢复完毕,后面的对象还没开始。这个顺序直接决定了跨对象引用该怎么解析。
-还有一处与录像有关的细节:读档开始时,主对象会先清掉残留的录像禁用标记,再逐个恢复对象。每个对象的 `loadState` 都被 `replaySystem.disable()` 与 `revert()` 包裹,读档本身不会被记进录像;但执行器不在包裹范围内,因为它可能确实要做需要记录的行为。在执行器里操作会影响成绩的状态时要谨慎,录像一致性的完整规则见 `高级教程 I · 录像一致性`。
+还有一处与录像有关的细节:读档开始时,主对象会先清掉残留的录像禁用标记,再逐个恢复对象。每个对象的 `loadState` 都被 `replaySystem.disable()` 与 `revert()` 包裹,读档本身不会被记进录像;但执行器不在包裹范围内,因为它可能确实要做需要记录的行为。在执行器里操作会影响成绩的状态时要谨慎,录像一致性的完整规则见[录像一致性](./replay-consistency.md)。
### afterLoad 的两种含义
@@ -416,3 +416,5 @@ console.log(content);
- 插件 `afterLoad` 只在初次加载后运行一次,不能替代读档执行器。
- 跨对象引用存 id 或键,读档时按顺序或延迟解析,不要存活对象引用。
- 不懂代码时优先改内置的 `saveState` / `loadState`,不要急着写新类。
+
+返回[高级 I 导览](./index.md)。