vue3-yuanma/packages/core/src/createRenderer.ts

1470 lines
41 KiB
TypeScript
Raw Normal View History

2018-09-19 23:35:38 +08:00
import { autorun, stop } from '@vue/observer'
import { queueJob } from '@vue/scheduler'
2018-09-19 23:35:38 +08:00
import { VNodeFlags, ChildrenFlags } from './flags'
import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
2018-09-19 23:35:38 +08:00
import {
VNode,
MountedVNode,
RenderNode,
createTextVNode,
cloneVNode,
Ref,
VNodeChildren
2018-09-19 23:35:38 +08:00
} from './vdom'
import {
2018-10-09 23:37:24 +08:00
ComponentInstance,
2018-09-19 23:35:38 +08:00
FunctionalComponent,
ComponentClass
} from './component'
import { updateProps } from './componentProps'
2018-09-19 23:35:38 +08:00
import {
renderInstanceRoot,
renderFunctionalRoot,
2018-09-19 23:35:38 +08:00
createComponentInstance,
teardownComponentInstance,
shouldUpdateFunctionalComponent
} from './componentUtils'
2018-09-27 05:10:34 +08:00
import { KeepAliveSymbol } from './optional/keepAlive'
import { pushWarningContext, popWarningContext } from './warning'
2018-09-19 23:35:38 +08:00
2018-09-20 12:17:20 +08:00
interface NodeOps {
createElement: (tag: string, isSVG?: boolean) => any
createText: (text: string) => any
setText: (node: any, text: string) => void
appendChild: (parent: any, child: any) => void
insertBefore: (parent: any, child: any, ref: any) => void
removeChild: (parent: any, child: any) => void
clearContent: (node: any) => void
parentNode: (node: any) => any
nextSibling: (node: any) => any
querySelector: (selector: string) => any
}
interface PatchDataFunction {
(
2018-09-19 23:35:38 +08:00
el: any,
key: string,
prevValue: any,
nextValue: any,
preVNode: VNode | null,
nextVNode: VNode,
isSVG: boolean,
// passed for DOM operations that removes child content
// e.g. innerHTML & textContent
unmountChildren: (children: VNode[], childFlags: ChildrenFlags) => void
2018-09-20 12:17:20 +08:00
): void
}
interface RendererOptions {
nodeOps: NodeOps
patchData: PatchDataFunction
2018-09-19 23:35:38 +08:00
teardownVNode?: (vnode: VNode) => void
}
// The whole mounting / patching / unmouting logic is placed inside this
// single function so that we can create multiple renderes with different
// platform definitions. This allows for use cases like creating a test
// renderer alongside an actual renderer.
export function createRenderer(options: RendererOptions) {
const {
nodeOps: {
createElement: platformCreateElement,
createText: platformCreateText,
setText: platformSetText,
appendChild: platformAppendChild,
insertBefore: platformInsertBefore,
removeChild: platformRemoveChild,
clearContent: platformClearContent,
parentNode: platformParentNode,
nextSibling: platformNextSibling,
querySelector: platformQuerySelector
},
patchData: platformPatchData,
teardownVNode
} = options
function insertOrAppend(
container: RenderNode,
newNode: RenderNode,
refNode: RenderNode | null
2018-09-19 23:35:38 +08:00
) {
if (refNode === null) {
platformAppendChild(container, newNode)
2018-09-19 23:35:38 +08:00
} else {
platformInsertBefore(container, newNode, refNode)
2018-09-19 23:35:38 +08:00
}
}
// Lifecycle Hooks -----------------------------------------------------------
2018-09-19 23:35:38 +08:00
2018-09-26 01:39:19 +08:00
const lifecycleHooks: Function[] = []
const vnodeUpdatedHooks: Function[] = []
2018-09-19 23:35:38 +08:00
function flushHooks() {
let fn
2018-09-26 01:39:19 +08:00
while ((fn = lifecycleHooks.shift())) {
2018-09-19 23:35:38 +08:00
fn()
}
}
// mounting ------------------------------------------------------------------
function mount(
vnode: VNode,
container: RenderNode | null,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
) {
2018-09-19 23:35:38 +08:00
const { flags } = vnode
if (flags & VNodeFlags.ELEMENT) {
2018-10-11 01:13:27 +08:00
mountElement(vnode, container, contextVNode, isSVG, endNode)
} else if (flags & VNodeFlags.COMPONENT) {
mountComponent(vnode, container, contextVNode, isSVG, endNode)
2018-09-19 23:35:38 +08:00
} else if (flags & VNodeFlags.TEXT) {
mountText(vnode, container, endNode)
2018-09-19 23:35:38 +08:00
} else if (flags & VNodeFlags.FRAGMENT) {
2018-10-11 01:13:27 +08:00
mountFragment(vnode, container, contextVNode, isSVG, endNode)
2018-09-19 23:35:38 +08:00
} else if (flags & VNodeFlags.PORTAL) {
2018-10-11 01:13:27 +08:00
mountPortal(vnode, container, contextVNode)
2018-09-19 23:35:38 +08:00
}
}
function mountArrayChildren(
children: VNode[],
container: RenderNode | null,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
2018-09-19 23:35:38 +08:00
) {
for (let i = 0; i < children.length; i++) {
let child = children[i]
if (child.el) {
children[i] = child = cloneVNode(child)
}
2018-10-11 01:13:27 +08:00
mount(children[i], container, contextVNode, isSVG, endNode)
2018-09-19 23:35:38 +08:00
}
}
function mountElement(
vnode: VNode,
container: RenderNode | null,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
) {
2018-09-19 23:35:38 +08:00
const { flags, tag, data, children, childFlags, ref } = vnode
isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
const el = (vnode.el = platformCreateElement(tag as string, isSVG))
if (data != null) {
for (const key in data) {
patchData(el, key, null, data[key], null, vnode, isSVG)
}
2018-09-26 01:39:19 +08:00
if (data.vnodeBeforeMount) {
data.vnodeBeforeMount(vnode)
}
2018-09-19 23:35:38 +08:00
}
if (childFlags !== ChildrenFlags.NO_CHILDREN) {
const hasSVGChildren = isSVG && tag !== 'foreignObject'
if (childFlags & ChildrenFlags.SINGLE_VNODE) {
2018-10-11 01:13:27 +08:00
mount(children as VNode, el, contextVNode, hasSVGChildren, endNode)
2018-09-19 23:35:38 +08:00
} else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
mountArrayChildren(
children as VNode[],
el,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
hasSVGChildren,
endNode
)
}
}
if (container != null) {
insertOrAppend(container, el, endNode)
}
if (ref) {
mountRef(ref, el)
}
2018-09-26 01:39:19 +08:00
if (data != null && data.vnodeMounted) {
lifecycleHooks.push(() => {
data.vnodeMounted(vnode)
})
}
2018-09-19 23:35:38 +08:00
}
2018-10-09 23:37:24 +08:00
function mountRef(ref: Ref, el: RenderNode | ComponentInstance) {
2018-09-26 01:39:19 +08:00
lifecycleHooks.push(() => {
2018-09-19 23:35:38 +08:00
ref(el)
})
}
function mountComponent(
2018-09-19 23:35:38 +08:00
vnode: VNode,
container: RenderNode | null,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
) {
2018-10-11 01:13:27 +08:00
vnode.contextVNode = contextVNode
if (__DEV__) {
pushWarningContext(vnode)
}
const { flags } = vnode
if (flags & VNodeFlags.COMPONENT_STATEFUL) {
2018-10-16 00:41:18 +08:00
mountStatefulComponent(vnode, container, isSVG, endNode)
} else {
2018-10-16 00:41:18 +08:00
mountFunctionalComponent(vnode, container, isSVG, endNode)
}
if (__DEV__) {
popWarningContext()
}
}
function mountStatefulComponent(
vnode: VNode,
container: RenderNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
2018-10-03 03:53:22 +08:00
if (vnode.flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) {
// kept-alive
activateComponentInstance(vnode, container, endNode)
2018-09-19 23:35:38 +08:00
} else {
2018-10-03 03:53:22 +08:00
mountComponentInstance(
2018-09-24 11:28:21 +08:00
vnode,
2018-10-03 03:53:22 +08:00
vnode.tag as ComponentClass,
container,
isSVG,
endNode
)
2018-09-19 23:35:38 +08:00
}
}
2018-10-03 03:53:22 +08:00
function mountFunctionalComponent(
vnode: VNode,
container: RenderNode | null,
isSVG: boolean,
endNode: RenderNode | null
) {
const subTree = (vnode.children = renderFunctionalRoot(vnode))
2018-10-11 01:13:27 +08:00
mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
2018-10-03 03:53:22 +08:00
vnode.el = subTree.el as RenderNode
}
2018-09-19 23:35:38 +08:00
function mountText(
vnode: VNode,
container: RenderNode | null,
endNode: RenderNode | null
) {
2018-09-19 23:35:38 +08:00
const el = (vnode.el = platformCreateText(vnode.children as string))
if (container != null) {
insertOrAppend(container, el, endNode)
}
}
function mountFragment(
vnode: VNode,
container: RenderNode | null,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
) {
2018-09-19 23:35:38 +08:00
const { children, childFlags } = vnode
switch (childFlags) {
case ChildrenFlags.SINGLE_VNODE:
2018-10-11 01:13:27 +08:00
mount(children as VNode, container, contextVNode, isSVG, endNode)
vnode.el = (children as MountedVNode).el
break
case ChildrenFlags.NO_CHILDREN:
const placeholder = createTextVNode('')
mountText(placeholder, container, null)
vnode.el = placeholder.el
break
default:
mountArrayChildren(
children as VNode[],
container,
2018-10-11 01:13:27 +08:00
contextVNode,
isSVG,
endNode
)
vnode.el = (children as MountedVNode[])[0].el
2018-09-19 23:35:38 +08:00
}
}
function mountPortal(
vnode: VNode,
container: RenderNode | null,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null
) {
2018-09-19 23:35:38 +08:00
const { tag, children, childFlags, ref } = vnode
const target = isString(tag) ? platformQuerySelector(tag) : tag
2018-09-19 23:35:38 +08:00
if (__DEV__ && !target) {
// TODO warn poartal target not found
}
if (childFlags & ChildrenFlags.SINGLE_VNODE) {
2018-10-11 01:13:27 +08:00
mount(children as VNode, target as RenderNode, contextVNode, false, null)
2018-09-19 23:35:38 +08:00
} else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
mountArrayChildren(
children as VNode[],
target as RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
false,
null
)
}
if (ref) {
mountRef(ref, target as RenderNode)
}
const placeholder = createTextVNode('')
mountText(placeholder, container, null)
vnode.el = placeholder.el
2018-09-19 23:35:38 +08:00
}
// patching ------------------------------------------------------------------
function patchData(
el: RenderNode,
key: string,
prevValue: any,
nextValue: any,
preVNode: VNode | null,
nextVNode: VNode,
isSVG: boolean
) {
2018-09-26 00:09:25 +08:00
if (reservedPropRE.test(key)) {
2018-09-19 23:35:38 +08:00
return
}
platformPatchData(
el,
key,
prevValue,
nextValue,
preVNode,
nextVNode,
isSVG,
unmountChildren
)
}
function patch(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean
) {
const nextFlags = nextVNode.flags
const prevFlags = prevVNode.flags
if (prevFlags !== nextFlags) {
2018-10-11 01:13:27 +08:00
replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
} else if (nextFlags & VNodeFlags.ELEMENT) {
2018-10-11 01:13:27 +08:00
patchElement(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
} else if (nextFlags & VNodeFlags.COMPONENT) {
2018-10-11 01:13:27 +08:00
patchComponent(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
} else if (nextFlags & VNodeFlags.TEXT) {
patchText(prevVNode, nextVNode)
} else if (nextFlags & VNodeFlags.FRAGMENT) {
2018-10-11 01:13:27 +08:00
patchFragment(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
} else if (nextFlags & VNodeFlags.PORTAL) {
2018-10-11 01:13:27 +08:00
patchPortal(prevVNode, nextVNode, contextVNode)
2018-09-19 23:35:38 +08:00
}
}
function patchElement(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean
) {
const { flags, tag } = nextVNode
isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
if (prevVNode.tag !== tag) {
2018-10-11 01:13:27 +08:00
replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
return
}
const el = (nextVNode.el = prevVNode.el)
2018-09-19 23:35:38 +08:00
const prevData = prevVNode.data
const nextData = nextVNode.data
2018-09-26 01:39:19 +08:00
if (nextData != null && nextData.vnodeBeforeUpdate) {
nextData.vnodeBeforeUpdate(nextVNode, prevVNode)
}
// patch data
2018-09-19 23:35:38 +08:00
if (prevData !== nextData) {
const prevDataOrEmpty = prevData || EMPTY_OBJ
const nextDataOrEmpty = nextData || EMPTY_OBJ
if (nextDataOrEmpty !== EMPTY_OBJ) {
for (const key in nextDataOrEmpty) {
const prevValue = prevDataOrEmpty[key]
const nextValue = nextDataOrEmpty[key]
if (prevValue !== nextValue) {
patchData(
el,
key,
prevValue,
nextValue,
prevVNode,
nextVNode,
isSVG
)
}
}
}
if (prevDataOrEmpty !== EMPTY_OBJ) {
for (const key in prevDataOrEmpty) {
const prevValue = prevDataOrEmpty[key]
if (prevValue != null && !nextDataOrEmpty.hasOwnProperty(key)) {
patchData(el, key, prevValue, null, prevVNode, nextVNode, isSVG)
}
}
}
}
// children
patchChildren(
prevVNode.childFlags,
nextVNode.childFlags,
prevVNode.children,
nextVNode.children,
el,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG && nextVNode.tag !== 'foreignObject',
null
)
2018-09-26 01:39:19 +08:00
if (nextData != null && nextData.vnodeUpdated) {
vnodeUpdatedHooks.push(() => {
nextData.vnodeUpdated(nextVNode, prevVNode)
})
}
2018-09-19 23:35:38 +08:00
}
function patchComponent(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean
) {
if (__DEV__) {
pushWarningContext(nextVNode)
}
2018-10-11 01:13:27 +08:00
nextVNode.contextVNode = contextVNode
2018-09-19 23:35:38 +08:00
const { tag, flags } = nextVNode
if (tag !== prevVNode.tag) {
2018-10-11 01:13:27 +08:00
replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
} else if (flags & VNodeFlags.COMPONENT_STATEFUL) {
patchStatefulComponent(prevVNode, nextVNode)
} else {
patchFunctionalComponent(
prevVNode,
nextVNode,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG
)
}
if (__DEV__) {
popWarningContext()
}
2018-09-19 23:35:38 +08:00
}
function patchStatefulComponent(prevVNode: MountedVNode, nextVNode: VNode) {
2018-09-19 23:35:38 +08:00
const { childFlags: prevChildFlags } = prevVNode
const {
2018-09-25 06:51:58 +08:00
data: nextData,
2018-09-19 23:35:38 +08:00
slots: nextSlots,
childFlags: nextChildFlags
} = nextVNode
const instance = (nextVNode.children =
2018-10-09 23:37:24 +08:00
prevVNode.children) as ComponentInstance
2018-09-19 23:35:38 +08:00
instance.$slots = nextSlots || EMPTY_OBJ
instance.$parentVNode = nextVNode as MountedVNode
2018-09-19 23:35:38 +08:00
// Update props. This will trigger child update if necessary.
2018-09-25 06:51:58 +08:00
if (nextData !== null) {
updateProps(instance, nextData)
2018-09-19 23:35:38 +08:00
}
// If has different slots content, or has non-compiled slots,
// the child needs to be force updated. It's ok to call $forceUpdate
// again even if props update has already queued an update, as the
// scheduler will not queue the same update twice.
const shouldForceUpdate =
prevChildFlags !== nextChildFlags ||
(nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
if (shouldForceUpdate) {
instance.$forceUpdate()
} else if (instance.$vnode.flags & VNodeFlags.COMPONENT) {
2018-10-11 01:13:27 +08:00
instance.$vnode.contextVNode = nextVNode
2018-09-19 23:35:38 +08:00
}
nextVNode.el = instance.$vnode.el
}
function patchFunctionalComponent(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean
) {
// functional component tree is stored on the vnode as `children`
2018-09-25 06:51:58 +08:00
const { data: prevData, slots: prevSlots } = prevVNode
const { data: nextData, slots: nextSlots } = nextVNode
2018-09-19 23:35:38 +08:00
const render = nextVNode.tag as FunctionalComponent
const prevTree = prevVNode.children as MountedVNode
2018-09-19 23:35:38 +08:00
let shouldUpdate = true
if (render.pure && prevSlots == null && nextSlots == null) {
2018-09-25 06:51:58 +08:00
shouldUpdate = shouldUpdateFunctionalComponent(prevData, nextData)
2018-09-19 23:35:38 +08:00
}
if (shouldUpdate) {
const nextTree = (nextVNode.children = renderFunctionalRoot(nextVNode))
2018-10-11 01:13:27 +08:00
patch(prevTree, nextTree, container, nextVNode as MountedVNode, isSVG)
2018-09-19 23:35:38 +08:00
nextVNode.el = nextTree.el
} else if (prevTree.flags & VNodeFlags.COMPONENT) {
// functional component returned another component
2018-10-11 01:13:27 +08:00
prevTree.contextVNode = nextVNode
2018-09-19 23:35:38 +08:00
}
}
function patchFragment(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean
) {
// determine the tail node of the previous fragment,
// then retrieve its next sibling to use as the end node for patchChildren.
const endNode = platformNextSibling(getVNodeLastEl(prevVNode))
const { childFlags, children } = nextVNode
2018-09-19 23:35:38 +08:00
patchChildren(
prevVNode.childFlags,
childFlags,
prevVNode.children,
children,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
2018-09-19 23:35:38 +08:00
)
switch (childFlags) {
case ChildrenFlags.SINGLE_VNODE:
nextVNode.el = (children as MountedVNode).el
break
case ChildrenFlags.NO_CHILDREN:
nextVNode.el = prevVNode.el
break
default:
nextVNode.el = (children as MountedVNode[])[0].el
}
}
function getVNodeLastEl(vnode: MountedVNode): RenderNode {
const { el, flags, children, childFlags } = vnode
if (flags & VNodeFlags.FRAGMENT) {
if (childFlags & ChildrenFlags.SINGLE_VNODE) {
return getVNodeLastEl(children as MountedVNode)
} else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
return getVNodeLastEl(
(children as MountedVNode[])[(children as MountedVNode[]).length - 1]
)
} else {
return el
2018-09-19 23:35:38 +08:00
}
} else {
return el
2018-09-19 23:35:38 +08:00
}
}
function patchText(prevVNode: MountedVNode, nextVNode: VNode) {
2018-09-19 23:35:38 +08:00
const el = (nextVNode.el = prevVNode.el) as RenderNode
const nextText = nextVNode.children
if (nextText !== prevVNode.children) {
platformSetText(el, nextText as string)
}
}
function patchPortal(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null
2018-09-19 23:35:38 +08:00
) {
const prevContainer = prevVNode.tag as RenderNode
const nextContainer = nextVNode.tag as RenderNode
const nextChildren = nextVNode.children
patchChildren(
prevVNode.childFlags,
nextVNode.childFlags,
prevVNode.children,
nextChildren,
prevContainer,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
false,
null
)
nextVNode.el = prevVNode.el
if (nextContainer !== prevContainer) {
switch (nextVNode.childFlags) {
case ChildrenFlags.SINGLE_VNODE:
insertVNode(nextChildren as MountedVNode, nextContainer, null)
2018-09-19 23:35:38 +08:00
break
case ChildrenFlags.NO_CHILDREN:
break
default:
for (let i = 0; i < (nextChildren as MountedVNode[]).length; i++) {
insertVNode(
(nextChildren as MountedVNode[])[i],
nextContainer,
null
)
2018-09-19 23:35:38 +08:00
}
break
}
}
}
function replaceVNode(
prevVNode: MountedVNode,
2018-09-19 23:35:38 +08:00
nextVNode: VNode,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean
) {
const refNode = platformNextSibling(getVNodeLastEl(prevVNode))
removeVNode(prevVNode, container)
2018-10-11 01:13:27 +08:00
mount(nextVNode, container, contextVNode, isSVG, refNode)
2018-09-19 23:35:38 +08:00
}
function patchChildren(
prevChildFlags: ChildrenFlags,
nextChildFlags: ChildrenFlags,
prevChildren: VNodeChildren,
nextChildren: VNodeChildren,
container: RenderNode,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
2018-09-19 23:35:38 +08:00
) {
switch (prevChildFlags) {
case ChildrenFlags.SINGLE_VNODE:
switch (nextChildFlags) {
case ChildrenFlags.SINGLE_VNODE:
patch(
prevChildren as MountedVNode,
2018-09-19 23:35:38 +08:00
nextChildren as VNode,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG
)
break
case ChildrenFlags.NO_CHILDREN:
removeVNode(prevChildren as MountedVNode, container)
2018-09-19 23:35:38 +08:00
break
default:
removeVNode(prevChildren as MountedVNode, container)
2018-09-19 23:35:38 +08:00
mountArrayChildren(
nextChildren as VNode[],
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
break
}
break
case ChildrenFlags.NO_CHILDREN:
switch (nextChildFlags) {
case ChildrenFlags.SINGLE_VNODE:
mount(
nextChildren as VNode,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
break
case ChildrenFlags.NO_CHILDREN:
break
default:
mountArrayChildren(
nextChildren as VNode[],
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
break
}
break
default:
// MULTIPLE_CHILDREN
if (nextChildFlags === ChildrenFlags.SINGLE_VNODE) {
removeChildren(prevChildren as MountedVNode[], container, endNode)
2018-10-11 01:13:27 +08:00
mount(nextChildren as VNode, container, contextVNode, isSVG, endNode)
2018-09-19 23:35:38 +08:00
} else if (nextChildFlags === ChildrenFlags.NO_CHILDREN) {
removeChildren(prevChildren as MountedVNode[], container, endNode)
2018-09-19 23:35:38 +08:00
} else {
const prevLength = (prevChildren as VNode[]).length
const nextLength = (nextChildren as VNode[]).length
if (prevLength === 0) {
if (nextLength > 0) {
mountArrayChildren(
nextChildren as VNode[],
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
}
} else if (nextLength === 0) {
removeChildren(prevChildren as MountedVNode[], container, endNode)
2018-09-19 23:35:38 +08:00
} else if (
prevChildFlags === ChildrenFlags.KEYED_VNODES &&
nextChildFlags === ChildrenFlags.KEYED_VNODES
) {
patchKeyedChildren(
prevChildren as MountedVNode[],
2018-09-19 23:35:38 +08:00
nextChildren as VNode[],
container,
prevLength,
nextLength,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
} else {
patchNonKeyedChildren(
prevChildren as MountedVNode[],
2018-09-19 23:35:38 +08:00
nextChildren as VNode[],
container,
prevLength,
nextLength,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
}
}
break
}
}
function patchNonKeyedChildren(
prevChildren: MountedVNode[],
2018-09-19 23:35:38 +08:00
nextChildren: VNode[],
container: RenderNode,
prevLength: number,
nextLength: number,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
2018-09-19 23:35:38 +08:00
) {
const commonLength = prevLength > nextLength ? nextLength : prevLength
let i = 0
let nextChild
let prevChild
for (i; i < commonLength; i++) {
nextChild = nextChildren[i]
prevChild = prevChildren[i]
if (nextChild.el) {
nextChildren[i] = nextChild = cloneVNode(nextChild)
}
2018-10-11 01:13:27 +08:00
patch(prevChild, nextChild, container, contextVNode, isSVG)
prevChildren[i] = nextChild as MountedVNode
2018-09-19 23:35:38 +08:00
}
if (prevLength < nextLength) {
for (i = commonLength; i < nextLength; i++) {
nextChild = nextChildren[i]
if (nextChild.el) {
nextChildren[i] = nextChild = cloneVNode(nextChild)
}
2018-10-11 01:13:27 +08:00
mount(nextChild, container, contextVNode, isSVG, endNode)
2018-09-19 23:35:38 +08:00
}
} else if (prevLength > nextLength) {
for (i = commonLength; i < prevLength; i++) {
removeVNode(prevChildren[i], container)
2018-09-19 23:35:38 +08:00
}
}
}
function patchKeyedChildren(
prevChildren: MountedVNode[],
2018-09-19 23:35:38 +08:00
nextChildren: VNode[],
container: RenderNode,
prevLength: number,
nextLength: number,
2018-10-11 01:13:27 +08:00
contextVNode: MountedVNode | null,
2018-09-19 23:35:38 +08:00
isSVG: boolean,
endNode: RenderNode | null
2018-09-19 23:35:38 +08:00
) {
let prevEnd = prevLength - 1
let nextEnd = nextLength - 1
let i
let j = 0
let prevVNode = prevChildren[j]
let nextVNode = nextChildren[j]
let nextPos
outer: {
// Sync nodes with the same key at the beginning.
while (prevVNode.key === nextVNode.key) {
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
2018-10-11 01:13:27 +08:00
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
prevChildren[j] = nextVNode as MountedVNode
2018-09-19 23:35:38 +08:00
j++
if (j > prevEnd || j > nextEnd) {
break outer
}
prevVNode = prevChildren[j]
nextVNode = nextChildren[j]
}
prevVNode = prevChildren[prevEnd]
nextVNode = nextChildren[nextEnd]
// Sync nodes with the same key at the end.
while (prevVNode.key === nextVNode.key) {
if (nextVNode.el) {
nextChildren[nextEnd] = nextVNode = cloneVNode(nextVNode)
}
2018-10-11 01:13:27 +08:00
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
prevChildren[prevEnd] = nextVNode as MountedVNode
2018-09-19 23:35:38 +08:00
prevEnd--
nextEnd--
if (j > prevEnd || j > nextEnd) {
break outer
}
prevVNode = prevChildren[prevEnd]
nextVNode = nextChildren[nextEnd]
}
}
if (j > prevEnd) {
if (j <= nextEnd) {
nextPos = nextEnd + 1
const nextNode =
nextPos < nextLength ? nextChildren[nextPos].el : endNode
while (j <= nextEnd) {
nextVNode = nextChildren[j]
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
j++
2018-10-11 01:13:27 +08:00
mount(nextVNode, container, contextVNode, isSVG, nextNode)
2018-09-19 23:35:38 +08:00
}
}
} else if (j > nextEnd) {
while (j <= prevEnd) {
removeVNode(prevChildren[j++], container)
2018-09-19 23:35:38 +08:00
}
} else {
let prevStart = j
const nextStart = j
const prevLeft = prevEnd - j + 1
const nextLeft = nextEnd - j + 1
const sources: number[] = []
for (i = 0; i < nextLeft; i++) {
sources.push(0)
}
// Keep track if its possible to remove whole DOM using textContent = ''
let canRemoveWholeContent = prevLeft === prevLength
let moved = false
let pos = 0
let patched = 0
// When sizes are small, just loop them through
if (nextLength < 4 || (prevLeft | nextLeft) < 32) {
for (i = prevStart; i <= prevEnd; i++) {
prevVNode = prevChildren[i]
if (patched < nextLeft) {
for (j = nextStart; j <= nextEnd; j++) {
nextVNode = nextChildren[j]
if (prevVNode.key === nextVNode.key) {
sources[j - nextStart] = i + 1
if (canRemoveWholeContent) {
canRemoveWholeContent = false
while (i > prevStart) {
removeVNode(prevChildren[prevStart++], container)
2018-09-19 23:35:38 +08:00
}
}
if (pos > j) {
moved = true
} else {
pos = j
}
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
2018-10-11 01:13:27 +08:00
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
patched++
break
}
}
if (!canRemoveWholeContent && j > nextEnd) {
removeVNode(prevVNode, container)
2018-09-19 23:35:38 +08:00
}
} else if (!canRemoveWholeContent) {
removeVNode(prevVNode, container)
2018-09-19 23:35:38 +08:00
}
}
} else {
const keyIndex: Record<string, number> = {}
// Map keys by their index
for (i = nextStart; i <= nextEnd; i++) {
keyIndex[nextChildren[i].key as string] = i
}
// Try to patch same keys
for (i = prevStart; i <= prevEnd; i++) {
prevVNode = prevChildren[i]
if (patched < nextLeft) {
j = keyIndex[prevVNode.key as string]
if (j !== void 0) {
if (canRemoveWholeContent) {
canRemoveWholeContent = false
while (i > prevStart) {
removeVNode(prevChildren[prevStart++], container)
2018-09-19 23:35:38 +08:00
}
}
nextVNode = nextChildren[j]
sources[j - nextStart] = i + 1
if (pos > j) {
moved = true
} else {
pos = j
}
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
2018-10-11 01:13:27 +08:00
patch(prevVNode, nextVNode, container, contextVNode, isSVG)
2018-09-19 23:35:38 +08:00
patched++
} else if (!canRemoveWholeContent) {
removeVNode(prevVNode, container)
2018-09-19 23:35:38 +08:00
}
} else if (!canRemoveWholeContent) {
removeVNode(prevVNode, container)
2018-09-19 23:35:38 +08:00
}
}
}
// fast-path: if nothing patched remove all old and add all new
if (canRemoveWholeContent) {
removeChildren(prevChildren as MountedVNode[], container, endNode)
2018-09-19 23:35:38 +08:00
mountArrayChildren(
nextChildren,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
endNode
)
} else {
if (moved) {
const seq = lis(sources)
j = seq.length - 1
for (i = nextLeft - 1; i >= 0; i--) {
if (sources[i] === 0) {
pos = i + nextStart
nextVNode = nextChildren[pos]
if (nextVNode.el) {
nextChildren[pos] = nextVNode = cloneVNode(nextVNode)
}
nextPos = pos + 1
mount(
nextVNode,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
nextPos < nextLength ? nextChildren[nextPos].el : endNode
)
} else if (j < 0 || i !== seq[j]) {
pos = i + nextStart
nextVNode = nextChildren[pos]
nextPos = pos + 1
insertVNode(
nextVNode as MountedVNode,
2018-09-19 23:35:38 +08:00
container,
nextPos < nextLength ? nextChildren[nextPos].el : endNode
)
} else {
j--
}
}
} else if (patched !== nextLeft) {
// when patched count doesn't match b length we need to insert those
// new ones loop backwards so we can use insertBefore
for (i = nextLeft - 1; i >= 0; i--) {
if (sources[i] === 0) {
pos = i + nextStart
nextVNode = nextChildren[pos]
if (nextVNode.el) {
nextChildren[pos] = nextVNode = cloneVNode(nextVNode)
}
nextPos = pos + 1
mount(
nextVNode,
container,
2018-10-11 01:13:27 +08:00
contextVNode,
2018-09-19 23:35:38 +08:00
isSVG,
nextPos < nextLength ? nextChildren[nextPos].el : endNode
)
}
}
}
}
}
}
function insertVNode(
vnode: MountedVNode,
container: RenderNode,
refNode: RenderNode | null
) {
const { flags, childFlags, children } = vnode
if (flags & VNodeFlags.FRAGMENT) {
switch (childFlags) {
case ChildrenFlags.SINGLE_VNODE:
insertVNode(children as MountedVNode, container, refNode)
break
case ChildrenFlags.NO_CHILDREN:
break
default:
for (let i = 0; i < (children as MountedVNode[]).length; i++) {
insertVNode((children as MountedVNode[])[i], container, refNode)
}
}
} else {
insertOrAppend(container, vnode.el as RenderNode, refNode)
}
}
2018-09-19 23:35:38 +08:00
// unmounting ----------------------------------------------------------------
function unmount(vnode: MountedVNode) {
2018-09-26 01:39:19 +08:00
const { flags, data, children, childFlags, ref } = vnode
const isElement = flags & VNodeFlags.ELEMENT
if (isElement || flags & VNodeFlags.FRAGMENT) {
if (isElement && data != null && data.vnodeBeforeUnmount) {
data.vnodeBeforeUnmount(vnode)
}
2018-09-19 23:35:38 +08:00
unmountChildren(children as VNodeChildren, childFlags)
if (teardownVNode !== void 0) {
teardownVNode(vnode)
}
2018-09-26 01:39:19 +08:00
if (isElement && data != null && data.vnodeUnmounted) {
data.vnodeUnmounted(vnode)
}
2018-09-19 23:35:38 +08:00
} else if (flags & VNodeFlags.COMPONENT) {
if (flags & VNodeFlags.COMPONENT_STATEFUL) {
2018-09-27 06:34:21 +08:00
if (flags & VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE) {
2018-10-09 23:37:24 +08:00
deactivateComponentInstance(children as ComponentInstance)
2018-09-27 06:34:21 +08:00
} else {
2018-10-09 23:37:24 +08:00
unmountComponentInstance(children as ComponentInstance)
2018-09-27 05:10:34 +08:00
}
2018-09-19 23:35:38 +08:00
} else {
unmount(children as MountedVNode)
2018-09-19 23:35:38 +08:00
}
} else if (flags & VNodeFlags.PORTAL) {
if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
removeChildren(
children as MountedVNode[],
vnode.tag as RenderNode,
null
)
2018-09-19 23:35:38 +08:00
} else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
removeVNode(children as MountedVNode, vnode.tag as RenderNode)
2018-09-19 23:35:38 +08:00
}
}
if (ref) {
ref(null)
}
}
function unmountChildren(children: VNodeChildren, childFlags: ChildrenFlags) {
if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
unmountArrayChildren(children as MountedVNode[])
2018-09-19 23:35:38 +08:00
} else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
unmount(children as MountedVNode)
2018-09-19 23:35:38 +08:00
}
}
function unmountArrayChildren(children: MountedVNode[]) {
2018-09-19 23:35:38 +08:00
for (let i = 0; i < children.length; i++) {
unmount(children[i])
}
}
function removeVNode(vnode: MountedVNode, container: RenderNode) {
2018-09-19 23:35:38 +08:00
unmount(vnode)
const { el, flags, children, childFlags } = vnode
if (container && el) {
if (flags & VNodeFlags.FRAGMENT) {
switch (childFlags) {
case ChildrenFlags.SINGLE_VNODE:
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++) {
removeVNode((children as MountedVNode[])[i], container)
}
}
} else {
platformRemoveChild(container, el)
}
;(vnode as any).el = null
2018-09-19 23:35:38 +08:00
}
}
function removeChildren(
children: MountedVNode[],
2018-09-19 23:35:38 +08:00
container: RenderNode,
refNode: RenderNode | null
2018-09-19 23:35:38 +08:00
) {
unmountArrayChildren(children)
if (refNode === null) {
2018-09-19 23:35:38 +08:00
platformClearContent(container)
} else {
for (let i = 0; i < children.length; i++) {
removeVNode(children[i], container)
2018-09-19 23:35:38 +08:00
}
}
}
// Component lifecycle -------------------------------------------------------
function mountComponentInstance(
2018-10-11 01:13:27 +08:00
vnode: VNode,
2018-09-19 23:35:38 +08:00
Component: ComponentClass,
container: RenderNode | null,
isSVG: boolean,
endNode: RenderNode | null
2018-09-19 23:35:38 +08:00
): RenderNode {
2018-09-27 05:10:34 +08:00
// a vnode may already have an instance if this is a compat call with
// new Vue()
2018-09-20 11:19:25 +08:00
const instance =
2018-10-11 01:13:27 +08:00
(__COMPAT__ && (vnode.children as ComponentInstance)) ||
2018-10-16 00:41:18 +08:00
createComponentInstance(vnode, Component)
2018-09-19 23:35:38 +08:00
2018-09-27 05:10:34 +08:00
// inject platform-specific unmount to keep-alive container
if ((Component as any)[KeepAliveSymbol] === true) {
;(instance as any).$unmount = unmountComponentInstance
}
2018-10-17 07:10:08 +08:00
const {
$proxy,
$options: { beforeMount, mounted, renderTracked, renderTriggered }
} = instance
if (beforeMount) {
beforeMount.call($proxy)
}
2018-09-19 23:35:38 +08:00
const queueUpdate = (instance.$forceUpdate = () => {
queueJob(instance._updateHandle, flushHooks)
})
instance._updateHandle = autorun(
() => {
2018-09-27 05:10:34 +08:00
if (instance._unmounted) {
2018-09-19 23:35:38 +08:00
return
}
if (instance._mounted) {
updateComponentInstance(instance, isSVG)
2018-09-19 23:35:38 +08:00
} else {
// this will be executed synchronously right here
instance.$vnode = renderInstanceRoot(instance) as MountedVNode
2018-10-11 01:13:27 +08:00
mount(
instance.$vnode,
container,
vnode as MountedVNode,
isSVG,
endNode
)
vnode.el = instance.$vnode.el
2018-10-05 04:44:23 +08:00
if (__COMPAT__) {
2018-10-05 04:35:07 +08:00
// expose __vue__ for devtools
2018-10-11 01:13:27 +08:00
;(vnode.el as any).__vue__ = instance
2018-10-05 04:35:07 +08:00
}
2018-10-05 04:44:23 +08:00
2018-09-19 23:35:38 +08:00
instance._mounted = true
2018-10-17 07:10:08 +08:00
if (vnode.ref) {
mountRef(vnode.ref, $proxy)
}
if (mounted) {
lifecycleHooks.push(() => {
mounted.call($proxy)
})
}
2018-09-19 23:35:38 +08:00
}
},
{
2018-09-25 01:38:54 +08:00
scheduler: queueUpdate,
2018-10-17 07:10:08 +08:00
onTrack: renderTracked,
onTrigger: renderTriggered
2018-09-19 23:35:38 +08:00
}
)
2018-10-11 01:13:27 +08:00
return vnode.el as RenderNode
2018-09-19 23:35:38 +08:00
}
2018-10-09 23:37:24 +08:00
function updateComponentInstance(
instance: ComponentInstance,
isSVG: boolean
) {
if (__DEV__ && instance.$parentVNode) {
pushWarningContext(instance.$parentVNode as VNode)
}
2018-10-17 07:10:08 +08:00
const {
$vnode: prevVNode,
$parentVNode,
$proxy,
$options: { beforeUpdate, updated }
} = instance
if (beforeUpdate) {
beforeUpdate.call($proxy, prevVNode)
}
const nextVNode = (instance.$vnode = renderInstanceRoot(
instance
) as MountedVNode)
const container = platformParentNode(prevVNode.el) as RenderNode
2018-10-17 07:10:08 +08:00
patch(prevVNode, nextVNode, container, $parentVNode as MountedVNode, isSVG)
2018-09-19 23:35:38 +08:00
const el = nextVNode.el as RenderNode
if (__COMPAT__) {
2018-10-05 04:35:07 +08:00
// expose __vue__ for devtools
;(el as any).__vue__ = instance
}
2018-10-11 01:13:27 +08:00
// recursively update contextVNode el for nested HOCs
2018-09-19 23:35:38 +08:00
if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
2018-10-17 07:10:08 +08:00
let vnode = $parentVNode
2018-09-19 23:35:38 +08:00
while (vnode !== null) {
if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
vnode.el = el
}
2018-10-11 01:13:27 +08:00
vnode = vnode.contextVNode
2018-09-19 23:35:38 +08:00
}
}
2018-10-17 07:10:08 +08:00
if (updated) {
2018-09-25 01:38:54 +08:00
// Because the child's update is executed by the scheduler and not
// synchronously within the parent's update call, the child's updated hook
// will be added to the queue AFTER the parent's, but they should be
// invoked BEFORE the parent's. Therefore we add them to the head of the
// queue instead.
2018-09-26 01:39:19 +08:00
lifecycleHooks.unshift(() => {
2018-10-17 07:10:08 +08:00
updated.call($proxy, nextVNode)
2018-09-19 23:35:38 +08:00
})
}
2018-09-26 01:39:19 +08:00
if (vnodeUpdatedHooks.length > 0) {
const vnodeUpdatedHooksForCurrentInstance = vnodeUpdatedHooks.slice()
vnodeUpdatedHooks.length = 0
lifecycleHooks.unshift(() => {
for (let i = 0; i < vnodeUpdatedHooksForCurrentInstance.length; i++) {
vnodeUpdatedHooksForCurrentInstance[i]()
}
})
}
if (__DEV__ && instance.$parentVNode) {
popWarningContext()
}
2018-09-19 23:35:38 +08:00
}
2018-10-09 23:37:24 +08:00
function unmountComponentInstance(instance: ComponentInstance) {
2018-09-27 05:10:34 +08:00
if (instance._unmounted) {
return
}
2018-10-17 07:10:08 +08:00
const {
$vnode,
$proxy,
_updateHandle,
$options: { beforeUnmount, unmounted }
} = instance
if (beforeUnmount) {
beforeUnmount.call($proxy)
}
if ($vnode) {
unmount($vnode)
}
stop(_updateHandle)
2018-09-19 23:35:38 +08:00
teardownComponentInstance(instance)
2018-09-27 05:10:34 +08:00
instance._unmounted = true
2018-10-17 07:10:08 +08:00
if (unmounted) {
unmounted.call($proxy)
2018-09-19 23:35:38 +08:00
}
}
2018-09-27 06:34:21 +08:00
// Keep Alive ----------------------------------------------------------------
function activateComponentInstance(
vnode: VNode,
container: RenderNode | null,
endNode: RenderNode | null
) {
2018-10-09 23:37:24 +08:00
const instance = vnode.children as ComponentInstance
vnode.el = instance.$el as RenderNode
if (container != null) {
insertVNode(instance.$vnode, container, endNode)
}
2018-09-27 06:34:21 +08:00
lifecycleHooks.push(() => {
callActivatedHook(instance, true)
})
}
2018-10-09 23:37:24 +08:00
function callActivatedHook(instance: ComponentInstance, asRoot: boolean) {
2018-09-27 06:34:21 +08:00
// 1. check if we are inside an inactive parent tree.
if (asRoot) {
instance._inactiveRoot = false
if (isInInactiveTree(instance)) return
}
if (asRoot || !instance._inactiveRoot) {
// 2. recursively call activated on child tree, depth-first
2018-10-17 07:10:08 +08:00
const {
$children,
$proxy,
$options: { activated }
} = instance
2018-09-27 06:34:21 +08:00
for (let i = 0; i < $children.length; i++) {
callActivatedHook($children[i], false)
}
2018-10-17 07:10:08 +08:00
if (activated) {
activated.call($proxy)
2018-09-27 06:34:21 +08:00
}
}
}
2018-10-09 23:37:24 +08:00
function deactivateComponentInstance(instance: ComponentInstance) {
2018-09-27 06:34:21 +08:00
callDeactivateHook(instance, true)
}
2018-10-09 23:37:24 +08:00
function callDeactivateHook(instance: ComponentInstance, asRoot: boolean) {
2018-09-27 06:34:21 +08:00
if (asRoot) {
instance._inactiveRoot = true
if (isInInactiveTree(instance)) return
}
if (asRoot || !instance._inactiveRoot) {
// 2. recursively call deactivated on child tree, depth-first
2018-10-17 07:10:08 +08:00
const {
$children,
$proxy,
$options: { deactivated }
} = instance
2018-09-27 06:34:21 +08:00
for (let i = 0; i < $children.length; i++) {
callDeactivateHook($children[i], false)
}
2018-10-17 07:10:08 +08:00
if (deactivated) {
deactivated.call($proxy)
2018-09-27 06:34:21 +08:00
}
}
}
2018-10-09 23:37:24 +08:00
function isInInactiveTree(instance: ComponentInstance): boolean {
2018-09-27 06:34:21 +08:00
while ((instance = instance.$parent as any) !== null) {
if (instance._inactiveRoot) return true
}
return false
}
2018-09-19 23:35:38 +08:00
// TODO hydrating ------------------------------------------------------------
// API -----------------------------------------------------------------------
2018-10-11 01:13:27 +08:00
function render(
vnode: VNode | null,
container: any
): ComponentInstance | null {
2018-09-19 23:35:38 +08:00
const prevVNode = container.vnode
if (vnode && vnode.el) {
vnode = cloneVNode(vnode)
}
if (prevVNode == null) {
if (vnode) {
mount(vnode, container, null, false, null)
container.vnode = vnode
}
} else {
if (vnode) {
patch(prevVNode, vnode, container, null, false)
container.vnode = vnode
} else {
removeVNode(prevVNode, container)
2018-09-19 23:35:38 +08:00
container.vnode = null
}
}
flushHooks()
return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
2018-10-09 23:37:24 +08:00
? (vnode.children as ComponentInstance).$proxy
: null
2018-09-19 23:35:38 +08:00
}
return { render }
}
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
export function lis(arr: number[]): number[] {
const p = arr.slice()
const result = [0]
let i
let j
let u
let v
let c
const len = arr.length
for (i = 0; i < len; i++) {
const arrI = arr[i]
if (arrI !== 0) {
j = result[result.length - 1]
if (arr[j] < arrI) {
p[i] = j
result.push(i)
continue
}
u = 0
v = result.length - 1
while (u < v) {
c = ((u + v) / 2) | 0
if (arr[result[c]] < arrI) {
u = c + 1
} else {
v = c
}
}
if (arrI < arr[result[u]]) {
if (u > 0) {
p[i] = result[u - 1]
}
result[u] = i
}
}
}
u = result.length
v = result[u - 1]
while (u-- > 0) {
result[u] = v
v = p[v]
}
return result
}