FunctionalComponent,
ComponentClass
} from './component'
-import { updateProps, resolveProps } from './componentProps'
+import { updateProps } from './componentProps'
import {
renderInstanceRoot,
+ renderFunctionalRoot,
createComponentInstance,
teardownComponentInstance,
- normalizeComponentRoot,
shouldUpdateFunctionalComponent
} from './componentUtils'
import { KeepAliveSymbol } from './optional/keepAlive'
+import { pushContext, popContext } from './warning'
interface NodeOps {
createElement: (tag: string, isSVG?: boolean) => any
const { flags } = vnode
if (flags & VNodeFlags.ELEMENT) {
mountElement(vnode, container, contextVNode, isSVG, endNode)
- } else if (flags & VNodeFlags.COMPONENT_STATEFUL) {
- mountStatefulComponent(vnode, container, contextVNode, isSVG, endNode)
- } else if (flags & VNodeFlags.COMPONENT_FUNCTIONAL) {
- mountFunctionalComponent(vnode, container, contextVNode, isSVG, endNode)
+ } else if (flags & VNodeFlags.COMPONENT) {
+ mountComponent(vnode, container, contextVNode, isSVG, endNode)
} else if (flags & VNodeFlags.TEXT) {
mountText(vnode, container, endNode)
} else if (flags & VNodeFlags.FRAGMENT) {
})
}
- function mountStatefulComponent(
+ function mountComponent(
vnode: VNode,
container: RenderNode | null,
contextVNode: MountedVNode | null,
endNode: RenderNode | null
) {
vnode.contextVNode = contextVNode
+ if (__DEV__) {
+ pushContext(vnode)
+ }
+ const { flags } = vnode
+ if (flags & VNodeFlags.COMPONENT_STATEFUL) {
+ mountStatefulComponent(vnode, container, contextVNode, isSVG, endNode)
+ } else {
+ mountFunctionalComponent(vnode, container, contextVNode, isSVG, endNode)
+ }
+ if (__DEV__) {
+ popContext()
+ }
+ }
+
+ function mountStatefulComponent(
+ vnode: VNode,
+ container: RenderNode | null,
+ contextVNode: MountedVNode | null,
+ isSVG: boolean,
+ endNode: RenderNode | null
+ ) {
if (vnode.flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) {
// kept-alive
activateComponentInstance(vnode, container, endNode)
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)
- const subTree = (vnode.children = normalizeComponentRoot(
- render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ),
- vnode
- ))
+ const subTree = (vnode.children = renderFunctionalRoot(vnode))
mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
vnode.el = subTree.el as RenderNode
}
contextVNode: MountedVNode | null,
isSVG: boolean
) {
+ if (__DEV__) {
+ pushContext(nextVNode)
+ }
nextVNode.contextVNode = contextVNode
const { tag, flags } = nextVNode
if (tag !== prevVNode.tag) {
isSVG
)
}
+ if (__DEV__) {
+ popContext()
+ }
}
function patchStatefulComponent(prevVNode: MountedVNode, nextVNode: VNode) {
}
if (shouldUpdate) {
- const { props, attrs } = resolveProps(nextData, render.props)
- const nextTree = (nextVNode.children = normalizeComponentRoot(
- render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ),
- nextVNode
- ))
+ const nextTree = (nextVNode.children = renderFunctionalRoot(nextVNode))
patch(prevTree, nextTree, container, nextVNode as MountedVNode, isSVG)
nextVNode.el = nextTree.el
} else if (prevTree.flags & VNodeFlags.COMPONENT) {
isSVG: boolean
) {
const refNode = platformNextSibling(getVNodeLastEl(prevVNode))
- reinsertVNode(prevVNode, container)
+ removeVNode(prevVNode, container)
mount(nextVNode, container, contextVNode, isSVG, refNode)
}
)
break
case ChildrenFlags.NO_CHILDREN:
- reinsertVNode(prevChildren as MountedVNode, container)
+ removeVNode(prevChildren as MountedVNode, container)
break
default:
- reinsertVNode(prevChildren as MountedVNode, container)
+ removeVNode(prevChildren as MountedVNode, container)
mountArrayChildren(
nextChildren as VNode[],
container,
}
} else if (prevLength > nextLength) {
for (i = commonLength; i < prevLength; i++) {
- reinsertVNode(prevChildren[i], container)
+ removeVNode(prevChildren[i], container)
}
}
}
}
} else if (j > nextEnd) {
while (j <= prevEnd) {
- reinsertVNode(prevChildren[j++], container)
+ removeVNode(prevChildren[j++], container)
}
} else {
let prevStart = j
if (canRemoveWholeContent) {
canRemoveWholeContent = false
while (i > prevStart) {
- reinsertVNode(prevChildren[prevStart++], container)
+ removeVNode(prevChildren[prevStart++], container)
}
}
if (pos > j) {
}
}
if (!canRemoveWholeContent && j > nextEnd) {
- reinsertVNode(prevVNode, container)
+ removeVNode(prevVNode, container)
}
} else if (!canRemoveWholeContent) {
- reinsertVNode(prevVNode, container)
+ removeVNode(prevVNode, container)
}
}
} else {
if (canRemoveWholeContent) {
canRemoveWholeContent = false
while (i > prevStart) {
- reinsertVNode(prevChildren[prevStart++], container)
+ removeVNode(prevChildren[prevStart++], container)
}
}
nextVNode = nextChildren[j]
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
patched++
} else if (!canRemoveWholeContent) {
- reinsertVNode(prevVNode, container)
+ removeVNode(prevVNode, container)
}
} else if (!canRemoveWholeContent) {
- reinsertVNode(prevVNode, container)
+ removeVNode(prevVNode, container)
}
}
}
null
)
} else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
- reinsertVNode(children as MountedVNode, vnode.tag as RenderNode)
+ removeVNode(children as MountedVNode, vnode.tag as RenderNode)
}
}
if (ref) {
}
}
- function reinsertVNode(vnode: MountedVNode, container: RenderNode) {
+ function removeVNode(vnode: MountedVNode, container: RenderNode) {
unmount(vnode)
const { el, flags, children, childFlags } = vnode
if (container && el) {
if (flags & VNodeFlags.FRAGMENT) {
switch (childFlags) {
case ChildrenFlags.SINGLE_VNODE:
- reinsertVNode(children as MountedVNode, container)
+ removeVNode(children as MountedVNode, container)
break
case ChildrenFlags.NO_CHILDREN:
platformRemoveChild(container, el)
break
default:
for (let i = 0; i < (children as MountedVNode[]).length; i++) {
- reinsertVNode((children as MountedVNode[])[i], container)
+ removeVNode((children as MountedVNode[])[i], container)
}
}
} else {
platformClearContent(container)
} else {
for (let i = 0; i < children.length; i++) {
- reinsertVNode(children[i], container)
+ removeVNode(children[i], container)
}
}
}
instance: ComponentInstance,
isSVG: boolean
) {
+ if (__DEV__ && instance.$parentVNode) {
+ pushContext(instance.$parentVNode as VNode)
+ }
const prevVNode = instance.$vnode
if (instance.beforeUpdate) {
}
})
}
+
+ if (__DEV__ && instance.$parentVNode) {
+ popContext()
+ }
}
function unmountComponentInstance(instance: ComponentInstance) {
patch(prevVNode, vnode, container, null, false)
container.vnode = vnode
} else {
- reinsertVNode(prevVNode, container)
+ removeVNode(prevVNode, container)
container.vnode = null
}
}
import { ComponentInstance } from './component'
+import { warn } from './warning'
+import { VNode } from './vdom'
+import { VNodeFlags } from './flags'
export const enum ErrorTypes {
BEFORE_CREATE = 1,
DESTROYED,
ERROR_CAPTURED,
RENDER,
- RENDER_ERROR,
WATCH_CALLBACK,
NATIVE_EVENT_HANDLER,
COMPONENT_EVENT_HANDLER
[ErrorTypes.DESTROYED]: 'destroyed lifecycle hook',
[ErrorTypes.ERROR_CAPTURED]: 'errorCaptured lifecycle hook',
[ErrorTypes.RENDER]: 'render function',
- [ErrorTypes.RENDER_ERROR]: 'renderError function',
[ErrorTypes.WATCH_CALLBACK]: 'watcher callback',
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'native event handler',
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'component event handler'
export function handleError(
err: Error,
- instance: ComponentInstance,
+ instance: ComponentInstance | VNode,
type: ErrorTypes
) {
- let cur = instance
- while (cur.$parent) {
- cur = cur.$parent
+ const isFunctional = (instance as VNode)._isVNode
+ let cur: ComponentInstance | null = null
+ if (isFunctional) {
+ let vnode = instance as VNode | null
+ while (vnode && !(vnode.flags & VNodeFlags.COMPONENT_STATEFUL)) {
+ vnode = vnode.contextVNode
+ }
+ if (vnode) {
+ cur = vnode.children as ComponentInstance
+ }
+ } else {
+ cur = (instance as ComponentInstance).$parent
+ }
+ while (cur) {
const handler = cur.errorCaptured
if (handler) {
try {
- const captured = handler.call(cur, err, type, instance)
+ const captured = handler.call(
+ cur,
+ err,
+ type,
+ isFunctional ? null : instance,
+ isFunctional ? instance : (instance as ComponentInstance).$parentVNode
+ )
if (captured) return
} catch (err2) {
logError(err2, ErrorTypes.ERROR_CAPTURED)
}
}
+ cur = cur.$parent
}
logError(err, type)
}
function logError(err: Error, type: ErrorTypes) {
if (__DEV__) {
const info = ErrorTypeStrings[type]
- console.warn(`Unhandled error${info ? ` in ${info}` : ``}:`)
+ warn(`Unhandled error${info ? ` in ${info}` : ``}`)
console.error(err)
} else {
throw err
import { ComponentType, ComponentClass, FunctionalComponent } from './component'
import { EMPTY_OBJ } from './utils'
+import { VNode } from './vdom'
-// TODO push vnodes instead
-// component vnodes get a new property (contextVNode) which points to the
-// parent component (stateful or functional)
-// this way we can use any component vnode to construct a trace that inludes
-// functional and stateful components.
+let stack: VNode[] = []
-// in createRenderer, parentComponent should be replced by ctx
-// $parent logic should also accomodate
-
-let stack: ComponentType[] = []
-
-export function pushComponent(c: ComponentType) {
- stack.push(c)
+export function pushContext(vnode: VNode) {
+ stack.push(vnode)
}
-export function popComponent() {
+export function popContext() {
stack.pop()
}
}
function getComponentTrace(): string {
- const current = stack[stack.length - 1]
+ let current: VNode | null | undefined = stack[stack.length - 1]
if (!current) {
- return ''
+ return '\nat <Root/>'
}
- // we can't just use the stack itself, because it will be incomplete
- // during updates
- // check recursive
+
+ // we can't just use the stack because it will be incomplete during updates
+ // that did not start from the root. Re-construct the parent chain using
+ // contextVNode information.
const normlaizedStack: Array<{
- type: ComponentType
+ type: VNode
recurseCount: number
}> = []
- stack.forEach(c => {
- const last = normlaizedStack[normlaizedStack.length - 1]
- if (last && last.type === c) {
+
+ while (current) {
+ const last = normlaizedStack[0]
+ if (last && last.type === current) {
last.recurseCount++
} else {
- normlaizedStack.push({ type: c, recurseCount: 0 })
+ normlaizedStack.unshift({
+ type: current,
+ recurseCount: 0
+ })
}
- })
+ current = current.contextVNode
+ }
+
return (
- `\n\nfound in\n\n` +
+ `\nat ` +
normlaizedStack
.map(({ type, recurseCount }, i) => {
- const padding = i === 0 ? '---> ' : ' '.repeat(5 + i * 2)
+ const padding = i === 0 ? '' : ' '.repeat(i + 1)
const postfix =
recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``
- return padding + formatComponentName(type) + postfix
+ return (
+ padding + formatComponentName(type.tag as ComponentType) + postfix
+ )
})
.join('\n')
)
let name: string
let file: string | null = null
- if (c.prototype.render) {
+ if (c.prototype && c.prototype.render) {
+ // stateful
const cc = c as ComponentClass
const options = cc.options || EMPTY_OBJ
name = options.displayName || cc.name
file = options.__file
} else {
+ // functional
const fc = c as FunctionalComponent
name = fc.displayName || fc.name
}