支持撤销重做的状态管理库核心是通过记录状态变更历史,让用户可以回溯或前进到指定状态,常见于编辑器、表单操作等场景。实现这类库需要关注状态存储、历史记录管理、操作触发三个核心部分。

核心设计思路
要实现撤销重做功能,首先需要明确几个核心概念:当前状态、历史状态栈、未来状态栈。每次状态变更时,将当前状态存入历史栈,同时清空未来栈;撤销操作从历史栈取出上一个状态作为当前状态,将原来的当前状态存入未来栈;重做操作则从未来栈取出下一个状态作为当前状态,将原来的当前状态存入历史栈。
核心数据结构
我们可以用两个数组分别存储历史状态和未来状态,再用一个变量存储当前状态,基础结构如下:
// 状态管理库基础结构
class UndoRedoManager {
constructor(initialState) {
// 当前状态
this.currentState = initialState;
// 历史状态栈,存储可撤销的状态
this.historyStack = [];
// 未来状态栈,存储可重做的状态
this.futureStack = [];
}
}
状态更新方法
当业务侧触发状态变更时,需要调用库提供的更新方法,该方法会将当前状态存入历史栈,再更新当前状态,同时清空未来栈,因为新的状态变更会覆盖之前的重做路径。
class UndoRedoManager {
constructor(initialState) {
this.currentState = initialState;
this.historyStack = [];
this.futureStack = [];
}
// 更新状态的方法
updateState(newState) {
// 将当前状态存入历史栈
this.historyStack.push(this.currentState);
// 更新当前状态
this.currentState = newState;
// 清空未来栈,新的变更后无法重做之前的操作
this.futureStack = [];
}
}
撤销与重做方法
撤销操作需要判断历史栈是否有内容,如果有则取出最后一个历史状态,将当前状态存入未来栈,再将取出的历史状态设为当前状态。重做操作则相反,判断未来栈是否有内容,取出第一个未来状态,将当前状态存入历史栈,再将取出的未来状态设为当前状态。
class UndoRedoManager {
constructor(initialState) {
this.currentState = initialState;
this.historyStack = [];
this.futureStack = [];
}
updateState(newState) {
this.historyStack.push(this.currentState);
this.currentState = newState;
this.futureStack = [];
}
// 撤销操作
undo() {
// 历史栈为空则无法撤销
if (this.historyStack.length === 0) {
return false;
}
// 取出历史栈最后一个状态
const prevState = this.historyStack.pop();
// 当前状态存入未来栈
this.futureStack.push(this.currentState);
// 更新当前状态为历史状态
this.currentState = prevState;
return true;
}
// 重做操作
redo() {
// 未来栈为空则无法重做
if (this.futureStack.length === 0) {
return false;
}
// 取出未来栈第一个状态
const nextState = this.futureStack.pop();
// 当前状态存入历史栈
this.historyStack.push(this.currentState);
// 更新当前状态为未来状态
this.currentState = nextState;
return true;
}
}
功能扩展
基础版本的状态管理库可以满足简单场景需求,实际使用中还可以扩展更多实用功能。
状态变更监听
很多场景下业务侧需要在状态变更、撤销、重做时触发对应的回调,我们可以增加监听机制,支持注册回调函数,在对应操作执行后触发。
class UndoRedoManager {
constructor(initialState) {
this.currentState = initialState;
this.historyStack = [];
this.futureStack = [];
// 存储监听回调的数组
this.listeners = [];
}
// 注册监听回调
subscribe(listener) {
this.listeners.push(listener);
// 返回取消订阅的方法
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
// 触发所有监听回调
_notify() {
this.listeners.forEach(listener => {
listener(this.currentState, this.canUndo(), this.canRedo());
});
}
updateState(newState) {
this.historyStack.push(this.currentState);
this.currentState = newState;
this.futureStack = [];
// 状态更新后触发监听
this._notify();
}
undo() {
if (this.historyStack.length === 0) {
return false;
}
const prevState = this.historyStack.pop();
this.futureStack.push(this.currentState);
this.currentState = prevState;
// 撤销后触发监听
this._notify();
return true;
}
redo() {
if (this.futureStack.length === 0) {
return false;
}
const nextState = this.futureStack.pop();
this.historyStack.push(this.currentState);
this.currentState = nextState;
// 重做后触发监听
this._notify();
return true;
}
// 判断是否可以撤销
canUndo() {
return this.historyStack.length > 0;
}
// 判断是否可以重做
canRedo() {
return this.futureStack.length > 0;
}
}
历史记录上限控制
如果状态变更非常频繁,历史栈会无限增大,占用过多内存,我们可以增加历史记录上限的配置,当历史栈长度超过上限时,移除最早的历史状态。
class UndoRedoManager {
constructor(initialState, maxHistory = 50) {
this.currentState = initialState;
this.historyStack = [];
this.futureStack = [];
this.listeners = [];
// 最大历史记录数,默认50条
this.maxHistory = maxHistory;
}
subscribe(listener) {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
_notify() {
this.listeners.forEach(listener => {
listener(this.currentState, this.canUndo(), this.canRedo());
});
}
updateState(newState) {
this.historyStack.push(this.currentState);
// 超过最大历史数,移除最早的记录
if (this.historyStack.length > this.maxHistory) {
this.historyStack.shift();
}
this.currentState = newState;
this.futureStack = [];
this._notify();
}
undo() {
if (this.historyStack.length === 0) {
return false;
}
const prevState = this.historyStack.pop();
this.futureStack.push(this.currentState);
this.currentState = prevState;
this._notify();
return true;
}
redo() {
if (this.futureStack.length === 0) {
return false;
}
const nextState = this.futureStack.pop();
this.historyStack.push(this.currentState);
this.currentState = nextState;
this._notify();
return true;
}
canUndo() {
return this.historyStack.length > 0;
}
canRedo() {
return this.futureStack.length > 0;
}
}
使用示例
下面是一个简单的文本编辑器场景的使用示例,通过状态管理库实现文本内容的撤销重做功能。
// 初始化状态管理库,初始文本为空
const textManager = new UndoRedoManager('', 20);
// 注册监听,状态变更时更新页面显示
textManager.subscribe((state) => {
document.getElementById('editor').value = state;
document.getElementById('undoBtn').disabled = !textManager.canUndo();
document.getElementById('redoBtn').disabled = !textManager.canRedo();
});
// 文本输入时触发状态更新
document.getElementById('editor').addEventListener('input', (e) => {
textManager.updateState(e.target.value);
});
// 撤销按钮点击事件
document.getElementById('undoBtn').addEventListener('click', () => {
textManager.undo();
});
// 重做按钮点击事件
document.getElementById('redoBtn').addEventListener('click', () => {
textManager.redo();
});
通过以上实现,我们就完成了一个支持撤销重做的通用状态管理库,开发者可以根据自身业务需求进一步扩展功能,比如增加状态快照、合并连续操作等,适配更复杂的场景。
状态管理撤销重做设计模式JavaScript数据结构修改时间:2026-07-15 04:39:30