You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试Vue mounted/unmounted中的addEventListener与removeEventListener

问题根因

你现在的代码存在两个核心问题,也是覆盖率缺失、逻辑不生效的原因:

  • onMounted里存在笔误:写了window.addEventListener.addEventListener,正确调用应该是window.addEventListener,这行本身就会抛出运行时错误
  • 事件绑定/移除的写法本身就不符合DOM API规范:addEventListener和removeEventListener必须传入完全相同的函数引用才能匹配,你两次传入的都是临时定义的匿名箭头函数,是两个完全独立的函数实例,哪怕走到onUnmounted逻辑,也根本移除不了之前绑定的监听,会造成内存泄漏
  • 覆盖率统计不到removeEventListener所在行,本质是你的测试用例从来没有触发过组件卸载动作,onUnmounted生命周期钩子根本没执行,对应代码自然不会被标记为已覆盖
第一步:修正组件业务代码

先把事件回调抽离为固定引用的函数,修正笔误:

import { ref, onMounted, onUnmounted } from 'vue'

const isMobile = ref(false)
// 抽离为独立函数,保证绑定、移除时拿到的是同一个引用
const handleWindowResize = () => {
  isMobile.value = setIsMobile()
}

onMounted(() => {
  // 修正多写的一层.addEventListener笔误
  window.addEventListener('resize', handleWindowResize)
})

onUnmounted(() => {
  // 传入和绑定时完全一致的函数引用
  window.removeEventListener('resize', handleWindowResize)
})

isMobile.value = setIsMobile()
第二步:编写对应测试用例

以Vue Test Utils + Vitest/Jest 测试栈为例,测试需要同时覆盖挂载绑定、卸载移除两个场景,核心是手动触发组件卸载动作,让onUnmounted钩子执行:

import { mount } from '@vue/test-utils'
import TargetComponent from './TargetComponent.vue'
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'

describe('window resize 事件监听逻辑', () => {
  let addEventSpy
  let removeEventSpy

  beforeEach(() => {
    // 监听window的事件方法,校验调用参数
    addEventSpy = vi.spyOn(window, 'addEventListener')
    removeEventSpy = vi.spyOn(window, 'removeEventListener')
  })

  afterEach(() => {
    // 还原spy,避免用例之间互相影响
    addEventSpy.mockRestore()
    removeEventSpy.mockRestore()
  })

  it('组件挂载时正确绑定resize事件监听', () => {
    mount(TargetComponent)
    expect(addEventSpy).toHaveBeenCalledWith('resize', expect.any(Function))
  })

  it('组件卸载时正确移除resize事件监听', () => {
    const wrapper = mount(TargetComponent)
    // 核心操作:手动触发组件卸载,执行onUnmounted内的逻辑
    wrapper.unmount()

    // 断言移除方法被调用
    expect(removeEventSpy).toHaveBeenCalledWith('resize', expect.any(Function))
    // 更严谨的校验:确认绑定和移除使用的是同一个函数引用
    const [, bindCallback] = addEventSpy.mock.calls.find(item => item[0] === 'resize')
    const [, removeCallback] = removeEventSpy.mock.calls.find(item => item[0] === 'resize')
    expect(bindCallback).toBe(removeCallback)
  })
})

额外提示:如果测试环境是jsdom,触发resize事件后也可以断言isMobile的值是否符合预期,进一步验证回调逻辑的正确性。

内容的提问来源于stack exchange,提问作者artuyees

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 10:03:21