watcheffect 不能直接监听 window 尺寸,因为 window.innerwidth/height 是普通只读属性,非响应式,无法触发依赖收集;需用 ref 包裹并配合 resize 事件手动更新,再由 watcheffect 追踪 ref 变化。

直接用 watchEffect 监听屏幕尺寸变化是不可行的,因为它只能自动追踪响应式依赖,而 window.innerWidth 和 window.innerHeight 是原生只读属性,本身不具有响应式性。
为什么 watchEffect 不能直接监听 window 尺寸?
watchEffect 的机制是“自动收集它内部访问的所有响应式数据的依赖”。但 window.innerWidth 是一个普通数值,不是 ref、reactive 或 computed,所以无论你在 watchEffect 里怎么读取它,都不会触发重运行——它只在首次执行时取一次值,后续窗口缩放完全无反应。
正确做法:用 ref 包裹尺寸,并在 resize 中更新
核心思路是:把窗口尺寸变成响应式数据,再让 watchEffect 追踪这个 ref。步骤如下:
- 定义
screenWidth = ref(window.innerWidth)和screenHeight = ref(window.innerHeight) - 在
onMounted中绑定window.addEventListener('resize', ...),并在回调里同步更新这两个 ref - 在
onUnmounted中移除事件监听,避免内存泄漏 - 用
watchEffect监听screenWidth或screenHeight,逻辑写在里面
示例代码:
import { ref, onMounted, onUnmounted, watchEffect } from 'vue'
const screenWidth = ref(window.innerWidth)
const screenHeight = ref(window.innerHeight)
const handleResize = () => {
screenWidth.value = window.innerWidth
screenHeight.value = window.innerHeight
}
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
watchEffect(() => {
if (screenWidth.value
更简洁的替代方案:封装成组合式函数
为复用方便,可抽成 useWindowSize():
- 返回
{ width, height }两个 ref - 内部完成事件绑定/解绑和响应式更新
- 各组件直接调用,再用 watchEffect 或 computed 消费
这样既保持逻辑清晰,又避免重复写 resize 处理,也符合 Vue 3 组合式 API 的设计习惯。
何时该选 watchEffect 而非 watch?
如果你只是根据尺寸做副作用(如修改 class、请求适配接口、调整 canvas 大小),watchEffect 更自然:无需声明 source,写法扁平,且能同时读多个响应式变量;
如果需要对比新旧值(比如只在宽度跨过 768px 时才触发动画),那就用 watch(screenWidth, (newVal, oldVal) => {...}, { immediate: true }),它提供明确的前后值参数。











