diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index 4504649bc5e..4dd1910bfa1 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -241,7 +241,7 @@ describe('api: options', () => { }) test('provide/inject', () => { - const Root = { + const Root = defineComponent({ data() { return { a: 1 @@ -253,45 +253,38 @@ describe('api: options', () => { } }, render() { - return [h(ChildA), h(ChildB), h(ChildC), h(ChildD)] - } - } as any - const ChildA = { - inject: ['a'], - render() { - return this.a - } - } as any - const ChildB = { - // object alias - inject: { b: 'a' }, - render() { - return this.b + return [h(ChildA), h(ChildB), h(ChildC), h(ChildD), h(ChildE)] } - } as any - const ChildC = { - inject: { - b: { - from: 'a' + }) + + const defineChild = (injectOptions: any, injectedKey = 'b') => + ({ + inject: injectOptions, + render() { + return this[injectedKey] } - }, - render() { - return this.b + } as any) + + const ChildA = defineChild(['a'], 'a') + const ChildB = defineChild({ b: 'a' }) + const ChildC = defineChild({ + b: { + from: 'a' } - } as any - const ChildD = { - inject: { - b: { - from: 'c', - default: 2 - } - }, - render() { - return this.b + }) + const ChildD = defineChild({ + b: { + from: 'c', + default: 2 } - } as any - - expect(renderToString(h(Root))).toBe(`1112`) + }) + const ChildE = defineChild({ + b: { + from: 'c', + default: () => 3 + } + }) + expect(renderToString(h(Root))).toBe(`11123`) }) test('lifecycle', async () => { diff --git a/packages/runtime-core/src/apiInject.ts b/packages/runtime-core/src/apiInject.ts index 2e1d428d1b9..02dc69e9d29 100644 --- a/packages/runtime-core/src/apiInject.ts +++ b/packages/runtime-core/src/apiInject.ts @@ -1,3 +1,4 @@ +import { isFunction } from '@vue/shared' import { currentInstance } from './component' import { currentRenderingInstance } from './componentRenderUtils' import { warn } from './warning' @@ -27,10 +28,15 @@ export function provide(key: InjectionKey | string, value: T) { } export function inject(key: InjectionKey | string): T | undefined -export function inject(key: InjectionKey | string, defaultValue: T): T +export function inject( + key: InjectionKey | string, + defaultValue: T, + treatDefaultAsFactory?: boolean +): T export function inject( key: InjectionKey | string, - defaultValue?: unknown + defaultValue?: unknown, + treatDefaultAsFactory = false ) { // fallback to `currentRenderingInstance` so that this can be called in // a functional component @@ -41,7 +47,9 @@ export function inject( // TS doesn't allow symbol as index type return provides[key as string] } else if (arguments.length > 1) { - return defaultValue + return treatDefaultAsFactory && isFunction(defaultValue) + ? defaultValue() + : defaultValue } else if (__DEV__) { warn(`injection "${String(key)}" not found.`) } diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index fd473714771..0eb24a6c2a1 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -457,7 +457,11 @@ export function applyOptions( for (const key in injectOptions) { const opt = injectOptions[key] if (isObject(opt)) { - ctx[key] = inject(opt.from, opt.default) + ctx[key] = inject( + opt.from, + opt.default, + true /* treat default function as factory */ + ) } else { ctx[key] = inject(opt) }