模块销毁必须在路由离开前手动触发:laydate需调用instance.remove(),layer需用end回调关闭并通知子页onDestroy,table需table.destroy()或reload清空状态,全局监听器和定时器须集中管理并显式清除。

模块销毁必须在路由离开前手动触发,不能等页面卸载
SPA 中用户不会真正“刷新页面”,beforeunload 或 unload 事件极少触发,且不可靠——尤其是 Vue/React 路由跳转、前进后退、甚至地址栏手动改路径时,这些事件根本不会执行。Layui 模块(如 laydate、layer、table)内部持有 DOM 引用、定时器、全局事件监听,若不主动销毁,会持续占用内存并导致后续重复初始化异常。
正确做法是:在路由守卫(如 Vue Router 的 beforeRouteLeave、React Router 的 useEffect cleanup)中显式调用对应模块的销毁方法,而非依赖浏览器生命周期钩子。
-
laydate.getInst(elem)存在时必须调用instance.remove(),否则日期面板残留、重复绑定 -
layer.open()返回的索引需保存,离开前用layer.close(index)关闭;若为type: 2(iframe),还需主动通知子页执行onDestroy -
table.render()返回的实例应缓存,销毁时优先用table.destroy(id)(Layui 2.9+ 支持),旧版则用table.reload(id, { data: [] })+ 手动清空分页状态
layer 弹层销毁必须用 end 回调,cancel 和 beforeClose 都不安全
end 是唯一在弹层 DOM 彻底移除、所有资源释放完毕后执行的钩子。很多开发者误用 cancel(只响应右上角 ×)或 beforeClose(关闭前拦截,DOM 尚未清理),结果导致 sessionStorage 清理失败、定时器未清除、Vue 实例仍在运行。
特别是 iframe 类型弹层:end 中不仅要调用 layer.close(),还必须尝试调用子页暴露的 onDestroy 函数:
layer.open({
type: 2,
content: '/form.html',
end: function() {
const iframe = layero.find('iframe')[0];
try {
iframe.contentWindow.onDestroy?.();
} catch (e) {}
sessionStorage.removeItem('tempFormState');
}
});
注意:跨域 iframe 会抛 SecurityError,同域但子页未定义 onDestroy 会报 undefined is not a function,所以必须加 try/catch 和可选链 ?.()。
table 表格销毁要连带清空分页、合计、搜索框,不能只清 data
仅执行 table.reload('id', { data: [] }) 会导致分页器仍显示“第 3 页 / 共 12 页”、合计栏残留旧数值、toolbar 中的搜索 input 未清空——因为这些状态不在 data 里,而是存在 table.config 和 DOM 属性中。
完整清空步骤如下:
- 调用
table.reload('id', { data: [], page: { curr: 1 }, totalRow: false }) - 手动重置搜索框:
$('input[lay-key="search"]').val('').trigger('input') - 若需保留合计栏但清空内容,进
done回调里操作$('.layui-table-total td').text('') - 多表格场景建议封装函数,传入 ID 数组循环调用,避免遗漏
全局监听器和定时器必须集中管理,避免匿名函数注册
Layui 自身不接管全局事件生命周期,你手动加的 document.addEventListener('click', handler) 或 setInterval 在组件卸载后依然存活。常见错误是写成匿名函数:
document.addEventListener('keydown', function(e) { if(e.code === 'Escape') layer.closeAll(); });
这会导致无法 removeEventListener,只能靠 AbortController 或变量引用管理:
const abortCtrl = new AbortController();
document.addEventListener('keydown', handler, { signal: abortCtrl.signal });
// 路由离开前
abortCtrl.abort(); // 自动解绑所有关联监听器
定时器同理:所有 setTimeout/setInterval 的返回值必须保存,离开前统一 clearTimeout/clearInterval。别信“SPA 会自动回收”——只要引用链没断,GC 就不会碰它。











