feat: template ref handling + ref unmount
This commit is contained in:
parent
10a2cf47ea
commit
8f9afdff64
@ -31,7 +31,14 @@ import {
|
|||||||
FULL_PROPS
|
FULL_PROPS
|
||||||
} from './patchFlags'
|
} from './patchFlags'
|
||||||
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
|
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
|
||||||
import { effect, stop, ReactiveEffectOptions } from '@vue/reactivity'
|
import {
|
||||||
|
effect,
|
||||||
|
stop,
|
||||||
|
ReactiveEffectOptions,
|
||||||
|
isRef,
|
||||||
|
Ref,
|
||||||
|
toRaw
|
||||||
|
} from '@vue/reactivity'
|
||||||
import { resolveProps } from './componentProps'
|
import { resolveProps } from './componentProps'
|
||||||
import { resolveSlots } from './componentSlots'
|
import { resolveSlots } from './componentSlots'
|
||||||
import {
|
import {
|
||||||
@ -80,7 +87,11 @@ export interface RendererOptions {
|
|||||||
oldValue: any,
|
oldValue: any,
|
||||||
isSVG: boolean,
|
isSVG: boolean,
|
||||||
prevChildren?: VNode[],
|
prevChildren?: VNode[],
|
||||||
unmountChildren?: (children: VNode[]) => void
|
parentComponent?: ComponentInstance | null,
|
||||||
|
unmountChildren?: (
|
||||||
|
children: VNode[],
|
||||||
|
parentComponent: ComponentInstance | null
|
||||||
|
) => void
|
||||||
): void
|
): void
|
||||||
insert(el: HostNode, parent: HostNode, anchor?: HostNode): void
|
insert(el: HostNode, parent: HostNode, anchor?: HostNode): void
|
||||||
remove(el: HostNode): void
|
remove(el: HostNode): void
|
||||||
@ -121,7 +132,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
// patching & not same type, unmount old tree
|
// patching & not same type, unmount old tree
|
||||||
if (n1 != null && !isSameType(n1, n2)) {
|
if (n1 != null && !isSameType(n1, n2)) {
|
||||||
anchor = getNextHostNode(n1)
|
anchor = getNextHostNode(n1)
|
||||||
unmount(n1, true)
|
unmount(n1, parentComponent, true)
|
||||||
n1 = null
|
n1 = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +248,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
patchElement(n1, n2, parentComponent, isSVG, optimized)
|
patchElement(n1, n2, parentComponent, isSVG, optimized)
|
||||||
}
|
}
|
||||||
if (n2.ref !== null && parentComponent !== null) {
|
if (n2.ref !== null && parentComponent !== null) {
|
||||||
setRef(n2.ref, parentComponent, n2.el)
|
setRef(n2.ref, n1 && n1.ref, parentComponent, n2.el)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +317,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
|
|
||||||
if (patchFlag & FULL_PROPS) {
|
if (patchFlag & FULL_PROPS) {
|
||||||
// element props contain dynamic keys, full diff needed
|
// element props contain dynamic keys, full diff needed
|
||||||
patchProps(el, n2, oldProps, newProps, isSVG)
|
patchProps(el, n2, oldProps, newProps, parentComponent, isSVG)
|
||||||
} else {
|
} else {
|
||||||
// class
|
// class
|
||||||
// this flag is matched when the element has dynamic class bindings.
|
// this flag is matched when the element has dynamic class bindings.
|
||||||
@ -343,6 +354,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
prev,
|
prev,
|
||||||
isSVG,
|
isSVG,
|
||||||
n1.children as VNode[],
|
n1.children as VNode[],
|
||||||
|
parentComponent,
|
||||||
unmountChildren
|
unmountChildren
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -361,7 +373,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
} else if (!optimized) {
|
} else if (!optimized) {
|
||||||
// unoptimized, full diff
|
// unoptimized, full diff
|
||||||
patchProps(el, n2, oldProps, newProps, isSVG)
|
patchProps(el, n2, oldProps, newProps, parentComponent, isSVG)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dynamicChildren != null) {
|
if (dynamicChildren != null) {
|
||||||
@ -389,6 +401,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
oldProps: any,
|
oldProps: any,
|
||||||
newProps: any,
|
newProps: any,
|
||||||
|
parentComponent: ComponentInstance | null,
|
||||||
isSVG: boolean
|
isSVG: boolean
|
||||||
) {
|
) {
|
||||||
if (oldProps !== newProps) {
|
if (oldProps !== newProps) {
|
||||||
@ -404,6 +417,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
prev,
|
prev,
|
||||||
isSVG,
|
isSVG,
|
||||||
vnode.children as VNode[],
|
vnode.children as VNode[],
|
||||||
|
parentComponent,
|
||||||
unmountChildren
|
unmountChildren
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -419,6 +433,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
null,
|
null,
|
||||||
isSVG,
|
isSVG,
|
||||||
vnode.children as VNode[],
|
vnode.children as VNode[],
|
||||||
|
parentComponent,
|
||||||
unmountChildren
|
unmountChildren
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -550,6 +565,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (n2.ref !== null && parentComponent !== null) {
|
if (n2.ref !== null && parentComponent !== null) {
|
||||||
setRef(
|
setRef(
|
||||||
n2.ref,
|
n2.ref,
|
||||||
|
n1 && n1.ref,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
(n2.component as ComponentInstance).renderProxy
|
(n2.component as ComponentInstance).renderProxy
|
||||||
)
|
)
|
||||||
@ -690,7 +706,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (shapeFlag & TEXT_CHILDREN) {
|
if (shapeFlag & TEXT_CHILDREN) {
|
||||||
// text children fast path
|
// text children fast path
|
||||||
if (prevShapeFlag & ARRAY_CHILDREN) {
|
if (prevShapeFlag & ARRAY_CHILDREN) {
|
||||||
unmountChildren(c1 as VNode[])
|
unmountChildren(c1 as VNode[], parentComponent)
|
||||||
}
|
}
|
||||||
hostSetElementText(container, c2 as string)
|
hostSetElementText(container, c2 as string)
|
||||||
} else {
|
} else {
|
||||||
@ -719,7 +735,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// c2 is null in this case
|
// c2 is null in this case
|
||||||
unmountChildren(c1 as VNode[], true)
|
unmountChildren(c1 as VNode[], parentComponent, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -754,7 +770,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
if (oldLength > newLength) {
|
if (oldLength > newLength) {
|
||||||
// remove old
|
// remove old
|
||||||
unmountChildren(c1, true, commonLength)
|
unmountChildren(c1, parentComponent, true, commonLength)
|
||||||
} else {
|
} else {
|
||||||
// mount new
|
// mount new
|
||||||
mountChildren(c2, container, anchor, parentComponent, isSVG, commonLength)
|
mountChildren(c2, container, anchor, parentComponent, isSVG, commonLength)
|
||||||
@ -855,7 +871,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
// i = 0, e1 = 0, e2 = -1
|
// i = 0, e1 = 0, e2 = -1
|
||||||
else if (i > e2) {
|
else if (i > e2) {
|
||||||
while (i <= e1) {
|
while (i <= e1) {
|
||||||
unmount(c1[i], true)
|
unmount(c1[i], parentComponent, true)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -898,7 +914,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
const prevChild = c1[i]
|
const prevChild = c1[i]
|
||||||
if (patched >= toBePatched) {
|
if (patched >= toBePatched) {
|
||||||
// all new children have been patched so this can only be a removal
|
// all new children have been patched so this can only be a removal
|
||||||
unmount(prevChild, true)
|
unmount(prevChild, parentComponent, true)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let newIndex
|
let newIndex
|
||||||
@ -914,7 +930,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newIndex === undefined) {
|
if (newIndex === undefined) {
|
||||||
unmount(prevChild, true)
|
unmount(prevChild, parentComponent, true)
|
||||||
} else {
|
} else {
|
||||||
newIndexToOldIndexMap[newIndex - s2] = i + 1
|
newIndexToOldIndexMap[newIndex - s2] = i + 1
|
||||||
if (newIndex >= maxNewIndexSoFar) {
|
if (newIndex >= maxNewIndexSoFar) {
|
||||||
@ -981,28 +997,45 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmount(vnode: VNode, doRemove?: boolean) {
|
function unmount(
|
||||||
|
vnode: VNode,
|
||||||
|
parentComponent: ComponentInstance | null,
|
||||||
|
doRemove?: boolean
|
||||||
|
) {
|
||||||
|
// unset ref
|
||||||
|
if (vnode.ref !== null && parentComponent !== null) {
|
||||||
|
setRef(vnode.ref, null, parentComponent, null)
|
||||||
|
}
|
||||||
|
|
||||||
const instance = vnode.component
|
const instance = vnode.component
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
unmountComponent(instance, doRemove)
|
unmountComponent(instance, doRemove)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldRemoveChildren = vnode.type === Fragment && doRemove
|
const shouldRemoveChildren = vnode.type === Fragment && doRemove
|
||||||
if (vnode.dynamicChildren != null) {
|
if (vnode.dynamicChildren != null) {
|
||||||
unmountChildren(vnode.dynamicChildren, shouldRemoveChildren)
|
unmountChildren(
|
||||||
|
vnode.dynamicChildren,
|
||||||
|
parentComponent,
|
||||||
|
shouldRemoveChildren
|
||||||
|
)
|
||||||
} else if (vnode.shapeFlag & ARRAY_CHILDREN) {
|
} else if (vnode.shapeFlag & ARRAY_CHILDREN) {
|
||||||
unmountChildren(vnode.children as VNode[], shouldRemoveChildren)
|
unmountChildren(
|
||||||
|
vnode.children as VNode[],
|
||||||
|
parentComponent,
|
||||||
|
shouldRemoveChildren
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doRemove) {
|
if (doRemove) {
|
||||||
hostRemove(vnode.el)
|
hostRemove(vnode.el)
|
||||||
if (vnode.anchor != null) hostRemove(vnode.anchor)
|
if (vnode.anchor != null) hostRemove(vnode.anchor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmountComponent(
|
function unmountComponent(instance: ComponentInstance, doRemove?: boolean) {
|
||||||
{ bum, effects, update, subTree, um }: ComponentInstance,
|
const { bum, effects, update, subTree, um } = instance
|
||||||
doRemove?: boolean
|
|
||||||
) {
|
|
||||||
// beforeUnmount hook
|
// beforeUnmount hook
|
||||||
if (bum !== null) {
|
if (bum !== null) {
|
||||||
invokeHooks(bum)
|
invokeHooks(bum)
|
||||||
@ -1013,7 +1046,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stop(update)
|
stop(update)
|
||||||
unmount(subTree, doRemove)
|
unmount(subTree, instance, doRemove)
|
||||||
// unmounted hook
|
// unmounted hook
|
||||||
if (um !== null) {
|
if (um !== null) {
|
||||||
queuePostFlushCb(um)
|
queuePostFlushCb(um)
|
||||||
@ -1022,11 +1055,12 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
|
|
||||||
function unmountChildren(
|
function unmountChildren(
|
||||||
children: VNode[],
|
children: VNode[],
|
||||||
|
parentComponent: ComponentInstance | null,
|
||||||
doRemove?: boolean,
|
doRemove?: boolean,
|
||||||
start: number = 0
|
start: number = 0
|
||||||
) {
|
) {
|
||||||
for (let i = start; i < children.length; i++) {
|
for (let i = start; i < children.length; i++) {
|
||||||
unmount(children[i], doRemove)
|
unmount(children[i], parentComponent, doRemove)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1037,13 +1071,35 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setRef(
|
function setRef(
|
||||||
ref: string | Function,
|
ref: string | Function | Ref<any>,
|
||||||
|
oldRef: string | Function | Ref<any> | null,
|
||||||
parent: ComponentInstance,
|
parent: ComponentInstance,
|
||||||
value: HostNode | ComponentInstance
|
value: HostNode | ComponentInstance | null
|
||||||
) {
|
) {
|
||||||
const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
|
const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
|
||||||
|
const rawData = toRaw(parent.data)
|
||||||
|
|
||||||
|
// unset old ref
|
||||||
|
if (oldRef !== null && oldRef !== ref) {
|
||||||
|
if (isString(oldRef)) {
|
||||||
|
refs[oldRef] = null
|
||||||
|
const oldSetupRef = rawData[oldRef]
|
||||||
|
if (isRef(oldSetupRef)) {
|
||||||
|
oldSetupRef.value = null
|
||||||
|
}
|
||||||
|
} else if (isRef(oldRef)) {
|
||||||
|
oldRef.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isString(ref)) {
|
if (isString(ref)) {
|
||||||
|
const setupRef = rawData[ref]
|
||||||
|
if (isRef(setupRef)) {
|
||||||
|
setupRef.value = value
|
||||||
|
}
|
||||||
refs[ref] = value
|
refs[ref] = value
|
||||||
|
} else if (isRef(ref)) {
|
||||||
|
ref.value = value
|
||||||
} else {
|
} else {
|
||||||
if (__DEV__ && !isFunction(ref)) {
|
if (__DEV__ && !isFunction(ref)) {
|
||||||
// TODO warn invalid ref type
|
// TODO warn invalid ref type
|
||||||
@ -1055,7 +1111,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
return function render(vnode: VNode | null, dom: HostNode): VNode | null {
|
return function render(vnode: VNode | null, dom: HostNode): VNode | null {
|
||||||
if (vnode == null) {
|
if (vnode == null) {
|
||||||
if (dom._vnode) {
|
if (dom._vnode) {
|
||||||
unmount(dom._vnode, true)
|
unmount(dom._vnode, null, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
patch(dom._vnode, vnode, dom)
|
patch(dom._vnode, vnode, dom)
|
||||||
|
@ -3,10 +3,11 @@ export function patchDOMProp(
|
|||||||
key: string,
|
key: string,
|
||||||
value: any,
|
value: any,
|
||||||
prevChildren: any,
|
prevChildren: any,
|
||||||
|
parentComponent: any,
|
||||||
unmountChildren: any
|
unmountChildren: any
|
||||||
) {
|
) {
|
||||||
if ((key === 'innerHTML' || key === 'textContent') && prevChildren != null) {
|
if ((key === 'innerHTML' || key === 'textContent') && prevChildren != null) {
|
||||||
unmountChildren(prevChildren)
|
unmountChildren(prevChildren, parentComponent)
|
||||||
}
|
}
|
||||||
el[key] = value
|
el[key] = value
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ export function patchProp(
|
|||||||
prevValue: any,
|
prevValue: any,
|
||||||
isSVG: boolean,
|
isSVG: boolean,
|
||||||
prevChildren?: VNode[],
|
prevChildren?: VNode[],
|
||||||
|
parentComponent?: any,
|
||||||
unmountChildren?: any
|
unmountChildren?: any
|
||||||
) {
|
) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -27,7 +28,14 @@ export function patchProp(
|
|||||||
if (isOn(key)) {
|
if (isOn(key)) {
|
||||||
patchEvent(el, key.slice(2).toLowerCase(), prevValue, nextValue)
|
patchEvent(el, key.slice(2).toLowerCase(), prevValue, nextValue)
|
||||||
} else if (!isSVG && key in el) {
|
} else if (!isSVG && key in el) {
|
||||||
patchDOMProp(el, key, nextValue, prevChildren, unmountChildren)
|
patchDOMProp(
|
||||||
|
el,
|
||||||
|
key,
|
||||||
|
nextValue,
|
||||||
|
prevChildren,
|
||||||
|
parentComponent,
|
||||||
|
unmountChildren
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
patchAttr(el, key, nextValue, isSVG)
|
patchAttr(el, key, nextValue, isSVG)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user