跳转到内容

DevTools

DevTools 用于观察状态变化、回放历史并导出调试数据。

  • 核心能力:时间旅行、Patch/Snapshot Diff、数据导出。
  • 组成:@iostore/devtools(核心能力)+ @iostore/devtools-react(可视化面板)。
  • 接入方式:可直接创建实例,也可通过行为扩展统一纳管。
  • 需要定位复杂状态更新来源。
  • 需要回放用户操作路径排查问题。
  • 需要导出状态轨迹给测试或排障流程。
import { io } from '@iostore/store';
import { createIoDevtools } from '@iostore/devtools';
const store = io({ counter: 0, user: { name: 'Ada' } });
const devtools = createIoDevtools(store, {
name: 'IO Store',
maxHistory: 200,
captureSnapshots: 'always',
perf: { enabled: true, windowSize: 60 },
});
import { IoDevtoolsPanel, IoDevtoolsErrorBoundary } from '@iostore/devtools-react';
import '@iostore/devtools-react/styles.css';
export function DevtoolsDock({ devtools }) {
return (
<IoDevtoolsErrorBoundary>
<IoDevtoolsPanel devtools={devtools} height={420} />
</IoDevtoolsErrorBoundary>
);
}

IoDevtoolsPanel 依赖基础样式文件,建议在应用入口或面板组件处引入:

import '@iostore/devtools-react/styles.css';

你可以通过 CSS 变量定制主题:

.io-devtools-panel {
--io-devtools-panel-bg: #0b1020;
--io-devtools-surface: #111827;
--io-devtools-text: #e5e7eb;
--io-devtools-muted: #9ca3af;
--io-devtools-border: #334155;
}
Todo List

Based on the example todo list, connected to IO DevTools.

1 left
Learn IO
Ship a demo
Devtools
Cursor: -1
History: 0
Avg: 0.00ms
Timeline
Details
Select an entry to inspect diffs.

你可以直接触发更新、时间旅行和导出,体验最小化的 DevTools 接入链路。

当前 count:0
历史记录长度:0
最近导出大小:0 chars
import { useState, useSyncExternalStore } from 'react';
import { createIoDevtools } from '@iostore/devtools';
import { io } from '@iostore/store';
import { devtools, withBehaviors } from '@iostore/store/behavior';
const store = io({ count: 0 });
const view = withBehaviors(store, [
devtools({
target: store,
create: (target) =>
createIoDevtools(target, {
name: 'IO Behavior Live Demo',
captureSnapshots: 'always',
maxHistory: 80,
}),
}),
]);
function DevtoolsMinimalLiveDemo() {
const snapshot = useSyncExternalStore(
store.subscribe,
store.snapshot,
store.snapshot,
);
const [exportBytes, setExportBytes] = useState(0);
const dt = view.extensions?.devtools;
return (
<section className="io-live">
<div>当前 count:{snapshot.count}</div>
<div>历史记录长度:{dt?.getState().history.length ?? 0}</div>
<div>最近导出大小:{exportBytes} chars</div>
<div className="io-live__row">
<button type="button" className="io-live__button" onClick={() => store.count.set((n) => n + 1)}>
+1
</button>
<button type="button" className="io-live__button" onClick={() => dt?.timeTravel.undo()}>
undo
</button>
<button type="button" className="io-live__button" onClick={() => dt?.timeTravel.redo()}>
redo
</button>
<button
type="button"
className="io-live__button"
onClick={() => setExportBytes(dt?.export.json().length ?? 0)}
>
export json
</button>
</div>
</section>
);
}
export default DevtoolsMinimalLiveDemo;

观察点:withBehaviors(devtools(...)) 能把 DevTools 实例挂到 view.extensions,并统一管理生命周期。

  • devtools.timeTravel.undo()
  • devtools.timeTravel.redo()
  • devtools.timeTravel.goTo(index)
  • Patch diff:来自 update.patches
  • Snapshot diffcaptureSnapshots: 'always' 时可用
  • Timeline label:优先显示 update.action,无 action 时回退到 patch 摘要
const json = devtools.export.json();
const reduxImportState = devtools.export.reduxDevToolsImport();

devtools() 行为本身不会增加新的调试能力。
调试能力(时间旅行、Diff、导出、面板数据)来自 createIoDevtools(...) 创建的实例。

它的作用是把这个实例接入 withBehaviors() 体系,获得一致的工程化体验:

  • 挂载到 view.extensions.devtools(或自定义 key)统一访问
  • 跟其他行为(如 persistschedule)一致地组合
  • view.destroy() 时自动清理 devtools.destroy()

如果你不需要 withBehaviors() 的统一组合和生命周期管理,直接调用 createIoDevtools(store, options) 也完全可以。

import { devtools, withBehaviors } from '@iostore/store/behavior';
import { createIoDevtools } from '@iostore/devtools';
import { io } from '@iostore/store';
const store = io({ count: 0 });
const view = withBehaviors(store, [
devtools({ target: store, create: createIoDevtools }),
]);
const dt = view.extensions?.devtools;
  • 未开启快照却期望完整 Snapshot Diff(需 captureSnapshots: 'always')。
  • maxHistory 过大导致内存占用上升。
  • 只看面板不看 patch/action 上下文,定位效率低。
  • 开发环境默认开启 DevTools,生产环境按需开启。
  • 对关键交互补充 update.action,提高时间线可读性。
  • 结合性能采样配置(perf)持续观察高频路径。