wip: refactor parentChain management

This commit is contained in:
Evan You 2018-10-10 13:13:27 -04:00
parent ef0c6effe8
commit 5cd1f33de3
7 changed files with 251 additions and 104 deletions

View File

@ -0,0 +1,122 @@
import {
h,
Component,
render,
nodeOps,
ComponentInstance,
observable,
nextTick
} from '@vue/renderer-test'
describe('Parent chain management', () => {
it('should have correct $parent / $root / $children', async () => {
let child: any
let grandChildren: any[] = []
const state = observable({ ok: true })
class Parent extends Component {
render() {
return h(Child)
}
}
class Child extends Component {
created() {
child = this
}
render() {
return [state.ok ? h(GrandChild) : null, h(GrandChild)]
}
}
class GrandChild extends Component {
created() {
grandChildren.push(this)
}
unmounted() {
grandChildren.splice(grandChildren.indexOf(this), 1)
}
render() {}
}
const root = nodeOps.createElement('div')
const parent = render(h(Parent), root) as ComponentInstance
expect(child.$parent).toBe(parent)
expect(child.$root).toBe(parent)
grandChildren.forEach(grandChild => {
expect(grandChild.$parent).toBe(child)
expect(grandChild.$root).toBe(parent)
})
expect(parent.$children).toEqual([child])
expect(grandChildren.length).toBe(2)
expect(child.$children).toEqual(grandChildren)
state.ok = false
await nextTick()
expect(grandChildren.length).toBe(1)
expect(child.$children).toEqual(grandChildren)
})
it('should have correct $parent / $root w/ functional component in between', async () => {
let child: any
let grandChildren: any[] = []
const state = observable({ ok: true })
class Parent extends Component {
render() {
return h(FunctionalChild)
}
}
const FunctionalChild = () => h(Child)
class Child extends Component {
created() {
child = this
}
render() {
return [
state.ok ? h(FunctionalGrandChild) : null,
h(FunctionalGrandChild)
]
}
}
const FunctionalGrandChild = () => h(GrandChild)
class GrandChild extends Component {
created() {
grandChildren.push(this)
}
unmounted() {
grandChildren.splice(grandChildren.indexOf(this), 1)
}
render() {}
}
const root = nodeOps.createElement('div')
const parent = render(h(Parent), root) as ComponentInstance
expect(child.$parent).toBe(parent)
expect(child.$root).toBe(parent)
grandChildren.forEach(grandChild => {
expect(grandChild.$parent).toBe(child)
expect(grandChild.$root).toBe(parent)
})
expect(parent.$children).toEqual([child])
expect(grandChildren.length).toBe(2)
expect(child.$children).toEqual(grandChildren)
state.ok = false
await nextTick()
expect(grandChildren.length).toBe(1)
expect(child.$children).toEqual(grandChildren)
})
})

View File

@ -19,7 +19,7 @@ import { handleError, ErrorTypes } from './errorHandling'
export function createComponentInstance(
vnode: VNode,
Component: ComponentClass,
parentComponent: ComponentInstance | null
contextVNode: MountedVNode | null
): ComponentInstance {
const instance = (vnode.children = new Component()) as ComponentInstance
instance.$parentVNode = vnode as MountedVNode
@ -28,7 +28,17 @@ export function createComponentInstance(
const proxy = (instance.$proxy = createRenderProxy(instance))
// pointer management
if (parentComponent) {
if (contextVNode !== null) {
// locate first non-functional parent
while (
contextVNode !== null &&
contextVNode.flags & VNodeFlags.COMPONENT_FUNCTIONAL &&
contextVNode.contextVNode !== null
) {
contextVNode = contextVNode.contextVNode as any
}
const parentComponent = (contextVNode as VNode)
.children as ComponentInstance
instance.$parent = parentComponent.$proxy
instance.$root = parentComponent.$root
parentComponent.$children.push(proxy)

View File

@ -111,36 +111,30 @@ export function createRenderer(options: RendererOptions) {
function mount(
vnode: VNode,
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
const { flags } = vnode
if (flags & VNodeFlags.ELEMENT) {
mountElement(vnode, container, parentComponent, isSVG, endNode)
mountElement(vnode, container, contextVNode, isSVG, endNode)
} else if (flags & VNodeFlags.COMPONENT_STATEFUL) {
mountStatefulComponent(vnode, container, parentComponent, isSVG, endNode)
mountStatefulComponent(vnode, container, contextVNode, isSVG, endNode)
} else if (flags & VNodeFlags.COMPONENT_FUNCTIONAL) {
mountFunctionalComponent(
vnode,
container,
parentComponent,
isSVG,
endNode
)
mountFunctionalComponent(vnode, container, contextVNode, isSVG, endNode)
} else if (flags & VNodeFlags.TEXT) {
mountText(vnode, container, endNode)
} else if (flags & VNodeFlags.FRAGMENT) {
mountFragment(vnode, container, parentComponent, isSVG, endNode)
mountFragment(vnode, container, contextVNode, isSVG, endNode)
} else if (flags & VNodeFlags.PORTAL) {
mountPortal(vnode, container, parentComponent)
mountPortal(vnode, container, contextVNode)
}
}
function mountArrayChildren(
children: VNode[],
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
@ -149,14 +143,14 @@ export function createRenderer(options: RendererOptions) {
if (child.el) {
children[i] = child = cloneVNode(child)
}
mount(children[i], container, parentComponent, isSVG, endNode)
mount(children[i], container, contextVNode, isSVG, endNode)
}
}
function mountElement(
vnode: VNode,
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
@ -174,12 +168,12 @@ export function createRenderer(options: RendererOptions) {
if (childFlags !== ChildrenFlags.NO_CHILDREN) {
const hasSVGChildren = isSVG && tag !== 'foreignObject'
if (childFlags & ChildrenFlags.SINGLE_VNODE) {
mount(children as VNode, el, parentComponent, hasSVGChildren, endNode)
mount(children as VNode, el, contextVNode, hasSVGChildren, endNode)
} else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
mountArrayChildren(
children as VNode[],
el,
parentComponent,
contextVNode,
hasSVGChildren,
endNode
)
@ -207,10 +201,11 @@ export function createRenderer(options: RendererOptions) {
function mountStatefulComponent(
vnode: VNode,
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
vnode.contextVNode = contextVNode
if (vnode.flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) {
// kept-alive
activateComponentInstance(vnode)
@ -219,7 +214,7 @@ export function createRenderer(options: RendererOptions) {
vnode,
vnode.tag as ComponentClass,
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -229,10 +224,11 @@ export function createRenderer(options: RendererOptions) {
function mountFunctionalComponent(
vnode: VNode,
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
vnode.contextVNode = contextVNode
const { tag, data, slots } = vnode
const render = tag as FunctionalComponent
const { props, attrs } = resolveProps(data, render.props)
@ -240,7 +236,7 @@ export function createRenderer(options: RendererOptions) {
render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ),
vnode
))
mount(subTree, container, parentComponent, isSVG, endNode)
mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
vnode.el = subTree.el as RenderNode
}
@ -258,14 +254,14 @@ export function createRenderer(options: RendererOptions) {
function mountFragment(
vnode: VNode,
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
const { children, childFlags } = vnode
switch (childFlags) {
case ChildrenFlags.SINGLE_VNODE:
mount(children as VNode, container, parentComponent, isSVG, endNode)
mount(children as VNode, container, contextVNode, isSVG, endNode)
vnode.el = (children as MountedVNode).el
break
case ChildrenFlags.NO_CHILDREN:
@ -277,7 +273,7 @@ export function createRenderer(options: RendererOptions) {
mountArrayChildren(
children as VNode[],
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -288,7 +284,7 @@ export function createRenderer(options: RendererOptions) {
function mountPortal(
vnode: VNode,
container: RenderNode | null,
parentComponent: ComponentInstance | null
contextVNode: MountedVNode | null
) {
const { tag, children, childFlags, ref } = vnode
const target = typeof tag === 'string' ? platformQuerySelector(tag) : tag
@ -298,18 +294,12 @@ export function createRenderer(options: RendererOptions) {
}
if (childFlags & ChildrenFlags.SINGLE_VNODE) {
mount(
children as VNode,
target as RenderNode,
parentComponent,
false,
null
)
mount(children as VNode, target as RenderNode, contextVNode, false, null)
} else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
mountArrayChildren(
children as VNode[],
target as RenderNode,
parentComponent,
contextVNode,
false,
null
)
@ -352,24 +342,24 @@ export function createRenderer(options: RendererOptions) {
prevVNode: MountedVNode,
nextVNode: VNode,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean
) {
const nextFlags = nextVNode.flags
const prevFlags = prevVNode.flags
if (prevFlags !== nextFlags) {
replaceVNode(prevVNode, nextVNode, container, parentComponent, isSVG)
replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
} else if (nextFlags & VNodeFlags.ELEMENT) {
patchElement(prevVNode, nextVNode, container, parentComponent, isSVG)
patchElement(prevVNode, nextVNode, container, contextVNode, isSVG)
} else if (nextFlags & VNodeFlags.COMPONENT) {
patchComponent(prevVNode, nextVNode, container, parentComponent, isSVG)
patchComponent(prevVNode, nextVNode, container, contextVNode, isSVG)
} else if (nextFlags & VNodeFlags.TEXT) {
patchText(prevVNode, nextVNode)
} else if (nextFlags & VNodeFlags.FRAGMENT) {
patchFragment(prevVNode, nextVNode, container, parentComponent, isSVG)
patchFragment(prevVNode, nextVNode, container, contextVNode, isSVG)
} else if (nextFlags & VNodeFlags.PORTAL) {
patchPortal(prevVNode, nextVNode, parentComponent)
patchPortal(prevVNode, nextVNode, contextVNode)
}
}
@ -377,14 +367,14 @@ export function createRenderer(options: RendererOptions) {
prevVNode: MountedVNode,
nextVNode: VNode,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean
) {
const { flags, tag } = nextVNode
isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
if (prevVNode.tag !== tag) {
replaceVNode(prevVNode, nextVNode, container, parentComponent, isSVG)
replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
return
}
@ -434,7 +424,7 @@ export function createRenderer(options: RendererOptions) {
prevVNode.children,
nextVNode.children,
el,
parentComponent,
contextVNode,
isSVG && nextVNode.tag !== 'foreignObject',
null
)
@ -450,12 +440,13 @@ export function createRenderer(options: RendererOptions) {
prevVNode: MountedVNode,
nextVNode: VNode,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean
) {
nextVNode.contextVNode = contextVNode
const { tag, flags } = nextVNode
if (tag !== prevVNode.tag) {
replaceVNode(prevVNode, nextVNode, container, parentComponent, isSVG)
replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
} else if (flags & VNodeFlags.COMPONENT_STATEFUL) {
patchStatefulComponent(prevVNode, nextVNode)
} else {
@ -463,7 +454,7 @@ export function createRenderer(options: RendererOptions) {
prevVNode,
nextVNode,
container,
parentComponent,
contextVNode,
isSVG
)
}
@ -497,7 +488,7 @@ export function createRenderer(options: RendererOptions) {
if (shouldForceUpdate) {
instance.$forceUpdate()
} else if (instance.$vnode.flags & VNodeFlags.COMPONENT) {
instance.$vnode.parentVNode = nextVNode
instance.$vnode.contextVNode = nextVNode
}
nextVNode.el = instance.$vnode.el
}
@ -506,7 +497,7 @@ export function createRenderer(options: RendererOptions) {
prevVNode: MountedVNode,
nextVNode: VNode,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean
) {
// functional component tree is stored on the vnode as `children`
@ -526,11 +517,11 @@ export function createRenderer(options: RendererOptions) {
render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ),
nextVNode
))
patch(prevTree, nextTree, container, parentComponent, isSVG)
patch(prevTree, nextTree, container, nextVNode as MountedVNode, isSVG)
nextVNode.el = nextTree.el
} else if (prevTree.flags & VNodeFlags.COMPONENT) {
// functional component returned another component
prevTree.parentVNode = nextVNode
prevTree.contextVNode = nextVNode
}
}
@ -538,7 +529,7 @@ export function createRenderer(options: RendererOptions) {
prevVNode: MountedVNode,
nextVNode: VNode,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean
) {
// determine the tail node of the previous fragment,
@ -551,7 +542,7 @@ export function createRenderer(options: RendererOptions) {
prevVNode.children,
children,
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -595,7 +586,7 @@ export function createRenderer(options: RendererOptions) {
function patchPortal(
prevVNode: MountedVNode,
nextVNode: VNode,
parentComponent: ComponentInstance | null
contextVNode: MountedVNode | null
) {
const prevContainer = prevVNode.tag as RenderNode
const nextContainer = nextVNode.tag as RenderNode
@ -606,7 +597,7 @@ export function createRenderer(options: RendererOptions) {
prevVNode.children,
nextChildren,
prevContainer,
parentComponent,
contextVNode,
false,
null
)
@ -631,12 +622,12 @@ export function createRenderer(options: RendererOptions) {
prevVNode: MountedVNode,
nextVNode: VNode,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean
) {
const refNode = platformNextSibling(getVNodeLastEl(prevVNode))
removeVNode(prevVNode, container)
mount(nextVNode, container, parentComponent, isSVG, refNode)
mount(nextVNode, container, contextVNode, isSVG, refNode)
}
function patchChildren(
@ -645,7 +636,7 @@ export function createRenderer(options: RendererOptions) {
prevChildren: VNodeChildren,
nextChildren: VNodeChildren,
container: RenderNode,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
@ -657,7 +648,7 @@ export function createRenderer(options: RendererOptions) {
prevChildren as MountedVNode,
nextChildren as VNode,
container,
parentComponent,
contextVNode,
isSVG
)
break
@ -669,7 +660,7 @@ export function createRenderer(options: RendererOptions) {
mountArrayChildren(
nextChildren as VNode[],
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -682,7 +673,7 @@ export function createRenderer(options: RendererOptions) {
mount(
nextChildren as VNode,
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -693,7 +684,7 @@ export function createRenderer(options: RendererOptions) {
mountArrayChildren(
nextChildren as VNode[],
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -704,13 +695,7 @@ export function createRenderer(options: RendererOptions) {
// MULTIPLE_CHILDREN
if (nextChildFlags === ChildrenFlags.SINGLE_VNODE) {
removeChildren(prevChildren as MountedVNode[], container, endNode)
mount(
nextChildren as VNode,
container,
parentComponent,
isSVG,
endNode
)
mount(nextChildren as VNode, container, contextVNode, isSVG, endNode)
} else if (nextChildFlags === ChildrenFlags.NO_CHILDREN) {
removeChildren(prevChildren as MountedVNode[], container, endNode)
} else {
@ -721,7 +706,7 @@ export function createRenderer(options: RendererOptions) {
mountArrayChildren(
nextChildren as VNode[],
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -738,7 +723,7 @@ export function createRenderer(options: RendererOptions) {
container,
prevLength,
nextLength,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -749,7 +734,7 @@ export function createRenderer(options: RendererOptions) {
container,
prevLength,
nextLength,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -765,7 +750,7 @@ export function createRenderer(options: RendererOptions) {
container: RenderNode,
prevLength: number,
nextLength: number,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
@ -779,7 +764,7 @@ export function createRenderer(options: RendererOptions) {
if (nextChild.el) {
nextChildren[i] = nextChild = cloneVNode(nextChild)
}
patch(prevChild, nextChild, container, parentComponent, isSVG)
patch(prevChild, nextChild, container, contextVNode, isSVG)
prevChildren[i] = nextChild as MountedVNode
}
if (prevLength < nextLength) {
@ -788,7 +773,7 @@ export function createRenderer(options: RendererOptions) {
if (nextChild.el) {
nextChildren[i] = nextChild = cloneVNode(nextChild)
}
mount(nextChild, container, parentComponent, isSVG, endNode)
mount(nextChild, container, contextVNode, isSVG, endNode)
}
} else if (prevLength > nextLength) {
for (i = commonLength; i < prevLength; i++) {
@ -803,7 +788,7 @@ export function createRenderer(options: RendererOptions) {
container: RenderNode,
prevLength: number,
nextLength: number,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
@ -821,7 +806,7 @@ export function createRenderer(options: RendererOptions) {
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, parentComponent, isSVG)
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
prevChildren[j] = nextVNode as MountedVNode
j++
if (j > prevEnd || j > nextEnd) {
@ -839,7 +824,7 @@ export function createRenderer(options: RendererOptions) {
if (nextVNode.el) {
nextChildren[nextEnd] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, parentComponent, isSVG)
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
prevChildren[prevEnd] = nextVNode as MountedVNode
prevEnd--
nextEnd--
@ -862,7 +847,7 @@ export function createRenderer(options: RendererOptions) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
j++
mount(nextVNode, container, parentComponent, isSVG, nextNode)
mount(nextVNode, container, contextVNode, isSVG, nextNode)
}
}
} else if (j > nextEnd) {
@ -907,7 +892,7 @@ export function createRenderer(options: RendererOptions) {
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, parentComponent, isSVG)
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
patched++
break
}
@ -951,7 +936,7 @@ export function createRenderer(options: RendererOptions) {
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, parentComponent, isSVG)
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
patched++
} else if (!canRemoveWholeContent) {
removeVNode(prevVNode, container)
@ -967,7 +952,7 @@ export function createRenderer(options: RendererOptions) {
mountArrayChildren(
nextChildren,
container,
parentComponent,
contextVNode,
isSVG,
endNode
)
@ -986,7 +971,7 @@ export function createRenderer(options: RendererOptions) {
mount(
nextVNode,
container,
parentComponent,
contextVNode,
isSVG,
nextPos < nextLength ? nextChildren[nextPos].el : endNode
)
@ -1017,7 +1002,7 @@ export function createRenderer(options: RendererOptions) {
mount(
nextVNode,
container,
parentComponent,
contextVNode,
isSVG,
nextPos < nextLength ? nextChildren[nextPos].el : endNode
)
@ -1149,18 +1134,18 @@ export function createRenderer(options: RendererOptions) {
// Component lifecycle -------------------------------------------------------
function mountComponentInstance(
parentVNode: VNode,
vnode: VNode,
Component: ComponentClass,
container: RenderNode | null,
parentComponent: ComponentInstance | null,
contextVNode: MountedVNode | null,
isSVG: boolean,
endNode: RenderNode | null
): RenderNode {
// a vnode may already have an instance if this is a compat call with
// new Vue()
const instance =
(__COMPAT__ && (parentVNode.children as ComponentInstance)) ||
createComponentInstance(parentVNode, Component, parentComponent)
(__COMPAT__ && (vnode.children as ComponentInstance)) ||
createComponentInstance(vnode, Component, contextVNode)
// inject platform-specific unmount to keep-alive container
if ((Component as any)[KeepAliveSymbol] === true) {
@ -1185,16 +1170,22 @@ export function createRenderer(options: RendererOptions) {
} else {
// this will be executed synchronously right here
instance.$vnode = renderInstanceRoot(instance) as MountedVNode
mount(instance.$vnode, container, instance, isSVG, endNode)
parentVNode.el = instance.$vnode.el
mount(
instance.$vnode,
container,
vnode as MountedVNode,
isSVG,
endNode
)
vnode.el = instance.$vnode.el
if (__DEV__) {
// expose __vue__ for devtools
;(parentVNode.el as any).__vue__ = instance
;(vnode.el as any).__vue__ = instance
}
instance._mounted = true
mountComponentInstanceCallbacks(instance, parentVNode.ref)
mountComponentInstanceCallbacks(instance, vnode.ref)
}
},
{
@ -1204,7 +1195,7 @@ export function createRenderer(options: RendererOptions) {
}
)
return parentVNode.el as RenderNode
return vnode.el as RenderNode
}
function mountComponentInstanceCallbacks(
@ -1212,7 +1203,7 @@ export function createRenderer(options: RendererOptions) {
ref: Ref | null
) {
if (ref) {
mountRef(ref, instance)
mountRef(ref, instance.$proxy)
}
if (instance.mounted) {
lifecycleHooks.push(() => {
@ -1235,7 +1226,13 @@ export function createRenderer(options: RendererOptions) {
instance
) as MountedVNode)
const container = platformParentNode(prevVNode.el) as RenderNode
patch(prevVNode, nextVNode, container, instance, isSVG)
patch(
prevVNode,
nextVNode,
container,
instance.$parentVNode as MountedVNode,
isSVG
)
const el = nextVNode.el as RenderNode
if (__DEV__) {
@ -1243,14 +1240,14 @@ export function createRenderer(options: RendererOptions) {
;(el as any).__vue__ = instance
}
// recursively update parentVNode el for nested HOCs
// recursively update contextVNode el for nested HOCs
if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
let vnode = instance.$parentVNode
while (vnode !== null) {
if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
vnode.el = el
}
vnode = vnode.parentVNode
vnode = vnode.contextVNode
}
}
@ -1354,7 +1351,10 @@ export function createRenderer(options: RendererOptions) {
// API -----------------------------------------------------------------------
function render(vnode: VNode | null, container: any) {
function render(
vnode: VNode | null,
container: any
): ComponentInstance | null {
const prevVNode = container.vnode
if (vnode && vnode.el) {
vnode = cloneVNode(vnode)

View File

@ -24,7 +24,12 @@ export { createAsyncComponent } from './optional/asyncComponent'
export { KeepAlive } from './optional/keepAlive'
// flags & types
export { ComponentType, ComponentClass, FunctionalComponent } from './component'
export {
ComponentType,
ComponentClass,
FunctionalComponent,
ComponentInstance
} from './component'
export * from './componentOptions'
export { VNodeFlags, ChildrenFlags } from './flags'
export { VNode, Slots } from './vdom'

View File

@ -31,6 +31,9 @@ export interface VNode {
// points to parent component's placeholder vnode
// this is used to update vnode.el for nested HOCs.
parentVNode: VNode | null
// only on mounted component nodes
// points to the parent stateful/functional component's placeholder node
contextVNode: VNode | null
}
export interface MountedVNode extends VNode {
@ -84,7 +87,8 @@ export function createVNode(
ref: ref === void 0 ? null : ref,
slots: slots === void 0 ? null : slots,
el: null,
parentVNode: null
parentVNode: null,
contextVNode: null
}
if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
normalizeChildren(vnode, children)

View File

@ -1,4 +1,4 @@
import { createRenderer, VNode } from '@vue/core'
import { createRenderer, VNode, ComponentInstance } from '@vue/core'
import { nodeOps } from './nodeOps'
import { patchData } from './patchData'
import { teardownVNode } from './teardownVNode'
@ -9,7 +9,10 @@ const { render: _render } = createRenderer({
teardownVNode
})
type publicRender = (node: VNode | null, container: HTMLElement) => void
type publicRender = (
node: VNode | null,
container: HTMLElement
) => ComponentInstance | null
export const render = _render as publicRender
// re-export everything from core

View File

@ -1,4 +1,4 @@
import { createRenderer, VNode } from '@vue/core'
import { createRenderer, VNode, ComponentInstance } from '@vue/core'
import { nodeOps, TestElement } from './nodeOps'
import { patchData } from './patchData'
@ -7,7 +7,10 @@ const { render: _render } = createRenderer({
patchData
})
type publicRender = (node: VNode | null, container: TestElement) => void
type publicRender = (
node: VNode | null,
container: TestElement
) => ComponentInstance | null
export const render = _render as publicRender
export { serialize } from './serialize'