欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > 【CodePen实战:撤销重做功能全记录】

【CodePen实战:撤销重做功能全记录】

2025/2/13 6:00:33 来源:https://blog.csdn.net/weixin_37342647/article/details/145599128  浏览:    关键词:【CodePen实战:撤销重做功能全记录】

🛠️ CodePen实战:撤销重做功能全记录

🌟 目录

  1. 🚨 真实报错全记录 - 那些折磨我的Bug
  2. 🏗️ 极简架构设计 - 适合实验项目的结构
  3. 🧩 模块实现细节 - 关键代码解析
  4. 🚑 急救方案 - 快速Debug技巧

🚨 真实报错全记录

案例1:Vue的"温柔警告" 💛

[Vue warn]: Property "canRedo" was accessed during render but is not defined on instance.

🕵️ 现象:重做按钮偶尔消失
🔍 诊断过程

  1. 检查模板中的canRedo拼写 ✅
  2. 发现setup()中漏返回属性:
// 错误代码
setup() {const canRedo = ref(false)// ...忘记return...
}

✅ 修复

return {canRedo // 显式暴露给模板
}

案例2:幽灵报错 👻

[object Error] { message: "" }

🕵️ 现象:控制台只显示空错误对象
🔍 诊断

  1. 添加错误边界:
window.onerror = (msg) => console.log('幽灵捕获:', msg)
  1. 发现异步操作未捕获异常:
// 错误代码
setTimeout(() => { throw new Error('test') }, 0)

✅ 修复

// 所有异步操作包裹try-catch
setTimeout(() => {try { /* 操作代码 */ }catch(e) { console.error(e) }
}, 0)

案例3:重做按钮罢工 🚫

🕵️ 现象:点击重做无任何反应
🔍 诊断流程

  1. 打印历史记录栈:
console.log('历史栈:', JSON.parse(JSON.stringify(history.stack)))
  1. 发现索引越界:
当前索引: 3 | 栈长度: 3
  1. 定位到redo方法:
// 错误代码
index.value += 1 // 当index=2时变成3,而长度是3

✅ 修复

// 添加边界检查
index.value = Math.min(index.value + 1, stack.length - 1)

🏗️ 极简架构设计

系统流程图

输入/删除
撤销/重做
用户输入
操作类型
防抖300ms记录
立即响应
生成快照
获取历史状态
保存到历史栈
更新编辑器

模块职责

模块职责代码示例
输入监听捕获用户操作并防抖watch(text, debounceFn)
历史管理存储/检索编辑器状态history.push(snapshot)
状态同步保持DOM与数据一致nextTick(updateSelection)
UI控制按钮状态/快捷键处理:disabled="!canUndo"

🧩 核心模块实现

历史管理器(精简版)

class History {constructor(max = 20) {this.stack = []this.index = -1 // 当前状态索引this.max = maxthis.lock = false // 防重入锁}// 🚨 关键方法:安全推送push(state) {if (this.lock) returnthis.lock = true// 裁剪后续记录this.stack.splice(this.index + 1)// 容量控制if (this.stack.length >= this.max) {this.stack.shift()this.index = Math.max(this.index - 1, -1)}this.stack.push(JSON.parse(JSON.stringify(state)))this.index = this.stack.length - 1setTimeout(() => this.lock = false, 50)}// 🚨 关键方法:安全撤销undo() {this.index = Math.max(this.index - 1, 0)return this.get()}// 🚨 关键方法:安全重做redo() {this.index = Math.min(this.index + 1, this.stack.length - 1)return this.get()}
}

状态同步器

// 🚨 DOM与数据同步
const syncSelection = () => {// 从DOM读取editor.selection.start = textarea.value.selectionStarteditor.selection.end = textarea.value.selectionEnd// 写入DOMnextTick(() => {textarea.value.selectionStart = editor.selection.starttextarea.value.selectionEnd = editor.selection.end})
}

🚑 急救Debug指南

场景1:操作后光标错位

快速检查

  1. 是否在nextTick中更新选区?
  2. 快照是否包含selection数据?
  3. 是否存在CSS影响光标位置?

场景2:历史记录混乱

诊断步骤

// 在push方法中添加日志
console.log('推送快照:', `内容长度: ${state.content.length}`, `光标: ${state.selection.start}-${state.selection.end}`
)

场景3:移动端失效

解决方案

// 添加触摸事件监听
textarea.addEventListener('touchend', saveSelection)

📌 经验总结

  1. Vue响应式陷阱:直接存储响应式对象到历史栈会导致内存泄漏
  2. DOM时序问题:光标操作必须包裹在nextTick中
  3. 防抖重要性:300ms间隔能平衡性能和体验
  4. 边界检查:所有索引操作都要有Math.min/max保护

最后建议:在CodePen中开发时,每实现一个功能就添加console.log检查点,比调试器更高效! 🐛🔍

完整可运行代码:https://codepen.io/RichardRourc/pen/bNGGJRV?editors=1111
github 仓库完整代码:https://github.com/RichardRourc/undo-redo/blob/main/undo%26redo.html 喜欢的点个赞哇
遇到新问题?随时截图发问,我会帮你分析! 💬

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com