用 watch 监听滚动需先定义 ref 响应式变量并手动更新 scrolltop 值,结合 computed 判断吸顶状态,动态绑定 class 控制定位,且必须在 onbeforeunmount 中移除事件监听以防内存泄漏。

用 watch 监听窗口滚动位置实现吸顶效果,核心在于实时捕获滚动偏移量,并根据阈值动态控制元素的定位状态。Vue 3 中虽然推荐使用 onMounted + addEventListener 的组合方式监听 scroll 事件,但若你坚持用 watch,需配合一个响应式变量手动更新滚动值——因为原生 window.scrollY 或 document.documentElement.scrollTop 并非响应式数据。
创建响应式滚动值并手动更新
Vue 3 不支持直接 watch 非响应式对象(如 window),所以得先定义一个 ref 存储当前滚动高度,并在 scroll 事件中主动赋值:
- 在
setup()中声明const scrollTop = ref(0) -
onMounted里添加window.addEventListener('scroll', () => { scrollTop.value = window.pageYOffset || document.documentElement.scrollTop }) - 用
watch(scrollTop, (newVal) => { /* 判断是否吸顶 */ })响应变化
吸顶判断逻辑要准确取值
获取滚动距离必须优先用 window.pageYOffset,其次 fallback 到 document.documentElement.scrollTop。不要用 document.body.scrollTop,它在多数现代浏览器中始终为 0;documentElement 对应的是 标签,才是标准滚动容器。
- 常见错误写法:
document.body.scrollTop→ 大概率返回 0 - 推荐写法:
window.pageYOffset || document.documentElement.scrollTop - 阈值建议设为导航栏原始位置的 offsetTop,或固定值如 80/120,避免硬编码导致适配问题
动态绑定 class 控制定位样式
吸顶本质是切换 CSS 定位方式:从 position: relative/static 变为 position: fixed; top: 0。建议用 :class 绑定一个计算属性或响应式布尔值:
- 定义
const isSticky = computed(() => scrollTop.value > 120) - 模板中写:
<header :class="{ 'sticky-header': isSticky }"></header> - CSS 中定义
.sticky-header { position: fixed; top: 0; left: 0; right: 0; z-index: 1000; } - 注意 fixed 元素会脱离文档流,记得给后续内容预留占位(例如加等高 padding)
记得清理事件监听防止内存泄漏
组件卸载时未移除 scroll 事件会导致潜在性能问题和意外触发:
- 在
onBeforeUnmount中调用window.removeEventListener('scroll', handler) - 把 handler 提前定义为具名函数,方便引用移除
- 如果使用 Composition API 的
watch+onUnmounted,也要同步清理










