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

View File

@ -504,6 +504,26 @@ export interface GenericComponentInstance {
* @internal vapor only * @internal vapor only
*/ */
hmrReload?: (newComp: any) => void 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 vapor?: never
uid: number uid: number
type: ConcreteComponent type: ConcreteComponent
parent: ComponentInternalInstance | null parent: GenericComponentInstance | null
root: ComponentInternalInstance root: GenericComponentInstance
appContext: AppContext appContext: AppContext
/** /**
* Vnode representing this component in its parent's vdom tree * Vnode representing this component in its parent's vdom tree
@ -589,21 +609,6 @@ export interface ComponentInternalInstance extends GenericComponentInstance {
* @internal * @internal
*/ */
inheritAttrs?: boolean 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 --------------------------------- // the rest are only for stateful components ---------------------------------
/** /**
@ -1210,7 +1215,7 @@ export function expose(
} }
export function getComponentPublicInstance( export function getComponentPublicInstance(
instance: ComponentInternalInstance, instance: GenericComponentInstance,
): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null { ): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null {
if (instance.exposed) { if (instance.exposed) {
return ( return (

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import {
type ComponentInternalInstance, type ComponentInternalInstance,
type ComponentOptions, type ComponentOptions,
type ConcreteComponent, type ConcreteComponent,
type GenericComponentInstance,
type SetupContext, type SetupContext,
getComponentName, getComponentName,
getCurrentInstance, getCurrentInstance,
@ -436,7 +437,7 @@ function registerKeepAliveHook(
hook.__wdc || hook.__wdc ||
(hook.__wdc = () => { (hook.__wdc = () => {
// only fire the hook if the target instance is NOT in a deactivated branch. // 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) { while (current) {
if (current.isDeactivated) { if (current.isDeactivated) {
return return
@ -453,7 +454,7 @@ function registerKeepAliveHook(
// arrays. // arrays.
if (target) { if (target) {
let current = target.parent let current = target.parent
while (current && current.parent) { while (current && current.parent && current.parent.vnode) {
if (isKeepAlive(current.parent.vnode)) { if (isKeepAlive(current.parent.vnode)) {
injectToKeepAliveRoot(wrappedHook, type, target, current) injectToKeepAliveRoot(wrappedHook, type, target, current)
} }
@ -466,7 +467,7 @@ function injectToKeepAliveRoot(
hook: Function & { __weh?: Function }, hook: Function & { __weh?: Function },
type: LifecycleHooks, type: LifecycleHooks,
target: ComponentInternalInstance, target: ComponentInternalInstance,
keepAliveRoot: ComponentInternalInstance, keepAliveRoot: GenericComponentInstance,
) { ) {
// injectHook wraps the original for error handling, so make sure to remove // injectHook wraps the original for error handling, so make sure to remove
// the wrapped version. // 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. // don't end up forcing the same parent to re-render multiple times.
queueJob(() => { queueJob(() => {
isHmrUpdating = true isHmrUpdating = true
instance.parent!.update() const parent = instance.parent!
if (parent.vapor) {
parent.hmrRerender!()
} else {
;(parent as ComponentInternalInstance).update()
}
isHmrUpdating = false isHmrUpdating = false
// #6930, #11248 avoid infinite recursion // #6930, #11248 avoid infinite recursion
dirtyInstances.delete(instance) dirtyInstances.delete(instance)

View File

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

View File

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

View File

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

View File

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

View File

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