只在客户端执行的钩子包括beforemount、mounted、beforeupdate、updated、beforeunmount、unmounted及其composition api对应版本,因它们依赖真实dom和浏览器环境,服务端仅生成html字符串,无挂载、更新、卸载等ui行为,故完全不执行。

在 SSR 场景中,区分“只在客户端执行”的生命周期钩子,核心是看它是否依赖浏览器运行时环境(如真实 DOM、用户交互、事件循环、window/document 等)。服务端只有 JavaScript 执行上下文,没有挂载、更新、卸载这些 UI 行为,所以相关钩子根本不会触发。
只在客户端执行的钩子有哪些
以下钩子在 SSR 渲染阶段(renderToString 或 renderToNodeStream)完全不执行,仅在客户端 hydration 后触发:
- beforeMount / mounted:需要真实 DOM 节点和挂载流程,服务端只生成 HTML 字符串
- beforeUpdate / updated:基于响应式变化 + DOM 重绘,服务端无更新机制
- beforeUnmount / unmounted:组件卸载是客户端运行时行为,服务端无生命周期终止概念
- onMounted / onUpdated / onBeforeMount / onBeforeUnmount(组合式 API):同理,仅在客户端 hydration 完成后调用
为什么它们服务端不执行
不是 Vue “忽略”了它们,而是设计上就无法成立:
- 服务端渲染是一次性同步过程,输出静态 HTML 字符串,不存在“挂载到 DOM”这一步
- 没有浏览器事件循环,无法触发更新队列或 nextTick 回调
- 没有 DOM 实例,
this.$el、ref()绑定的元素在服务端为null - 组件实例在服务端渲染后即被销毁,不存在“长期存活→等待卸载”的状态
怎么验证某个钩子是否只在客户端跑
最直接的方法是在钩子里打印环境信息:
- 在
mounted()里写console.log('mounted:', typeof window)→ 客户端输出'object',服务端不执行,无日志 - 在
created()里同样打印 → 两端都会输出,服务端是'undefined',客户端是'object' - 用
process.server(Nuxt)或import.meta.env.SSR(Vite + Vue SSR)做条件判断,可明确区分执行环境
误用这些钩子的典型后果
把本该只在客户端运行的逻辑塞进服务端上下文,轻则报错,重则破坏 hydration:
- 在
mounted中请求首屏数据 → 页面先吐出空 HTML,再发请求、闪屏,失去 SSR 意义 - 在
setup()里没判断就调localStorage.getItem→ SSR 报ReferenceError: localStorage is not defined - 在
created里启动setInterval→ 服务端无法清理,导致内存泄漏(SSR 进程常驻时尤其危险) - 用
$nextTick包裹 DOM 操作 → 服务端静默跳过,客户端执行,可能造成 hydration 差异警告










