diff --git a/packages/runtime-core/src/apiInject.ts b/packages/runtime-core/src/apiInject.ts index 9285614835d..718a8401f38 100644 --- a/packages/runtime-core/src/apiInject.ts +++ b/packages/runtime-core/src/apiInject.ts @@ -2,6 +2,7 @@ import { isFunction } from '@vue/shared' import { currentInstance, getCurrentGenericInstance } from './component' import { currentApp } from './apiCreateApp' import { warn } from './warning' +import { isHmrUpdating } from './hmr' interface InjectionConstraint {} @@ -12,7 +13,7 @@ export function provide | string | number>( value: K extends InjectionKey ? V : T, ): void { if (__DEV__) { - if (!currentInstance || currentInstance.isMounted) { + if (!currentInstance || (currentInstance.isMounted && !isHmrUpdating)) { warn(`provide() can only be used inside setup().`) } } diff --git a/packages/runtime-vapor/__tests__/hmr.spec.ts b/packages/runtime-vapor/__tests__/hmr.spec.ts index c5b07a6af79..9c17f60fcaf 100644 --- a/packages/runtime-vapor/__tests__/hmr.spec.ts +++ b/packages/runtime-vapor/__tests__/hmr.spec.ts @@ -8,6 +8,7 @@ import { onDeactivated, onMounted, onUnmounted, + provide, ref, toDisplayString, } from '@vue/runtime-dom' @@ -1064,6 +1065,42 @@ describe('hot module replacement', () => { ) }) + test('reload setup only component', async () => { + const childId = 'test-child-reload-01' + const Child = defineVaporComponent({ + __hmrId: childId, + render: compileToFunction(`
foo
`), + }) + createRecord(childId, Child as any) + + // Simulate router-view, it does not have a render function, + // so when it rerenders, the setup function will be called + const RouterView = defineVaporComponent({ + setup() { + provide('foo', 'bar') + return createComponent(Child) + }, + }) + + const { html } = define({ + setup() { + return createComponent(RouterView) + }, + }).render() + + expect(html()).toBe('
foo
') + + // will trigger parent rerender + reload(childId, { + __hmrId: childId, + render: compileToFunction(`
bar
`), + }) + + await nextTick() + expect(html()).toBe('
bar
') + expect('provide() can only be used inside setup()').not.toHaveBeenWarned() + }) + describe('switch vapor/vdom modes', () => { test('vapor -> vdom', async () => { const id = 'vapor-to-vdom'