🌐 AI搜索 & 代理 主页
Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(useResizeObserver): clear directive when component is unmounted(#…
  • Loading branch information
kalu5 committed Nov 14, 2025
commit c8ae60f38f813e49a37149b38653435e572b956c
53 changes: 52 additions & 1 deletion packages/core/useResizeObserver/directive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { VueWrapper } from '@vue/test-utils'
import type { UseResizeObserverOptions } from './index'
import { mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { defineComponent } from 'vue'
import { defineComponent, nextTick } from 'vue'
import { vResizeObserver } from './directive'

const App = defineComponent({
Expand All @@ -27,6 +27,15 @@ const App = defineComponent({
describe('vResizeObserver', () => {
let onResizeObserver = vi.fn()
let wrapper: VueWrapper<any>
globalThis.ResizeObserver = vi.fn().mockImplementation((callback) => {
return {
observe: vi.fn((target) => {
callback([{ contentRect: { width: target.offsetWidth || 0 } }], this)
}),
unobserve: vi.fn(),
disconnect: vi.fn(),
}
})

describe('given no options', () => {
beforeEach(() => {
Expand All @@ -43,9 +52,30 @@ describe('vResizeObserver', () => {
})
})

const observer = new ResizeObserver(onResizeObserver)

it('should be defined', () => {
expect(wrapper).toBeDefined()
})

it('should clear directive when component is unmounted', async () => {
const element = wrapper.element.querySelector('div')
if (element) {
observer.observe(element)
element.style.width = '200px'
await new Promise(resolve => setTimeout(resolve, 100))
expect(onResizeObserver).toBeCalledTimes(1)

observer.disconnect()
wrapper.unmount()
onResizeObserver.mockClear()
await nextTick()

element.style.width = '300px'
await new Promise(resolve => setTimeout(resolve, 100))
expect(onResizeObserver).toBeCalledTimes(0)
}
})
})

describe('given options', () => {
Expand All @@ -67,8 +97,29 @@ describe('vResizeObserver', () => {
})
})

const observer = new ResizeObserver(onResizeObserver)

it('should be defined', () => {
expect(wrapper).toBeDefined()
})

it('should clear directive when component is unmounted', async () => {
const element = wrapper.element.querySelector('div')
if (element) {
observer.observe(element)
element.style.width = '200px'
await new Promise(resolve => setTimeout(resolve, 100))
expect(onResizeObserver).toBeCalledTimes(1)

observer.disconnect()
wrapper.unmount()
onResizeObserver.mockClear()
await nextTick()

element.style.width = '300px'
await new Promise(resolve => setTimeout(resolve, 100))
expect(onResizeObserver).toBeCalledTimes(0)
}
})
})
})
21 changes: 16 additions & 5 deletions packages/core/useResizeObserver/directive.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import type { ResizeObserverCallback, UseResizeObserverOptions } from '@vueuse/core'
import type { ObjectDirective } from 'vue'
import type { EffectScope, ObjectDirective } from 'vue'
import { useResizeObserver } from '@vueuse/core'
import { effectScope } from 'vue'

type BindingValueFunction = ResizeObserverCallback

type BindingValueArray = [BindingValueFunction, UseResizeObserverOptions]

const vResizeObserverScopes = new WeakMap<HTMLElement, EffectScope>()

export const vResizeObserver: ObjectDirective<
HTMLElement,
BindingValueFunction | BindingValueArray
> = {
mounted(el, binding) {
if (typeof binding.value === 'function')
useResizeObserver(el, binding.value)
else
useResizeObserver(el, ...binding.value)
const scope = vResizeObserverScopes.get(el) ?? effectScope()
vResizeObserverScopes.set(el, scope)
scope.run(() => {
if (typeof binding.value === 'function')
useResizeObserver(el, binding.value)
else
useResizeObserver(el, ...binding.value)
})
},
unmounted(el) {
vResizeObserverScopes.get(el)?.stop()
vResizeObserverScopes.delete(el)
},
}