diff --git a/packages/runtime-core/__tests__/directives.spec.ts b/packages/runtime-core/__tests__/directives.spec.ts index cab514d8f44..bfd6e439a46 100644 --- a/packages/runtime-core/__tests__/directives.spec.ts +++ b/packages/runtime-core/__tests__/directives.spec.ts @@ -9,6 +9,7 @@ import { DirectiveBinding, nextTick } from '@vue/runtime-test' +import { currentInstance, ComponentInternalInstance } from '../src/component' describe('directives', () => { it('should work', async () => { @@ -17,6 +18,7 @@ describe('directives', () => { function assertBindings(binding: DirectiveBinding) { expect(binding.value).toBe(count.value) expect(binding.arg).toBe('foo') + expect(binding.instance).toBe(_instance && _instance.proxy) expect(binding.modifiers && binding.modifiers.ok).toBe(true) } @@ -105,9 +107,13 @@ describe('directives', () => { unmounted } + let _instance: ComponentInternalInstance | null = null let _vnode: VNode | null = null let _prevVnode: VNode | null = null const Comp = { + setup() { + _instance = currentInstance + }, render() { _prevVnode = _vnode _vnode = withDirectives(h('div', count.value), [ @@ -147,6 +153,7 @@ describe('directives', () => { function assertBindings(binding: DirectiveBinding) { expect(binding.value).toBe(count.value) expect(binding.arg).toBe('foo') + expect(binding.instance).toBe(_instance && _instance.proxy) expect(binding.modifiers && binding.modifiers.ok).toBe(true) } @@ -160,9 +167,13 @@ describe('directives', () => { expect(prevVNode).toBe(_prevVnode) }) as DirectiveHook) + let _instance: ComponentInternalInstance | null = null let _vnode: VNode | null = null let _prevVnode: VNode | null = null const Comp = { + setup() { + _instance = currentInstance + }, render() { _prevVnode = _vnode _vnode = withDirectives(h('div', count.value), [ @@ -196,6 +207,7 @@ describe('directives', () => { function assertBindings(binding: DirectiveBinding) { expect(binding.value).toBe(count.value) expect(binding.arg).toBe('foo') + expect(binding.instance).toBe(_instance && _instance.proxy) expect(binding.modifiers && binding.modifiers.ok).toBe(true) } @@ -284,6 +296,7 @@ describe('directives', () => { unmounted } + let _instance: ComponentInternalInstance | null = null let _vnode: VNode | null = null let _prevVnode: VNode | null = null @@ -294,6 +307,9 @@ describe('directives', () => { } const Comp = { + setup() { + _instance = currentInstance + }, render() { return withDirectives(h(Child, { count: count.value }), [ [ diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index 6f733eadcd6..d3e638f9010 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -15,9 +15,12 @@ import { VNode } from './vnode' import { isFunction, EMPTY_OBJ, makeMap, EMPTY_ARR } from '@vue/shared' import { warn } from './warning' import { ComponentInternalInstance } from './component' +import { currentRenderingInstance } from './componentRenderUtils' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' +import { ComponentPublicInstance } from './componentProxy' export interface DirectiveBinding { + instance: ComponentPublicInstance | null value: any oldValue: any arg?: string @@ -105,9 +108,14 @@ export function withDirectives( vnode: T, directives: DirectiveArguments ): T { + const internalInstance = currentRenderingInstance + if (internalInstance === null) { + __DEV__ && warn(`withDirectives can only be used inside render functions.`) + return vnode + } + const instance = internalInstance.proxy const props = vnode.props || (vnode.props = {}) - const bindings: DirectiveBinding[] = - vnode.dirs || (vnode.dirs = new Array(directives.length)) + const bindings = vnode.dirs || (vnode.dirs = new Array(directives.length)) const injected: Record = {} for (let i = 0; i < directives.length; i++) { let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i] @@ -119,6 +127,7 @@ export function withDirectives( } bindings[i] = { dir, + instance, value, oldValue: void 0, arg,