自动保存逻辑必须外置,因<template>不执行JS、不触发事件;克隆后需手动绑定input事件、生成唯一键名、正确恢复contenteditable内容,并在beforeunload中仅同步写localStorage。

用 <template> 封装时,自动保存逻辑必须外置
<template> 本身不执行 JS、不触发事件、不参与渲染流程,它只是个离线容器。你把 input 放进 <template>,再克隆出来,oninput 属性不会生效,addEventListener 也得手动加——不是“封装完就自带保存”,而是“封装后还得补逻辑”。
- 别在
<template>内写<script>或内联事件(如onclick),它们不会执行 - 克隆后必须遍历所有可编辑元素,逐个绑定
input事件和防抖存逻辑 - 每个实例要生成唯一键名,比如
draft-${Date.now()}-${Math.random().toString(36).slice(2, 9)},否则多个同模板实例互相覆盖 - 如果模板含
contenteditable元素,恢复时不能用.value,得用.textContent或.innerText,且需手动触发input事件让框架感知变化
customElements.define() 组件里怎么安全存草稿
自定义元素适合需要样式隔离、状态封装的场景,但自动保存不能只靠组件内部逻辑——localStorage 是全局的,组件实例之间会冲突,必须靠上下文隔离。
- 在
connectedCallback中初始化保存键:用this.id || this.getAttribute('data-key') || 'default'生成前缀 - 监听输入时,只处理当前 shadowRoot 内的字段:
this.shadowRoot.querySelectorAll('input, textarea, select') - 恢复草稿不能在
constructor里做,DOM 还没挂载;要在connectedCallback或首次render()后延时执行(requestIdleCallback或setTimeout(..., 0)) - 对 checkbox/radio 恢复时,必须写成
el.checked = storedValue === 'true',因为localStorage只存字符串,Boolean('false')是true
表单字段动态增删时,自动保存怎么不漏掉新字段
用户点击“添加一行”后插入的新 input,如果不重新绑定监听,后续输入根本不会存——这不是 bug,是监听器没挂上去的自然结果。
- 每次动态插入字段后,立刻调用统一的绑定函数,比如
bindAutoSave(el.querySelector('input')) - 避免重复绑定:给已处理的元素加
data-autosave-bound="true"属性,检查后再绑 - 富文本编辑器(如 Quill)不能只监听原生
input,得用其自有 API,例如quill.on('text-change', () => saveDraft()) - 删除字段时,记得从草稿对象中同步删掉对应 key,否则恢复时会填空值或报错
beforeunload 里只能干一件事:同步写 localStorage
很多人以为 beforeunload 是“最后保险”,其实它只有约 50ms 执行窗口,且禁止异步操作。任何 await、fetch、甚至封装过的 saveDraft() 函数,只要内部有 Promise,就必然失效。
立即学习“前端免费学习笔记(深入)”;
- 只在
input事件中持续更新一个全局标记window.isDirty = true -
beforeunload回调里只做:if (window.isDirty) localStorage.setItem(key, JSON.stringify(draft)) - 不要在这里调用任何带
try/catch或日志打印的函数——哪怕只多一行 console.log,都可能超时 - 测试必须关掉标签页验证,
location.reload()不触发beforeunload
mounted、React 的 useEffect、原生的 requestIdleCallback,选错时机,存了也白存。



















