refactor(hmr): simplify hmr force update check

This commit is contained in:
Evan You 2020-06-12 14:53:48 -04:00
parent 8f2a7489b7
commit e76ed4c269
6 changed files with 24 additions and 43 deletions

View File

@ -71,10 +71,6 @@ export interface ComponentInternalOptions {
* @internal * @internal
*/ */
__hmrId?: string __hmrId?: string
/**
* @internal
*/
__hmrUpdated?: boolean
/** /**
* This one should be exposed so that devtools can make use of it * This one should be exposed so that devtools can make use of it
*/ */
@ -325,12 +321,6 @@ export interface ComponentInternalInstance {
* @internal * @internal
*/ */
[LifecycleHooks.ERROR_CAPTURED]: LifecycleHook [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
/**
* hmr marker (dev only)
* @internal
*/
hmrUpdated?: boolean
} }
const emptyAppContext = createAppContext() const emptyAppContext = createAppContext()

View File

@ -16,6 +16,7 @@ import {
import { handleError, ErrorCodes } from './errorHandling' import { handleError, ErrorCodes } from './errorHandling'
import { PatchFlags, ShapeFlags, isOn } from '@vue/shared' import { PatchFlags, ShapeFlags, isOn } from '@vue/shared'
import { warn } from './warning' import { warn } from './warning'
import { isHmrUpdating } from './hmr'
// mark the current rendering instance for asset resolution (e.g. // mark the current rendering instance for asset resolution (e.g.
// resolveComponent, resolveDirective) during render // resolveComponent, resolveDirective) during render
@ -247,13 +248,8 @@ export function shouldUpdateComponent(
// Parent component's render function was hot-updated. Since this may have // Parent component's render function was hot-updated. Since this may have
// caused the child component's slots content to have changed, we need to // caused the child component's slots content to have changed, we need to
// force the child to update as well. // force the child to update as well.
if (__DEV__ && (prevChildren || nextChildren) && parentComponent) { if (__DEV__ && (prevChildren || nextChildren) && isHmrUpdating) {
let parent: ComponentInternalInstance | null = parentComponent return true
do {
if (parent.hmrUpdated) {
return true
}
} while ((parent = parent.parent))
} }
// force child update for runtime directive or transition on component vnode. // force child update for runtime directive or transition on component vnode.

View File

@ -18,7 +18,7 @@ import {
import { warn } from './warning' import { warn } from './warning'
import { isKeepAlive } from './components/KeepAlive' import { isKeepAlive } from './components/KeepAlive'
import { withCtx } from './helpers/withRenderContext' import { withCtx } from './helpers/withRenderContext'
import { queuePostFlushCb } from './scheduler' import { isHmrUpdating } from './hmr'
export type Slot = (...args: any[]) => VNode[] export type Slot = (...args: any[]) => VNode[]
@ -125,14 +125,10 @@ export const updateSlots = (
if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) { if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
if ((children as RawSlots)._ === 1) { if ((children as RawSlots)._ === 1) {
// compiled slots. // compiled slots.
if (__DEV__ && instance.parent && instance.parent.hmrUpdated) { if (__DEV__ && isHmrUpdating) {
// Parent was HMR updated so slot content may have changed. // Parent was HMR updated so slot content may have changed.
// force update slots and mark instance for hmr as well // force update slots and mark instance for hmr as well
extend(slots, children as Slots) extend(slots, children as Slots)
instance.hmrUpdated = true
queuePostFlushCb(() => {
instance.hmrUpdated = false
})
} else if ( } else if (
// bail on dynamic slots (v-if, v-for, reference of scope variables) // bail on dynamic slots (v-if, v-for, reference of scope variables)
!(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS) !(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS)

View File

@ -1,5 +1,6 @@
/* eslint-disable no-restricted-globals */ /* eslint-disable no-restricted-globals */
import { import {
Component,
ComponentInternalInstance, ComponentInternalInstance,
ComponentOptions, ComponentOptions,
InternalRenderFunction InternalRenderFunction
@ -7,6 +8,10 @@ import {
import { queueJob, queuePostFlushCb } from './scheduler' import { queueJob, queuePostFlushCb } from './scheduler'
import { extend } from '@vue/shared' import { extend } from '@vue/shared'
export let isHmrUpdating = false
export const hmrDirtyComponents = new Set<Component>()
export interface HMRRuntime { export interface HMRRuntime {
createRecord: typeof createRecord createRecord: typeof createRecord
rerender: typeof rerender rerender: typeof rerender
@ -72,9 +77,9 @@ function rerender(id: string, newRender?: Function) {
} }
instance.renderCache = [] instance.renderCache = []
// this flag forces child components with slot content to update // this flag forces child components with slot content to update
instance.hmrUpdated = true isHmrUpdating = true
instance.update() instance.update()
instance.hmrUpdated = false isHmrUpdating = false
}) })
} }
@ -85,7 +90,7 @@ function reload(id: string, newComp: ComponentOptions) {
// updates // updates
Array.from(record).forEach(instance => { Array.from(record).forEach(instance => {
const comp = instance.type const comp = instance.type
if (!comp.__hmrUpdated) { if (!hmrDirtyComponents.has(comp)) {
// 1. Update existing comp definition to match new one // 1. Update existing comp definition to match new one
extend(comp, newComp) extend(comp, newComp)
for (const key in comp) { for (const key in comp) {
@ -95,10 +100,10 @@ function reload(id: string, newComp: ComponentOptions) {
} }
// 2. Mark component dirty. This forces the renderer to replace the component // 2. Mark component dirty. This forces the renderer to replace the component
// on patch. // on patch.
comp.__hmrUpdated = true hmrDirtyComponents.add(comp)
// 3. Make sure to unmark the component after the reload. // 3. Make sure to unmark the component after the reload.
queuePostFlushCb(() => { queuePostFlushCb(() => {
comp.__hmrUpdated = false hmrDirtyComponents.delete(comp)
}) })
} }

View File

@ -56,7 +56,7 @@ import {
} from './components/Suspense' } from './components/Suspense'
import { TeleportImpl } from './components/Teleport' import { TeleportImpl } from './components/Teleport'
import { isKeepAlive, KeepAliveContext } from './components/KeepAlive' import { isKeepAlive, KeepAliveContext } from './components/KeepAlive'
import { registerHMR, unregisterHMR } from './hmr' import { registerHMR, unregisterHMR, isHmrUpdating } from './hmr'
import { import {
ErrorCodes, ErrorCodes,
callWithErrorHandling, callWithErrorHandling,
@ -791,18 +791,11 @@ function baseCreateRenderer(
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate') invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
} }
// check if any component of the parent chain has `hmrUpdated` if (__DEV__ && isHmrUpdating) {
if (__DEV__ && parentComponent) { // HMR updated, force full diff
let parent: ComponentInternalInstance | null = parentComponent patchFlag = 0
do { optimized = false
if (parent.hmrUpdated) { dynamicChildren = null
// HMR updated, force full diff
patchFlag = 0
optimized = false
dynamicChildren = null
break
}
} while ((parent = parent.parent))
} }
if (patchFlag > 0) { if (patchFlag > 0) {
@ -1025,7 +1018,7 @@ function baseCreateRenderer(
optimized = true optimized = true
} }
if (__DEV__ && parentComponent && parentComponent.hmrUpdated) { if (__DEV__ && isHmrUpdating) {
// HMR updated, force full diff // HMR updated, force full diff
patchFlag = 0 patchFlag = 0
optimized = false optimized = false

View File

@ -32,6 +32,7 @@ import { TeleportImpl, isTeleport } from './components/Teleport'
import { currentRenderingInstance } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderUtils'
import { RendererNode, RendererElement } from './renderer' import { RendererNode, RendererElement } from './renderer'
import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets' import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
import { hmrDirtyComponents } from './hmr'
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
__isFragment: true __isFragment: true
@ -236,7 +237,7 @@ export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
if ( if (
__DEV__ && __DEV__ &&
n2.shapeFlag & ShapeFlags.COMPONENT && n2.shapeFlag & ShapeFlags.COMPONENT &&
(n2.type as Component).__hmrUpdated hmrDirtyComponents.has(n2.type as Component)
) { ) {
// HMR only: if the component has been hot-updated, force a reload. // HMR only: if the component has been hot-updated, force a reload.
return false return false