refactor: separate vnode hooks and directive hooks

This commit is contained in:
Evan You
2020-03-18 11:43:32 -04:00
parent 6679799540
commit aa4ab39c1a
8 changed files with 164 additions and 117 deletions

View File

@@ -51,7 +51,6 @@ import {
import { resolveProps } from './componentProps'
import { resolveSlots } from './componentSlots'
import { pushWarningContext, popWarningContext, warn } from './warning'
import { invokeDirectiveHook } from './directives'
import { ComponentPublicInstance } from './componentProxy'
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
import {
@@ -62,8 +61,13 @@ import {
import { PortalImpl } from './components/Portal'
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
import { registerHMR, unregisterHMR } from './hmr'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import {
ErrorCodes,
callWithErrorHandling,
callWithAsyncErrorHandling
} from './errorHandling'
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
import { invokeDirectiveHook } from './directives'
const __HMR__ = __BUNDLER__ && __DEV__
@@ -526,7 +530,15 @@ function baseCreateRenderer<
) => {
let el: HostElement
let vnodeHook: VNodeHook | undefined | null
const { type, props, shapeFlag, transition, scopeId, patchFlag } = vnode
const {
type,
props,
shapeFlag,
transition,
scopeId,
patchFlag,
dirs
} = vnode
if (
vnode.el !== null &&
hostCloneNode !== undefined &&
@@ -546,9 +558,12 @@ function baseCreateRenderer<
}
}
if ((vnodeHook = props.onVnodeBeforeMount) != null) {
invokeDirectiveHook(vnodeHook, parentComponent, vnode)
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
}
if (dirs != null) {
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
}
// scopeId
if (__BUNDLER__) {
@@ -585,11 +600,13 @@ function baseCreateRenderer<
hostInsert(el, container, anchor)
if (
(vnodeHook = props && props.onVnodeMounted) != null ||
(transition != null && !transition.persisted)
(transition != null && !transition.persisted) ||
dirs != null
) {
queuePostRenderEffect(() => {
vnodeHook && invokeDirectiveHook(vnodeHook, parentComponent, vnode)
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
transition && !transition.persisted && transition.enter(el)
dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
}, parentSuspense)
}
}
@@ -630,13 +647,16 @@ function baseCreateRenderer<
optimized: boolean
) => {
const el = (n2.el = n1.el) as HostElement
let { patchFlag, dynamicChildren } = n2
let { patchFlag, dynamicChildren, dirs } = n2
const oldProps = (n1 && n1.props) || EMPTY_OBJ
const newProps = n2.props || EMPTY_OBJ
let vnodeHook: VNodeHook | undefined | null
if ((vnodeHook = newProps.onVnodeBeforeUpdate) != null) {
invokeDirectiveHook(vnodeHook, parentComponent, n2, n1)
invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
}
if (dirs != null) {
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
}
if (__HMR__ && parentComponent && parentComponent.renderUpdated) {
@@ -750,9 +770,10 @@ function baseCreateRenderer<
)
}
if ((vnodeHook = newProps.onVnodeUpdated) != null) {
if ((vnodeHook = newProps.onVnodeUpdated) != null || dirs != null) {
queuePostRenderEffect(() => {
invokeDirectiveHook(vnodeHook!, parentComponent, n2, n1)
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated')
}, parentSuspense)
}
}
@@ -1063,12 +1084,19 @@ function baseCreateRenderer<
// create reactive effect for rendering
instance.update = effect(function componentEffect() {
if (!instance.isMounted) {
let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode
const { bm, m, a, parent } = instance
const subTree = (instance.subTree = renderComponentRoot(instance))
// beforeMount hook
if (instance.bm !== null) {
invokeHooks(instance.bm)
if (bm !== null) {
invokeHooks(bm)
}
if (initialVNode.el && hydrateNode) {
// onVnodeBeforeMount
if ((vnodeHook = props && props.onVnodeBeforeMount) != null) {
invokeVNodeHook(vnodeHook, parent, initialVNode)
}
if (el && hydrateNode) {
// vnode has adopted host node - perform hydration instead of mount.
hydrateNode(
initialVNode.el as Node,
@@ -1089,36 +1117,50 @@ function baseCreateRenderer<
initialVNode.el = subTree.el
}
// mounted hook
if (instance.m !== null) {
queuePostRenderEffect(instance.m, parentSuspense)
if (m !== null) {
queuePostRenderEffect(m, parentSuspense)
}
// onVnodeMounted
if ((vnodeHook = props && props.onVnodeMounted) != null) {
queuePostRenderEffect(() => {
invokeVNodeHook(vnodeHook!, parent, initialVNode)
}, parentSuspense)
}
// activated hook for keep-alive roots.
if (
instance.a !== null &&
instance.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
a !== null &&
initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
) {
queuePostRenderEffect(instance.a, parentSuspense)
queuePostRenderEffect(a, parentSuspense)
}
instance.isMounted = true
} else {
// updateComponent
// This is triggered by mutation of component's own state (next: null)
// OR parent calling processComponent (next: HostVNode)
const { next } = instance
let { next, bu, u, parent, vnode } = instance
let vnodeHook: VNodeHook | null | undefined
if (__DEV__) {
pushWarningContext(next || instance.vnode)
}
if (next !== null) {
updateComponentPreRender(instance, next)
} else {
next = vnode
}
const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree
instance.subTree = nextTree
// beforeUpdate hook
if (instance.bu !== null) {
invokeHooks(instance.bu)
if (bu !== null) {
invokeHooks(bu)
}
// onVnodeBeforeUpdate
if (
(vnodeHook = next.props && next.props.onVnodeBeforeUpdate) != null
) {
invokeVNodeHook(vnodeHook, parent, next, vnode)
}
// reset refs
// only needed if previous patch had refs
@@ -1136,7 +1178,7 @@ function baseCreateRenderer<
parentSuspense,
isSVG
)
instance.vnode.el = nextTree.el
next.el = nextTree.el
if (next === null) {
// self-triggered update. In case of HOC, update parent component
// vnode el. HOC is indicated by parent instance's subTree pointing
@@ -1144,10 +1186,15 @@ function baseCreateRenderer<
updateHOCHostEl(instance, nextTree.el)
}
// updated hook
if (instance.u !== null) {
queuePostRenderEffect(instance.u, parentSuspense)
if (u !== null) {
queuePostRenderEffect(u, parentSuspense)
}
// onVnodeUpdated
if ((vnodeHook = next.props && next.props.onVnodeUpdated) != null) {
queuePostRenderEffect(() => {
invokeVNodeHook(vnodeHook!, parent, next!, vnode)
}, parentSuspense)
}
if (__DEV__) {
popWarningContext()
}
@@ -1617,7 +1664,8 @@ function baseCreateRenderer<
parentSuspense,
doRemove = false
) => {
const { props, ref, children, dynamicChildren, shapeFlag } = vnode
const { props, ref, children, dynamicChildren, shapeFlag, dirs } = vnode
const shouldInvokeDirs = dirs != null && shapeFlag & ShapeFlags.ELEMENT
let vnodeHook: VNodeHook | undefined | null
// unset ref
@@ -1640,7 +1688,10 @@ function baseCreateRenderer<
}
if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
invokeDirectiveHook(vnodeHook, parentComponent, vnode)
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
if (shouldInvokeDirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
}
if (dynamicChildren != null) {
@@ -1654,9 +1705,14 @@ function baseCreateRenderer<
remove(vnode)
}
if ((vnodeHook = props && props.onVnodeUnmounted) != null) {
if (
(vnodeHook = props && props.onVnodeUnmounted) != null ||
shouldInvokeDirs
) {
queuePostRenderEffect(() => {
invokeDirectiveHook(vnodeHook!, parentComponent, vnode)
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
shouldInvokeDirs &&
invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')
}, parentSuspense)
}
}
@@ -1880,6 +1936,18 @@ function baseCreateRenderer<
}
}
export function invokeVNodeHook(
hook: VNodeHook,
instance: ComponentInternalInstance | null,
vnode: VNode,
prevVNode: VNode | null = null
) {
callWithAsyncErrorHandling(hook, instance, ErrorCodes.VNODE_HOOK, [
vnode,
prevVNode
])
}
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
function getSequence(arr: number[]): number[] {
const p = arr.slice()