草稿到底该存哪:浏览器存储的三个选择
给网页增加“自动保存草稿”时,最省事的做法通常是把内容转成 JSON,塞进 localStorage。文本不多、写入不频繁时,这个方案很实用。但当草稿包含大量记录、图片和附件,或者页面每输入一个字就保存一次,原来的方便可能逐渐变成卡顿和维护负担。
选择浏览器存储,可以先问三个问题:数据需要保留多久,体积大不大,是否需要按字段查询。把这些条件说清楚,通常就能确定大致方向。
localStorage 适合小规模、需要跨会话保留的简单数据,例如界面偏好和少量草稿。它以字符串保存键值,并且读写是同步的。体积较大的序列化和频繁写入会占用主线程,因此应控制数据大小,把连续输入合并后再保存。读取时还要考虑旧版本格式损坏或者用户手动修改过内容。
function loadDraft() {
try {
const raw = localStorage.getItem('editor-draft-v1');
if (raw === null) return '';
const value = JSON.parse(raw);
return typeof value.text === 'string' ? value.text : '';
} catch {
return '';
}
}
function saveDraft(text) {
try {
localStorage.setItem('editor-draft-v1', JSON.stringify({ text }));
return true;
} catch {
return false;
}
}
这里保存失败时返回了明确结果,界面可以据此提示“本地保存失败”。不要在捕获异常后继续显示“已保存”,否则用户可能在关闭页面后才发现内容丢失。示例把无法解析的草稿视作空内容,真实编辑器如果需要恢复重要文字,可以提供导出或恢复入口,避免直接覆盖已有数据。
sessionStorage 的接口相似,但存储范围与当前标签页会话相关,刷新页面通常仍能保留,关闭对应标签页后通常结束。它适合多步骤表单中的临时状态。通过复制标签页或带打开者关系创建页面时,初始内容可能被复制,后续又分别变化,因此不要把它当作跨标签页同步机制。
当数据较多,或者需要保存结构化对象、Blob、按索引查询时,可以使用 IndexedDB。它采用异步接口,并提供事务。代价是初始化、版本升级和错误处理更复杂,因此并非保存一个主题颜色也需要引入它。
下面假设数据库连接已经打开,并且在升级事务中创建了以 id 为主键的 drafts 对象仓库。保存时应以事务完成作为成功依据:
function saveToDatabase(db, draft) {
return new Promise((resolve, reject) => {
const tx = db.transaction('drafts', 'readwrite');
tx.objectStore('drafts').put(draft);
tx.oncomplete = () => resolve();
tx.onabort = () => reject(tx.error ?? new Error('草稿保存已中止'));
});
}
单个写入请求成功,并不等于整个事务最终成功。也不要在活跃事务中随意等待网络请求:事务可能在等待期间结束。通常先准备好要写入的数据,再开启事务,完成相关数据库操作。
这三种存储都受来源、浏览器策略和容量限制影响,也可能被用户清理。隐私模式和受限环境的表现还可能不同。重要内容如果需要跨设备恢复,仍然应提供服务器同步或用户可控的导出能力,不能把浏览器存储视为永久备份。
安全方面,同源页面中的恶意脚本可能读取这些数据。把敏感凭证换一个存储位置,并不能解决脚本注入问题;退出登录时,也要处理与账号有关的本地内容,避免下一位使用者看到上一个账号的草稿。
验证时,除了刷新页面,还应测试两个标签页、关闭重开、存储写入失败,以及旧版本数据的读取。对于编辑器,尤其要观察界面的保存状态是否与实际结果一致。选对存储方式的价值,不只是成功写入一次,更在于用户能准确知道内容保存在什么地方、何时可能失效。
- 点赞
- 收藏
- 关注作者
评论(0)