if (flags & VNodeFlags.COMPONENT_STATEFUL) {
if (flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) {
// kept-alive
- el = vnode.el as RenderNode
- // TODO activated hook
+ el = activateComponentInstance(vnode)
} else {
el = mountComponentInstance(
vnode,
)
}
} else {
- debugger
// functional component
const render = tag as FunctionalComponent
const { props, attrs } = resolveProps(data, render.props, render)
}
} else if (flags & VNodeFlags.COMPONENT) {
if (flags & VNodeFlags.COMPONENT_STATEFUL) {
- if ((flags & VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE) === 0) {
+ if (flags & VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE) {
+ deactivateComponentInstance(children as MountedComponent)
+ } else {
unmountComponentInstance(children as MountedComponent)
}
} else {
}
}
+ // Keep Alive ----------------------------------------------------------------
+
+ function activateComponentInstance(vnode: VNode): RenderNode {
+ const instance = vnode.children as MountedComponent
+ const el = (vnode.el = instance.$el)
+ lifecycleHooks.push(() => {
+ callActivatedHook(instance, true)
+ })
+ return el as RenderNode
+ }
+
+ function callActivatedHook(instance: MountedComponent, asRoot: boolean) {
+ // 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
+ const { $children } = instance
+ for (let i = 0; i < $children.length; i++) {
+ callActivatedHook($children[i], false)
+ }
+ if (instance.activated) {
+ instance.activated.call(instance.$proxy)
+ }
+ }
+ }
+
+ function deactivateComponentInstance(instance: MountedComponent) {
+ callDeactivateHook(instance, true)
+ }
+
+ function callDeactivateHook(instance: MountedComponent, asRoot: boolean) {
+ if (asRoot) {
+ instance._inactiveRoot = true
+ if (isInInactiveTree(instance)) return
+ }
+ if (asRoot || !instance._inactiveRoot) {
+ // 2. recursively call deactivated on child tree, depth-first
+ const { $children } = instance
+ for (let i = 0; i < $children.length; i++) {
+ callDeactivateHook($children[i], false)
+ }
+ if (instance.deactivated) {
+ instance.deactivated.call(instance.$proxy)
+ }
+ }
+ }
+
+ function isInInactiveTree(instance: MountedComponent): boolean {
+ while ((instance = instance.$parent as any) !== null) {
+ if (instance._inactiveRoot) return true
+ }
+ return false
+ }
+
// TODO hydrating ------------------------------------------------------------
// API -----------------------------------------------------------------------
)
} else if (tag === Fragment) {
if (__DEV__ && ref) {
- // TODO warn fragment cannot have ref
+ console.warn(
+ 'Ref cannot be used on Fragments. Use it on inner elements instead.'
+ )
}
return createFragment(children, ChildrenFlags.UNKNOWN_CHILDREN, key)
} else if (tag === Portal) {
if (__DEV__ && !portalTarget) {
- // TODO warn portal must have a target
+ console.warn('Portal must have a target: ', portalTarget)
}
return createPortal(
portalTarget,
ref
)
} else {
+ if (
+ __DEV__ &&
+ (!tag || (typeof tag !== 'function' && typeof tag !== 'object'))
+ ) {
+ console.warn('Invalid component passed to h(): ', tag)
+ }
// component
return createComponentVNode(
tag,
this.keys = new Set()
}
- unmounted() {
+ beforeUnmount() {
this.cache.forEach(vnode => {
// change flag so it can be properly unmounted
vnode.flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
const { cache, keys } = this
const key = vnode.key == null ? comp : vnode.key
const cached = cache.get(key)
+ cache.set(key, vnode)
+
if (cached) {
vnode.children = cached.children
- vnode.el = cached.el
+ // avoid vnode being mounted as fresh
vnode.flags |= VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE
// make this key the freshest
keys.delete(key)
keys.add(key)
} else {
- cache.set(key, vnode)
keys.add(key)
// prune oldest entry
if (max && keys.size > parseInt(max, 10)) {
this.pruneCacheEntry(Array.from(this.keys)[0])
}
}
+ // avoid vnode being unmounted
vnode.flags |= VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE
return vnode
}