Browse Source

fix(hmr): suppress `provide()` warning during HMR updates for mounted instances (#14195)

edison 6 months ago
parent
commit
d823d6a325

+ 2 - 1
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<T> {}
 
@@ -12,7 +13,7 @@ export function provide<T, K = InjectionKey<T> | string | number>(
   value: K extends InjectionKey<infer V> ? V : T,
 ): void {
   if (__DEV__) {
-    if (!currentInstance || currentInstance.isMounted) {
+    if (!currentInstance || (currentInstance.isMounted && !isHmrUpdating)) {
       warn(`provide() can only be used inside setup().`)
     }
   }

+ 39 - 0
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,44 @@ describe('hot module replacement', () => {
     )
   })
 
+  // Vapor router-view has no render function (setup-only).
+  // When HMR rerender is triggered, the setup function is re-executed.
+  // Ensure provide() warning is suppressed.
+  test('rerender setup-only component', async () => {
+    const childId = 'test-child-reload-01'
+    const Child = defineVaporComponent({
+      __hmrId: childId,
+      render: compileToFunction(`<div>foo</div>`),
+    })
+    createRecord(childId, Child as any)
+
+    // without a render function
+    const Parent = defineVaporComponent({
+      setup() {
+        provide('foo', 'bar')
+        return createComponent(Child)
+      },
+    })
+
+    const { html } = define({
+      setup() {
+        return createComponent(Parent)
+      },
+    }).render()
+
+    expect(html()).toBe('<div>foo</div>')
+
+    // will trigger parent rerender
+    reload(childId, {
+      __hmrId: childId,
+      render: compileToFunction(`<div>bar</div>`),
+    })
+
+    await nextTick()
+    expect(html()).toBe('<div>bar</div>')
+    expect('provide() can only be used inside setup()').not.toHaveBeenWarned()
+  })
+
   describe('switch vapor/vdom modes', () => {
     test('vapor -> vdom', async () => {
       const id = 'vapor-to-vdom'