) {
// patching & not same type, unmount old tree
if (n1 != null && !isSameType(n1, n2)) {
- anchor = hostNextSibling(n1.el)
+ anchor = hostNextSibling(n1.anchor || n1.el)
unmount(n1, true)
n1 = null
}
anchor?: HostNode,
optimized?: boolean
) {
- const fragmentAnchor = (n2.el = n1 ? n1.el : document.createComment(''))
+ const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateComment(''))
+ const fragmentEndAnchor = (n2.anchor = n1
+ ? n1.anchor
+ : hostCreateComment(''))
if (n1 == null) {
- insert(fragmentAnchor, container, anchor)
+ insert(fragmentStartAnchor, container, anchor)
+ insert(fragmentEndAnchor, container, anchor)
// a fragment can only have array children
- mountChildren(n2.children as VNodeChildren, container, fragmentAnchor)
+ mountChildren(n2.children as VNodeChildren, container, fragmentEndAnchor)
} else {
- patchChildren(n1, n2, container, fragmentAnchor, optimized)
+ patchChildren(n1, n2, container, fragmentEndAnchor, optimized)
}
}
patchKeyedChildren(c1 as VNode[], c2, container, anchor, optimized)
} else {
// c2 is null in this case
- unmountChildren(c1 as VNode[], 0, true)
+ unmountChildren(c1 as VNode[], true)
}
}
}
}
if (oldLength > newLength) {
// remove old
- unmountChildren(c1, commonLength, true)
+ unmountChildren(c1, true, commonLength)
} else {
// mount new
mountChildren(c2, container, anchor, commonLength)
anchor?: HostNode,
optimized?: boolean
) {
- // TODO
- patchUnkeyedChildren(c1, c2, container, anchor, optimized)
+ let i = 0
+ let e1 = c1.length - 1
+ let e2 = c2.length - 1
+
+ // 1. sync from start
+ // (a b) c
+ // (a b) d e
+ while (i <= e1 && i <= e2) {
+ const n1 = c1[i]
+ const n2 = (c2[i] = normalizeChild(c2[i]))
+ if (isSameType(n1, n2)) {
+ patch(n1, n2, container, anchor, optimized)
+ } else {
+ break
+ }
+ i++
+ }
+
+ // 2. sync from end
+ // a (b c)
+ // d e (b c)
+ while (i <= e1 && i <= e2) {
+ const n1 = c1[e1]
+ const n2 = (c2[e2] = normalizeChild(c2[e2]))
+ if (isSameType(n1, n2)) {
+ patch(n1, n2, container, anchor, optimized)
+ } else {
+ break
+ }
+ e1--
+ e2--
+ }
+
+ // 3. common sequence + mount
+ // (a b)
+ // (a b) c
+ // i = 2, e1 = 1, e2 = 2
+ // (a b)
+ // c (a b)
+ // i = 0, e1 = -1, e2 = 0
+ if (i > e1) {
+ if (i <= e2) {
+ const nextPos = e2 + 1
+ const nextAnchor =
+ nextPos < c2.length ? (c2[nextPos] as VNode).el : anchor
+ while (i <= e2) {
+ patch(null, (c2[i] = normalizeChild(c2[i])), container, nextAnchor)
+ i++
+ }
+ }
+ }
+
+ // 4. common sequence + unmount
+ // (a b) c
+ // (a b)
+ // i = 2, e1 = 2, e2 = 1
+ // a (b c)
+ // (b c)
+ // i = 0, e1 = 0, e2 = -1
+ else if (i > e2) {
+ while (i <= e1) {
+ unmount(c1[i], true)
+ i++
+ }
+ }
+
+ // 5. inner diff
+ // - build key:index map
+ // - loop through old and try to patch / remove
+ // - if moved: apply minimal move w/ levenshtein distance
+ // - no move: mount and insert new ones
+ else {
+ }
}
function unmount(vnode: VNode, doRemove?: boolean) {
+ const shouldRemoveChildren = vnode.type === Fragment && doRemove
if (vnode.dynamicChildren != null) {
- unmountChildren(vnode.dynamicChildren)
+ unmountChildren(vnode.dynamicChildren, shouldRemoveChildren)
} else if (Array.isArray(vnode.children)) {
- unmountChildren(vnode.children as VNode[])
+ unmountChildren(vnode.children as VNode[], shouldRemoveChildren)
}
if (doRemove) {
- if (vnode.type === Fragment) {
- // raw VNodeChildren is normalized to VNode[] when the VNode is patched
- unmountChildren(vnode.children as VNode[], 0, doRemove)
- }
remove(vnode.el)
+ if (vnode.anchor != null) remove(vnode.anchor)
}
}
function unmountChildren(
children: VNode[],
- start: number = 0,
- doRemove?: boolean
+ doRemove?: boolean,
+ start: number = 0
) {
for (let i = start; i < children.length; i++) {
unmount(children[i], doRemove)
export interface VNode {
el: any
+ anchor: any // fragment anchor
type: VNodeTypes
props: { [key: string]: any } | null
key: string | number | null
dynamicChildren: VNode[] | null
}
-const blockStack: (VNode[])[] = []
+const blockStack: (VNode[] | null)[] = []
// open block
-export function openBlock() {
- blockStack.push([])
+export function openBlock(disableTrackng?: boolean) {
+ blockStack.push(disableTrackng ? null : [])
}
let shouldTrack = true
shouldTrack = false
const vnode = createVNode(type, props, children, patchFlag, dynamicProps)
shouldTrack = true
- vnode.dynamicChildren = blockStack.pop() || null
+ const trackedNodes = blockStack.pop()
+ vnode.dynamicChildren =
+ trackedNodes && trackedNodes.length ? trackedNodes : null
// a block is always going to be patched
trackDynamicNode(vnode)
return vnode
dynamicProps: string[] | null = null
): VNode {
const vnode: VNode = {
- el: null,
type,
props,
key: props && props.key,
children,
+ el: null,
+ anchor: null,
patchFlag,
dynamicProps,
dynamicChildren: null
function trackDynamicNode(vnode: VNode) {
const currentBlockDynamicNodes = blockStack[blockStack.length - 1]
- if (currentBlockDynamicNodes) {
+ if (currentBlockDynamicNodes != null) {
currentBlockDynamicNodes.push(vnode)
}
}