实现一个完整的交互闭环
本课交付:能够解释保存失败的完整交互。
核心方法
完整交互需要把输入、校验、保存和显示连接起来。本课解释资料包中可运行的 app.js,重点是数据何时改变、失败时保留什么、界面从哪里获得真实状态。不要将正文片段直接当作完整脚本;完整实现和事件处理都在资料包中。
本课的成果
运行新增、勾选、删除和刷新流程,理解数据模型与关键函数,并记录一次成功保存和一次失败处理。示例按单个活动页面使用,不提供多标签并发或跨设备同步。
案例拆解
第一步:定义唯一的数据来源
const book = {
id: 'example-001',
title: '我的第一本书',
done: false
};
let books = [];每条记录用 ID 区分,不能用书名当唯一键,否则两本同名记录可能一起被删除。books 保存当前有效数据;列表和计数从它计算,不另外维护一个容易失去同步的“总数”。
第二步:校验用户输入与存储数据
function validTitle(title) {
return typeof title === 'string' && title.trim() === title &&
Array.from(title).length >= 1 && Array.from(title).length <= 80;
}提交时先 trim() 去除首尾空格,再使用此函数。读取本机数据时还要检查是不是数组、是否超过 100 条、每条 ID 是否有效且唯一、done 是否为布尔值。JSON.parse() 成功只说明 JSON 语法有效,不说明内容符合应用要求。
第三步:先确认写入成功,再改变应用状态
function save(next) {
try {
localStorage.setItem(KEY, JSON.stringify(next));
books = next;
return true;
} catch {
say('保存失败,本次修改未生效。请检查浏览器存储权限或可用空间。', true);
return false;
}
}KEY 和 say() 在完整文件中定义。这里先写存储,再更新 books;因此浏览器拒绝写入时,旧状态仍然成立。提交事件只有在 save() 返回 true 后才清空输入、重新渲染并提示成功。勾选失败则重新显示旧状态,不留下一个没有保存的视觉结果。
第四步:把用户文字当作文字
const title = document.createElement('span');
title.textContent = book.title;不要把书名插入 innerHTML 拼出的标签中。用户可能输入看起来像 HTML 的内容;它应该作为书名文字显示,而不是执行为页面结构或脚本。textContent 用于设置文本,这个选择减少了此处的 HTML 注入风险;它不代表应用的所有安全问题都自动解决。MDN:textContent ↗
第五步:理解事件如何改变数据
新增用数组展开追加新记录;勾选用 map() 产生对应记录已修改的新数组;删除用 filter() 产生不包含该 ID 的数组。三种操作都经过同一个保存入口,再调用 render(),这样失败反馈和存储规则不会散落在多个位置。
const next = books.map(book =>
book.id === id ? { ...book, done: checkbox.checked } : book
);
if (save(next)) say('阅读状态已保存。');
render();完整脚本还会把焦点放回对应复选框。重新创建 DOM 元素后,原来的焦点节点已不存在,需要主动维持键盘操作的连续性。
第六步:处理加载损坏的情况
读取失败时设置 blocked = true,禁用新增并提示原记录未被覆盖。用户确认清空后,只有成功保存空数组才解除阻止。这比在 catch 中悄悄写入空数组更容易保护可能仍可恢复的数据。
提示词与使用说明
请解释以下脚本的数据流,不要先重写。
从一次新增开始,指出输入校验、存储写入、状态更新、DOM 渲染
和成功提示的实际顺序。再模拟 setItem 抛出异常时的每一步。
检查是否存在“提示成功但没有保存”或“保存失败仍改变状态”。
仅依据我提供的代码判断,缺失函数请明确指出。
[粘贴资料包完整 app.js]独立练习
先把答案写进本课笔记,再展开解析。以下人物、活动和数据均为教学用的虚构材料;练习记录请填写你实际完成的过程。
给定材料
现有流程:先提示“保存成功”,再调用 localStorage.setItem;没有捕获异常。
浏览器可能禁用存储,或已有内容不是合法 JSON。你的任务
重新排列读取、校验、修改、保存、反馈的顺序。写出解析失败与写入失败各怎么处理,不把坏记录直接覆盖为空数组。
完成后,对照参考解析
读取时 try/catch 处理异常,再验证记录结构;损坏记录要提示并保留恢复机会。用户修改后尝试写入,写成功才提示已保存;本课示例采用先写入再更新列表的顺序:写失败时保留原列表,提示“保存失败,本次修改未生效”,并保留输入供重试。不要声称未生效的修改已经保存,或能从列表中导出。localStorage 按来源隔离,不自动跨设备;file: URL 的行为不可作为统一保证。请通过本地 HTTP 服务验证。
验收与迁移
对照自己的成果逐项检查。未达到的项目,写明缺什么、怎样补;这些标准用于自查,不会自动标记课时完成。
- 成功提示在写入成功之后
- 读取失败没有自动销毁旧记录
- 明确区分当前页面状态与持久化状态
带回自己的场景
主动模拟一次存储失败,而非只测正常新增。线上小概率故障通常藏在未测试的异常分支里。
延伸资料
浏览器存储的使用与限制 ↗。资料用于核对方法和技术边界,案例与练习由 Nextory 编写。