组件卸载后,哪些东西还在工作
消失的是组件,不一定是它创建的工作。从动画帧、事件监听到网络请求,给每个开始安排一个结束,才能让交互界面长期保持稳定。
向下阅读
一个面板关掉之后,画面看起来已经安静了。但如果它创建的动画帧还在运行、事件还在监听、请求还在等待,消失的只是 DOM,不是这段功能占用的工作。
这类问题经常在第一次打开时不明显。反复打开、关闭,或者在几个页面之间来回切换之后,重复监听、旧请求和闲置绘制才逐渐显露。界面需要考虑的时间范围,比一次成功展示更长。
从“谁创建”开始列清单
组件可能创建动画时间轴、浏览器监听、观察器、定时器、图形上下文和网络请求。它们都可以由一个组件启动,但并不因为这个组件不再可见就自动结束。
一个简单的方法,是在实现时为每个开始写下对应的结束:添加监听之后在哪里移除,申请动画帧后如何取消,创建观察器后何时断开,发起请求后谁负责中止。先列出所有权,再决定清理放在哪个生命周期中。
只有返回一个清理函数,也不意味着工作已经完整结束。回调闭包里可能还保存旧状态,另一个作用域可能重新启动任务;依赖变化也可能让同一段初始化再次发生。创建与释放需要针对实际对象,而不是凭借“已经写了 cleanup”获得安心。
隐藏与卸载不同
有些面板关闭后会卸载内容,适合直接释放内部动画和图形资源。有些播放器必须跨页面保留,不能因为某个页面消失就销毁真正发声的节点。这两种需求需要不同的所有者。
全站会话可以留在长期存在的组件中,面板只负责显示。这样,关闭助手不会抹掉对话,关闭可视化也可以结束绘制,而不会顺手清空属于整个站点的状态。
页面暂时隐藏,又是另一种情况。标签页不在前台时,内容仍然存在,继续高频绘制却很少有意义。可以在可见性变化时停止循环,回来之后重新启动,并避免把隐藏期间的全部时间差一次性加入动画。
尺寸变化也会留下工作
Canvas 或图形组件经常需要观察容器尺寸。观察器应该跟着容器走,而不是随着每次渲染重复添加。尺寸变化后更新绘制区域,卸载时断开,才能避免已经移除的对象继续收到通知。
事件监听同样需要稳定的函数引用。添加和移除的目标、类型与回调应当一致,否则清理可能看起来执行了,实际却没有移除原来的监听。
这些细节看起来琐碎,但它们决定一个组件能否被安全地复用。组件越常见,生命周期边界越值得在实现时讲清楚,而不是等出现偶发卡顿再补救。
停止之后仍然可以有画面
用户关闭动效,不一定意味着图形区域应该空白。绘制一个静态状态之后停止循环,可以保留界面身份和内容层次,同时减少持续工作。静态图形与持续动画,是可以分开管理的两种能力。
同样,终止网络请求之后,输入和已经完成的历史仍然可以保留。结束某项工作,不应该无意中清空其他有价值的信息。清理需要准确,不只是彻底。
一个组件的质量,既体现在打开时的效果,也体现在结束时是否留下不属于自己的工作。给开始安排结束,给资源确定所有者,交互才能经得起反复使用。