feat: activated/deactivated hooks
This commit is contained in:
parent
7c2ec8ace0
commit
ee50fb9723
@ -54,6 +54,8 @@ export interface MountedComponent<D = Data, P = Data>
|
||||
beforeUnmount?(): void
|
||||
unmounted?(): void
|
||||
errorCaptured?(): (err: Error, type: ErrorTypes) => boolean | void
|
||||
activated?(): void
|
||||
deactivated?(): void
|
||||
|
||||
_updateHandle: Autorun
|
||||
_queueJob: ((fn: () => void) => void)
|
||||
@ -95,6 +97,7 @@ class InternalComponent {
|
||||
public _queueJob: ((fn: () => void) => void) | null = null
|
||||
public _revokeProxy: () => void
|
||||
public _isVue: boolean = true
|
||||
public _inactiveRoot: boolean = false
|
||||
|
||||
constructor(options?: ComponentOptions) {
|
||||
this.$options = options || (this.constructor as any).options || EMPTY_OBJ
|
||||
|
@ -122,17 +122,13 @@ export function normalizeComponentRoot(
|
||||
componentVNode &&
|
||||
(flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
|
||||
) {
|
||||
const isKeepAlive = (flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) > 0
|
||||
if (
|
||||
inheritAttrs !== false &&
|
||||
attrs !== void 0 &&
|
||||
Object.keys(attrs).length > 0
|
||||
) {
|
||||
vnode = cloneVNode(vnode, attrs)
|
||||
if (isKeepAlive) {
|
||||
vnode.el = el
|
||||
}
|
||||
} else if (el && !isKeepAlive) {
|
||||
} else if (el) {
|
||||
vnode = cloneVNode(vnode)
|
||||
}
|
||||
if (flags & VNodeFlags.COMPONENT) {
|
||||
|
@ -274,8 +274,7 @@ export function createRenderer(options: RendererOptions) {
|
||||
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,
|
||||
@ -287,7 +286,6 @@ export function createRenderer(options: RendererOptions) {
|
||||
)
|
||||
}
|
||||
} else {
|
||||
debugger
|
||||
// functional component
|
||||
const render = tag as FunctionalComponent
|
||||
const { props, attrs } = resolveProps(data, render.props, render)
|
||||
@ -1106,7 +1104,9 @@ export function createRenderer(options: RendererOptions) {
|
||||
}
|
||||
} 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 {
|
||||
@ -1303,6 +1303,63 @@ export function createRenderer(options: RendererOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 -----------------------------------------------------------------------
|
||||
|
@ -80,12 +80,14 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||
)
|
||||
} 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,
|
||||
@ -95,6 +97,12 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||
ref
|
||||
)
|
||||
} else {
|
||||
if (
|
||||
__DEV__ &&
|
||||
(!tag || (typeof tag !== 'function' && typeof tag !== 'object'))
|
||||
) {
|
||||
console.warn('Invalid component passed to h(): ', tag)
|
||||
}
|
||||
// component
|
||||
return createComponentVNode(
|
||||
tag,
|
||||
|
@ -29,7 +29,7 @@ export class KeepAlive extends Component<{}, KeepAliveProps> {
|
||||
this.keys = new Set()
|
||||
}
|
||||
|
||||
unmounted() {
|
||||
beforeUnmount() {
|
||||
this.cache.forEach(vnode => {
|
||||
// change flag so it can be properly unmounted
|
||||
vnode.flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
|
||||
@ -88,21 +88,23 @@ export class KeepAlive extends Component<{}, KeepAliveProps> {
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user