diff --git a/packages/runtime-core/__tests__/directives.spec.ts b/packages/runtime-core/__tests__/directives.spec.ts index 3bca7c4e..02d24711 100644 --- a/packages/runtime-core/__tests__/directives.spec.ts +++ b/packages/runtime-core/__tests__/directives.spec.ts @@ -367,4 +367,32 @@ describe('directives', () => { expect(d1.mounted).toHaveBeenCalled() expect(d2.mounted).toHaveBeenCalled() }) + + test('should disable tracking inside directive lifecycle hooks', async () => { + const count = ref(0) + const text = ref('') + const beforeUpdate = jest.fn(() => count.value++) + + const App = { + render() { + return withDirectives(h('p', text.value), [ + [ + { + beforeUpdate + } + ] + ]) + } + } + + const root = nodeOps.createElement('div') + render(h(App), root) + expect(beforeUpdate).toHaveBeenCalledTimes(0) + expect(count.value).toBe(0) + + text.value = 'foo' + await nextTick() + expect(beforeUpdate).toHaveBeenCalledTimes(1) + expect(count.value).toBe(1) + }) }) diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index a1987908..a82a9c7d 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -19,6 +19,7 @@ import { currentRenderingInstance } from './componentRenderContext' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' import { ComponentPublicInstance } from './componentPublicInstance' import { mapCompatDirectiveHook } from './compat/customDirective' +import { pauseTracking, resetTracking } from '@vue/reactivity' export interface DirectiveBinding { instance: ComponentPublicInstance | null @@ -130,12 +131,16 @@ export function invokeDirectiveHook( hook = mapCompatDirectiveHook(name, binding.dir, instance) } if (hook) { + // disable tracking inside all lifecycle hooks + // since they can potentially be called inside effects. + pauseTracking() callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [ vnode.el, binding, vnode, prevVNode ]) + resetTracking() } } }