很多 Vue 页面之所以越来越难维护,不是因为模板复杂,而是因为副作用散得到处都是。请求在某个钩子里发,事件监听在另一个地方绑,定时器忘了清,watch 又到处响应,最后页面能跑,但没人说得清哪些逻辑什么时候发生、依赖谁、页面离开后会不会留下脏状态。
所以这一篇真正要讲的,不只是生命周期名字,而是 Vue 页面里的“副作用治理”。也就是:什么时候做什么事,为什么放在这里,什么时候必须清理,哪些响应式监听应该存在,哪些其实是在制造混乱。
什么叫副作用?
从 Vue 项目视角看,凡是“不是单纯根据状态生成界面”的动作,都可以视为副作用,比如:
- 发请求;
- 订阅事件;
- 操作浏览器 API;
- 设置定时器;
- 手动同步外部状态;
- 写日志或埋点;
- 根据某个变化触发额外流程。
副作用本身不可怕,页面几乎不可能完全没有副作用。真正可怕的是副作用没有被放在正确的时机和边界里。
生命周期为什么是副作用的第一层边界?
Vue 组件并不是一上来就“完整存在”的。它会经历创建、挂载、更新和卸载等阶段。生命周期的价值,就在于告诉你:某段逻辑应该在什么时候出现。
更重要的是,它让你区分这几件事:
- 逻辑依赖的是响应式数据,还是依赖真实 DOM;
- 行为只需要执行一次,还是要跟随状态不断变化;
- 页面离开时是否必须清理。
如果不先分清这些事情,生命周期钩子和 watch 很快就会被混用。
created、mounted 这类钩子最重要的区别是什么?
很多人会机械记忆“哪个先执行、哪个后执行”,但更有价值的判断是:
- 此时组件状态是否已经准备好;
- DOM 是否已经可用;
- 我是不是必须依赖浏览器环境或节点尺寸。
created 更偏初始化和数据准备
它适合那些不依赖真实 DOM 的初始化逻辑,比如:
- 整理初始参数;
- 组装本地状态;
- 做部分早期数据准备。
mounted 更偏需要真实挂载结果的逻辑
比如:
- 依赖 DOM 尺寸;
- 需要访问浏览器对象;
- 需要初始化某些与页面节点强绑定的第三方实例。
真正成熟的判断,不是背“推荐写在哪”,而是知道当前逻辑到底依赖什么条件。
为什么副作用不能都塞进生命周期钩子?
因为不是所有变化都只发生一次。真实页面里经常会有:
- 路由参数变化后重新拉取;
- 分页变化后重新查询;
- 某个筛选条件改变后联动更新;
- 权限或主题变化后同步外部表现。
这些场景如果都只靠某个生命周期钩子,很快就会发现它根本不够表达。于是才需要 watch、watchEffect 这些围绕响应式依赖变化来组织副作用的能力。
watch 真正适合什么?
watch 更适合那些“我很明确知道要盯哪份状态,并在它变化时执行某个副作用”的场景。它的优点在于依赖清楚、触发条件明确,特别适合:
- 监听路由参数;
- 监听分页、筛选条件;
- 监听某个开关后触发额外请求;
- 在状态变化时同步外部系统。
也正因为如此,watch 比较适合“我知道自己在等什么变化”的场景。
watchEffect 又适合什么?
watchEffect 更像是“执行一段依赖收集驱动的副作用”。它的优势在于写法直接,适合那些依赖关系自然从代码中流出来的场景。但它也更容易让逻辑边界变模糊。
一个更稳的经验通常是:
- 依赖来源清晰、触发条件需要可控时,优先
watch; - 副作用逻辑短、依赖很自然时,再考虑
watchEffect; - 一旦逻辑开始复杂,优先回到更可读的显式结构。
很多页面难排查,就是因为监听逻辑被写得“很自动”,最后谁触发了什么反而更难看清。
为什么清理时机这么重要?
副作用最容易留下的问题,往往不是“没执行”,而是“执行完了却没结束”。比如:
- 页面卸载后事件监听还在;
- 定时器继续跑;
- 旧请求回来把新页面状态覆盖掉;
- 组件销毁后还在试图更新状态。
这些问题短期可能不明显,但在复杂页面、弹窗、多次切换和长时间运行场景里非常常见。清理副作用,本质上是在管理页面生命周期之外的残留影响。
把生命周期和副作用放回项目里,怎样组织更稳?
一个更成熟的顺序通常是:
1. 先区分这段逻辑是纯计算还是副作用;2. 再区分它是一次性初始化,还是跟随状态变化;3. 如果依赖 DOM 或浏览器环境,放到合适时机;4. 如果会留下持续影响,就一定要设计清理路径;5. 不要把多个互不相关的副作用全塞进同一处。
这套顺序比背生命周期表有用得多,因为它更接近真实项目里的决策过程。
最常见的几个误区
1. 所有逻辑都往 mounted 塞
结果页面一复杂,副作用顺序和依赖关系会越来越难看。
2. 看到状态变化就条件反射写 watch
很多逻辑其实应该是派生状态,而不是副作用监听。
3. watchEffect 用得很顺手,但边界越来越模糊
短期省代码,长期很难排查。
4. 事件监听、定时器、订阅不清理
这类问题最容易在弹窗和长生命周期页面里积累。
总结
Vue 生命周期真正重要的,不是记住钩子名称,而是学会管理副作用:什么时候初始化、什么时候依赖 DOM、什么时候跟随状态变化、什么时候必须清理。created、mounted、watch、watchEffect 并不是互相替代的工具,而是用来表达不同副作用边界的手段。只要你把“时机、依赖、持续性、清理”这四件事想清楚,页面逻辑就会稳定很多。