diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index e9f36ecd22f..b177fd91fdc 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,6 +1,6 @@ import { DebuggerOptions, ReactiveEffect } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' -import { isFunction, NOOP } from '@vue/shared' +import { isFunction, isHmrUpdating, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' import { Dep } from './dep' @@ -56,7 +56,11 @@ export class ComputedRefImpl { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) trackRefValue(self) - if (self._dirty || !self._cacheable) { + // #7155 - should return the latest value during HMR to avoid the page not updating. + if (__DEV__ && isHmrUpdating) { + self._dirty = false + self._value = self.effect.run()! + } else if (self._dirty || !self._cacheable) { self._dirty = false self._value = self.effect.run()! } diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 4b501052ce4..e8ea3781b7c 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -332,6 +332,37 @@ describe('hot module replacement', () => { expect(serializeInner(root)).toBe(`
bar
`) }) + // #7155 - should force update computed value when HMR is active + test('force update computed value', () => { + const root = nodeOps.createElement('div') + const parentId = 'test-force-computed-parent' + const childId = 'test-force-computed-child' + + const Child: ComponentOptions = { + __hmrId: childId, + computed: { + slotContent() { + return this.$slots.default?.() + } + }, + render: compileToFunction(``) + } + createRecord(childId, Child) + + const Parent: ComponentOptions = { + __hmrId: parentId, + components: { Child }, + render: compileToFunction(`1`) + } + createRecord(parentId, Parent) + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`1`) + + rerender(parentId, compileToFunction(`2`)) + expect(serializeInner(root)).toBe(`2`) + }) + // #1305 - component should remove class test('remove static class from parent', () => { const root = nodeOps.createElement('div') diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index d9de968a074..da4d98bd293 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -15,9 +15,14 @@ import { blockStack } from './vnode' import { handleError, ErrorCodes } from './errorHandling' -import { PatchFlags, ShapeFlags, isOn, isModelListener } from '@vue/shared' +import { + PatchFlags, + ShapeFlags, + isOn, + isModelListener, + isHmrUpdating +} from '@vue/shared' import { warn } from './warning' -import { isHmrUpdating } from './hmr' import { NormalizedProps } from './componentProps' import { isEmitListener } from './componentEmits' import { setCurrentRenderingInstance } from './componentRenderContext' diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 682a107e676..a8a35c8f1e0 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -13,12 +13,12 @@ import { ShapeFlags, extend, def, - SlotFlags + SlotFlags, + isHmrUpdating } from '@vue/shared' import { warn } from './warning' import { isKeepAlive } from './components/KeepAlive' import { ContextualRenderFn, withCtx } from './componentRenderContext' -import { isHmrUpdating } from './hmr' import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig' import { toRaw } from '@vue/reactivity' diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index f9f845298dc..0bcc5118380 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -9,9 +9,8 @@ import { traverseStaticChildren } from '../renderer' import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode' -import { isString, ShapeFlags } from '@vue/shared' +import { isHmrUpdating, isString, ShapeFlags } from '@vue/shared' import { warn } from '../warning' -import { isHmrUpdating } from '../hmr' export type TeleportVNode = VNode diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index c5039f62b6f..4ca9599f8d9 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -8,12 +8,10 @@ import { isClassComponent } from './component' import { queueJob, queuePostFlushCb } from './scheduler' -import { extend, getGlobalThis } from '@vue/shared' +import { extend, getGlobalThis, setHmrUpdating } from '@vue/shared' type HMRComponent = ComponentOptions | ClassComponent -export let isHmrUpdating = false - export const hmrDirtyComponents = new Set() export interface HMRRuntime { @@ -92,9 +90,9 @@ function rerender(id: string, newRender?: Function) { } instance.renderCache = [] // this flag forces child components with slot content to update - isHmrUpdating = true + setHmrUpdating(true) instance.update() - isHmrUpdating = false + setHmrUpdating(false) }) } diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 4dfbc656996..3e018714bb0 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -35,7 +35,8 @@ import { NOOP, invokeArrayFns, isArray, - getGlobalThis + getGlobalThis, + isHmrUpdating } from '@vue/shared' import { queueJob, @@ -58,7 +59,7 @@ import { } from './components/Suspense' import { TeleportImpl, TeleportVNode } from './components/Teleport' import { isKeepAlive, KeepAliveContext } from './components/KeepAlive' -import { registerHMR, unregisterHMR, isHmrUpdating } from './hmr' +import { registerHMR, unregisterHMR } from './hmr' import { createHydrationFunctions, RootHydrateFunction } from './hydration' import { invokeDirectiveHook } from './directives' import { startMeasure, endMeasure } from './profiling' diff --git a/packages/shared/src/hmr.ts b/packages/shared/src/hmr.ts new file mode 100644 index 00000000000..3f185ed7837 --- /dev/null +++ b/packages/shared/src/hmr.ts @@ -0,0 +1,2 @@ +export let isHmrUpdating = false +export const setHmrUpdating = (v: boolean) => (isHmrUpdating = v) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index e3fcd86627b..8beca180d61 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -13,6 +13,7 @@ export * from './escapeHtml' export * from './looseEqual' export * from './toDisplayString' export * from './typeUtils' +export * from './hmr' export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__ ? Object.freeze({})