wip: vdom in vapor hmr reload

This commit is contained in:
Evan You 2025-02-04 22:44:17 +08:00
parent 4b12529878
commit be5c2a2f51
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
13 changed files with 68 additions and 44 deletions

View File

@ -3,6 +3,7 @@ import {
type ComponentInternalInstance,
type ComponentOptions,
type ConcreteComponent,
type GenericComponentInstance,
currentInstance,
isInSSRComponentSetup,
} from './component'
@ -39,7 +40,7 @@ export interface AsyncComponentOptions<T = any> {
) => any
}
export const isAsyncWrapper = (i: ComponentInternalInstance | VNode): boolean =>
export const isAsyncWrapper = (i: GenericComponentInstance | VNode): boolean =>
!!(i.type as ComponentOptions).__asyncLoader
/*! #__NO_SIDE_EFFECTS__ */
@ -206,10 +207,14 @@ export function defineAsyncComponent<
load()
.then(() => {
loaded.value = true
if (instance.parent && isKeepAlive(instance.parent.vnode)) {
if (
instance.parent &&
instance.parent.vnode &&
isKeepAlive(instance.parent.vnode)
) {
// parent is keep-alive, force update so the loaded component's
// name is taken into account
instance.parent.update()
;(instance.parent as ComponentInternalInstance).update()
}
})
.catch(err => {

View File

@ -504,6 +504,26 @@ export interface GenericComponentInstance {
* @internal vapor only
*/
hmrReload?: (newComp: any) => void
// these only exist on vdom instances
vnode?: VNode
subTree?: VNode
/**
* Custom Element instance (if component is created by defineCustomElement)
* @internal
*/
ce?: ComponentCustomElementInterface
/**
* is custom element? (kept only for compatibility)
* @internal
*/
isCE?: boolean
/**
* custom element specific HMR method
* @internal
*/
ceReload?: (newStyles?: string[]) => void
}
/**
@ -514,8 +534,8 @@ export interface ComponentInternalInstance extends GenericComponentInstance {
vapor?: never
uid: number
type: ConcreteComponent
parent: ComponentInternalInstance | null
root: ComponentInternalInstance
parent: GenericComponentInstance | null
root: GenericComponentInstance
appContext: AppContext
/**
* Vnode representing this component in its parent's vdom tree
@ -589,21 +609,6 @@ export interface ComponentInternalInstance extends GenericComponentInstance {
* @internal
*/
inheritAttrs?: boolean
/**
* Custom Element instance (if component is created by defineCustomElement)
* @internal
*/
ce?: ComponentCustomElementInterface
/**
* is custom element? (kept only for compatibility)
* @internal
*/
isCE?: boolean
/**
* custom element specific HMR method
* @internal
*/
ceReload?: (newStyles?: string[]) => void
// the rest are only for stateful components ---------------------------------
/**
@ -1210,7 +1215,7 @@ export function expose(
}
export function getComponentPublicInstance(
instance: ComponentInternalInstance,
instance: GenericComponentInstance,
): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null {
if (instance.exposed) {
return (

View File

@ -232,7 +232,7 @@ export function initProps(
instance.attrs = attrs
}
function isInHmrContext(instance: ComponentInternalInstance | null) {
function isInHmrContext(instance: GenericComponentInstance | null) {
while (instance) {
if (instance.type.__hmrId) return true
instance = instance.parent

View File

@ -2,6 +2,7 @@ import {
type Component,
type ComponentInternalInstance,
type Data,
type GenericComponentInstance,
getComponentPublicInstance,
isStatefulComponent,
} from './component'
@ -355,10 +356,11 @@ export type PublicPropertiesMap = Record<
* public $parent chains, skip functional ones and go to the parent instead.
*/
const getPublicInstance = (
i: ComponentInternalInstance | null,
i: GenericComponentInstance | null,
): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null => {
if (!i) return null
if (isStatefulComponent(i)) return getComponentPublicInstance(i)
if (!i || i.vapor) return null
if (isStatefulComponent(i as ComponentInternalInstance))
return getComponentPublicInstance(i)
return getPublicInstance(i.parent)
}

View File

@ -455,12 +455,12 @@ export function updateHOCHostEl(
el: typeof vnode.el, // HostNode
): void {
while (parent && !parent.vapor) {
const root = parent.subTree
const root = parent.subTree!
if (root.suspense && root.suspense.activeBranch === vnode) {
root.el = vnode.el
}
if (root === vnode) {
;(vnode = parent.vnode).el = el
;(vnode = parent.vnode!).el = el
parent = parent.parent
} else {
break

View File

@ -2,6 +2,7 @@ import {
type ComponentInternalInstance,
type ComponentOptions,
type ConcreteComponent,
type GenericComponentInstance,
type SetupContext,
getComponentName,
getCurrentInstance,
@ -436,7 +437,7 @@ function registerKeepAliveHook(
hook.__wdc ||
(hook.__wdc = () => {
// only fire the hook if the target instance is NOT in a deactivated branch.
let current: ComponentInternalInstance | null = target
let current: GenericComponentInstance | null = target
while (current) {
if (current.isDeactivated) {
return
@ -453,7 +454,7 @@ function registerKeepAliveHook(
// arrays.
if (target) {
let current = target.parent
while (current && current.parent) {
while (current && current.parent && current.parent.vnode) {
if (isKeepAlive(current.parent.vnode)) {
injectToKeepAliveRoot(wrappedHook, type, target, current)
}
@ -466,7 +467,7 @@ function injectToKeepAliveRoot(
hook: Function & { __weh?: Function },
type: LifecycleHooks,
target: ComponentInternalInstance,
keepAliveRoot: ComponentInternalInstance,
keepAliveRoot: GenericComponentInstance,
) {
// injectHook wraps the original for error handling, so make sure to remove
// the wrapped version.

View File

@ -154,7 +154,12 @@ function reload(id: string, newComp: HMRComponent): void {
// don't end up forcing the same parent to re-render multiple times.
queueJob(() => {
isHmrUpdating = true
instance.parent!.update()
const parent = instance.parent!
if (parent.vapor) {
parent.hmrRerender!()
} else {
;(parent as ComponentInternalInstance).update()
}
isHmrUpdating = false
// #6930, #11248 avoid infinite recursion
dirtyInstances.delete(instance)

View File

@ -768,7 +768,7 @@ export function createHydrationFunctions(
if (parent.vnode.el === oldNode) {
parent.vnode.el = parent.subTree.el = newNode
}
parent = parent.parent
parent = parent.parent as ComponentInternalInstance
}
}
@ -945,7 +945,11 @@ function resolveCssVars(
}
}
if (vnode === root && instance.parent) {
resolveCssVars(instance.parent, instance.vnode, expectedMap)
resolveCssVars(
instance.parent as ComponentInternalInstance,
instance.vnode,
expectedMap,
)
}
}

View File

@ -19,6 +19,7 @@ import {
type ComponentOptions,
type ConcreteComponent,
type Data,
type GenericComponentInstance,
type LifecycleHook,
createComponentInstance,
getComponentPublicInstance,
@ -749,7 +750,7 @@ function baseCreateRenderer(
vnode: VNode,
scopeId: string | null,
slotScopeIds: string[] | null,
parentComponent: ComponentInternalInstance | null,
parentComponent: GenericComponentInstance | null,
) => {
if (scopeId) {
hostSetScopeId(el, scopeId)
@ -774,7 +775,7 @@ function baseCreateRenderer(
(isSuspense(subTree.type) &&
(subTree.ssContent === vnode || subTree.ssFallback === vnode))
) {
const parentVNode = parentComponent!.vnode
const parentVNode = parentComponent!.vnode!
setScopeId(
el,
parentVNode,
@ -1382,8 +1383,8 @@ function baseCreateRenderer(
}
} else {
// custom element style injection
if (root.ce) {
root.ce._injectChildStyle(type)
if ((root as ComponentInternalInstance).ce) {
;(root as ComponentInternalInstance).ce!._injectChildStyle(type)
}
if (__DEV__) {

View File

@ -18,6 +18,7 @@ import {
type ComponentInternalInstance,
type ConcreteComponent,
type Data,
type GenericComponentInstance,
isClassComponent,
} from './component'
import type { RawSlots } from './componentSlots'
@ -903,7 +904,7 @@ export function mergeProps(...args: (Data & VNodeProps)[]): Data {
export function invokeVNodeHook(
hook: VNodeHook,
instance: ComponentInternalInstance | null,
instance: GenericComponentInstance | null,
vnode: VNode,
prevVNode: VNode | null = null,
): void {

View File

@ -1,5 +1,4 @@
import {
type ComponentInternalInstance,
type Data,
type GenericComponentInstance,
formatComponentName,
@ -106,9 +105,9 @@ export function getComponentTrace(): ComponentTraceStack {
})
}
if (isVNode(currentCtx)) {
const parent: ComponentInternalInstance | null =
const parent: GenericComponentInstance | null =
currentCtx.component && currentCtx.component.parent
currentCtx = parent && parent.vnode
currentCtx = (parent && parent.vnode) || parent
} else {
currentCtx = currentCtx.parent
}

View File

@ -10,6 +10,7 @@ import {
vtcKey,
} from './Transition'
import {
type ComponentInternalInstance,
type ComponentOptions,
DeprecationTypes,
Fragment,
@ -122,7 +123,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
!rawProps.tag &&
compatUtils.checkCompatEnabled(
DeprecationTypes.TRANSITION_GROUP_ROOT,
instance.parent,
instance.parent as ComponentInternalInstance,
)
) {
tag = 'span'

View File

@ -172,7 +172,7 @@ function renderComponentSubTree(
if (parent && parent.subTree && parent.subTree === cur.vnode) {
// parent is a non-SSR compiled component and is rendering this
// component as root. inherit its scopeId if present.
cur = parent
cur = parent as ComponentInternalInstance
} else {
break
}
@ -314,7 +314,7 @@ function renderElementVNode(
if (curVnode.scopeId) {
openTag += ` ${curVnode.scopeId}`
}
curParent = curParent.parent
curParent = curParent.parent as ComponentInternalInstance
}
if (slotScopeId) {
openTag += ` ${slotScopeId}`