如何实现一个支持撤销重做的状态管理库?

来源:苹果APP网作者:石川澪头衔:网络博主
导读:本期聚焦于小伙伴创作的《如何实现一个支持撤销重做的状态管理库?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何实现一个支持撤销重做的状态管理库?》有用,将其分享出去将是对创作者最好的鼓励。

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

如何实现一个支持撤销重做的状态管理库?

核心设计思路

要实现撤销重做功能,首先需要明确几个核心概念:当前状态、历史状态栈、未来状态栈。每次状态变更时,将当前状态存入历史栈,同时清空未来栈;撤销操作从历史栈取出上一个状态作为当前状态,将原来的当前状态存入未来栈;重做操作则从未来栈取出下一个状态作为当前状态,将原来的当前状态存入历史栈。

核心数据结构

我们可以用两个数组分别存储历史状态和未来状态,再用一个变量存储当前状态,基础结构如下:

// 状态管理库基础结构
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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。