Text,
Fragment,
Empty,
- createVNode,
+ normalizeVNode,
VNode,
VNodeChildren
} from './vnode.js'
import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
+import { effect } from '@vue/observer'
import { isString, isFunction, isArray } from '@vue/shared'
+import { renderComponentRoot, shouldUpdateComponent } from './component.js'
const emptyArr: any[] = []
const emptyObj: { [key: string]: any } = {}
createComment(text: string): HostNode
setText(node: HostNode, text: string): void
setElementText(node: HostNode, text: string): void
+ parentNode(node: HostNode): HostNode | null
nextSibling(node: HostNode): HostNode | null
}
createComment: hostCreateComment,
setText: hostSetText,
setElementText: hostSetElementText,
+ parentNode: hostParentNode,
nextSibling: hostNextSibling
} = options
) {
// patching & not same type, unmount old tree
if (n1 != null && !isSameType(n1, n2)) {
- anchor = hostNextSibling(n1.anchor || n1.el)
+ anchor = getNextHostNode(n1)
unmount(n1, true)
n1 = null
}
start: number = 0
) {
for (let i = start; i < children.length; i++) {
- const child = (children[i] = normalizeChild(children[i]))
+ const child = (children[i] = normalizeVNode(children[i]))
patch(null, child, container, anchor)
}
}
- function normalizeChild(child: any): VNode {
- if (child == null) {
- // empty placeholder
- return createVNode(Empty)
- } else if (isArray(child)) {
- // fragment
- return createVNode(Fragment, null, child)
- } else if (typeof child === 'object') {
- // already vnode
- return child as VNode
- } else {
- // primitive types
- return createVNode(Text, null, child + '')
- }
- }
-
function patchElement(n1: VNode, n2: VNode, optimized?: boolean) {
const el = (n2.el = n1.el)
const { patchFlag, dynamicChildren } = n2
n2: VNode,
container: HostNode,
anchor?: HostNode
- ) {}
+ ) {
+ if (n1 == null) {
+ mountComponent(n2, container, anchor)
+ } else {
+ updateComponent(n1.component, n2, container, anchor)
+ }
+ }
+
+ function mountComponent(
+ vnode: VNode,
+ container: HostNode,
+ anchor?: HostNode
+ ) {
+ const instance = (vnode.component = {
+ vnode: null,
+ subTree: null,
+ updateHandle: null,
+ render: vnode.type
+ } as any)
+
+ instance.updateHandle = effect(
+ () => {
+ if (!instance.vnode) {
+ // initial mount
+ instance.vnode = vnode
+ const subTree = (instance.subTree = renderComponentRoot(instance))
+ patch(null, subTree, container, anchor)
+ vnode.el = subTree.el
+ } else {
+ updateComponent(instance, vnode)
+ }
+ },
+ {
+ scheduler: e => e() // TODO use proper scheduler
+ }
+ ) as any
+ }
+
+ function updateComponent(
+ instance: any,
+ next: VNode,
+ container?: HostNode,
+ anchor?: HostNode
+ ) {
+ const prev = instance.vnode
+ instance.vnode = next
+ next.component = instance
+ if (shouldUpdateComponent(prev, next)) {
+ const prevTree = instance.subTree
+ const nextTree = (instance.subTree = renderComponentRoot(instance))
+ patch(
+ prevTree,
+ nextTree,
+ container || hostParentNode(prevTree.el),
+ anchor || getNextHostNode(prevTree)
+ )
+ next.el = nextTree.el
+ }
+ }
function patchChildren(
n1: VNode | null,
const commonLength = Math.min(oldLength, newLength)
let i
for (i = 0; i < commonLength; i++) {
- const nextChild = (c2[i] = normalizeChild(c2[i]))
+ const nextChild = (c2[i] = normalizeVNode(c2[i]))
patch(c1[i], nextChild, container, null, optimized)
}
if (oldLength > newLength) {
// (a b) d e
while (i <= e1 && i <= e2) {
const n1 = c1[i]
- const n2 = (c2[i] = normalizeChild(c2[i]))
+ const n2 = (c2[i] = normalizeVNode(c2[i]))
if (isSameType(n1, n2)) {
patch(n1, n2, container, parentAnchor, optimized)
} else {
// d e (b c)
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
- const n2 = (c2[e2] = normalizeChild(c2[e2]))
+ const n2 = (c2[e2] = normalizeVNode(c2[e2]))
if (isSameType(n1, n2)) {
patch(n1, n2, container, parentAnchor, optimized)
} else {
const nextPos = e2 + 1
const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
while (i <= e2) {
- patch(null, (c2[i] = normalizeChild(c2[i])), container, anchor)
+ patch(null, (c2[i] = normalizeVNode(c2[i])), container, anchor)
i++
}
}
// 5.1 build key:index map for newChildren
const keyToNewIndexMap: Map<any, number> = new Map()
for (i = s2; i <= e2; i++) {
- const nextChild = (c2[i] = normalizeChild(c2[i]))
+ const nextChild = (c2[i] = normalizeVNode(c2[i]))
if (nextChild.key != null) {
// TODO warn duplicate keys
keyToNewIndexMap.set(nextChild.key, i)
}
function move(vnode: VNode, container: HostNode, anchor: HostNode) {
+ if (vnode.component != null) {
+ move(vnode.component.subTree, container, anchor)
+ return
+ }
if (vnode.type === Fragment) {
hostInsert(vnode.el, container, anchor)
const children = vnode.children as VNode[]
}
function unmount(vnode: VNode, doRemove?: boolean) {
+ if (vnode.component != null) {
+ // TODO teardown component
+ unmount(vnode.component.subTree, doRemove)
+ return
+ }
const shouldRemoveChildren = vnode.type === Fragment && doRemove
if (vnode.dynamicChildren != null) {
unmountChildren(vnode.dynamicChildren, shouldRemoveChildren)
}
}
+ function getNextHostNode(vnode: VNode): HostNode {
+ return vnode.component === null
+ ? hostNextSibling(vnode.anchor || vnode.el)
+ : getNextHostNode(vnode.component.subTree)
+ }
+
return function render(vnode: VNode, dom: HostNode): VNode {
patch(dom._vnode, vnode, dom)
return (dom._vnode = vnode)