diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index 7870299a..7a1dc92d 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -249,7 +249,9 @@ export function createRenderer(options: RendererOptions) { if (isReservedProp(key)) continue hostPatchProp(el, key, props[key], null, isSVG) } - invokeDirectiveHook(props.vnodeBeforeMount, parentComponent, vnode) + if (props.vnodeBeforeMount != null) { + invokeDirectiveHook(props.vnodeBeforeMount, parentComponent, vnode) + } } if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { hostSetElementText(el, vnode.children as string) @@ -263,8 +265,10 @@ export function createRenderer(options: RendererOptions) { ) } hostInsert(el, container, anchor) - if (props != null) { - invokeDirectiveHook(props.vnodeMounted, parentComponent, vnode) + if (props != null && props.vnodeMounted != null) { + queuePostFlushCb(() => { + invokeDirectiveHook(props.vnodeMounted, parentComponent, vnode) + }) } } @@ -294,6 +298,10 @@ export function createRenderer(options: RendererOptions) { const oldProps = (n1 && n1.props) || EMPTY_OBJ const newProps = n2.props || EMPTY_OBJ + if (newProps.vnodeBeforeUpdate != null) { + invokeDirectiveHook(newProps.vnodeBeforeUpdate, parentComponent, n2) + } + if (patchFlag) { // the presence of a patchFlag means this element's render code was // generated by the compiler and can take the fast path. @@ -379,6 +387,12 @@ export function createRenderer(options: RendererOptions) { // full diff patchChildren(n1, n2, el, null, parentComponent, isSVG) } + + if (newProps.vnodeUpdated != null) { + queuePostFlushCb(() => { + invokeDirectiveHook(newProps.vnodeUpdated, parentComponent, n2) + }) + } } function patchProps( @@ -1017,27 +1031,37 @@ export function createRenderer(options: RendererOptions) { parentComponent: ComponentInstance | null, doRemove?: boolean ) { + const { + props, + ref, + type, + component, + children, + dynamicChildren, + shapeFlag, + anchor + } = vnode + // unset ref - if (vnode.ref !== null && parentComponent !== null) { - setRef(vnode.ref, null, parentComponent, null) + if (ref !== null && parentComponent !== null) { + setRef(ref, null, parentComponent, null) } - const instance = vnode.component - if (instance != null) { - unmountComponent(instance, doRemove) + if (component != null) { + unmountComponent(component, doRemove) return } - const shouldRemoveChildren = vnode.type === Fragment && doRemove - if (vnode.dynamicChildren != null) { + if (props != null && props.vnodeBeforeUnmount != null) { + invokeDirectiveHook(props.vnodeBeforeUnmount, parentComponent, vnode) + } + + const shouldRemoveChildren = type === Fragment && doRemove + if (dynamicChildren != null) { + unmountChildren(dynamicChildren, parentComponent, shouldRemoveChildren) + } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { unmountChildren( - vnode.dynamicChildren, - parentComponent, - shouldRemoveChildren - ) - } else if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) { - unmountChildren( - vnode.children as VNode[], + children as VNode[], parentComponent, shouldRemoveChildren ) @@ -1045,7 +1069,13 @@ export function createRenderer(options: RendererOptions) { if (doRemove) { hostRemove(vnode.el) - if (vnode.anchor != null) hostRemove(vnode.anchor) + if (anchor != null) hostRemove(anchor) + } + + if (props != null && props.vnodeUnmounted != null) { + queuePostFlushCb(() => { + invokeDirectiveHook(props.vnodeUnmounted, parentComponent, vnode) + }) } } diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index bb799aa8..8e3bd6f2 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -127,9 +127,6 @@ export function invokeDirectiveHook( instance: ComponentInstance | null, vnode: VNode ) { - if (hook == null) { - return - } const args = [vnode] if (isArray(hook)) { for (let i = 0; i < hook.length; i++) { diff --git a/packages/runtime-core/src/patchFlags.ts b/packages/runtime-core/src/patchFlags.ts index 279a0019..9061107c 100644 --- a/packages/runtime-core/src/patchFlags.ts +++ b/packages/runtime-core/src/patchFlags.ts @@ -45,21 +45,22 @@ export const enum PatchFlags { // exclusive with CLASS, STYLE and PROPS. FULL_PROPS = 1 << 4, + // Indicates an element that only needs non-props patching, e.g. ref or + // directives (vnodeXXX hooks). It simply marks the vnode as "need patch", + // since every pathced vnode checks for refs and vnodeXXX hooks. + NEED_PATCH = 1 << 5, + // Indicates a fragment or element with keyed or partially-keyed v-for // children - KEYED = 1 << 5, + KEYED = 1 << 6, // Indicates a fragment or element that contains unkeyed v-for children - UNKEYED = 1 << 6, + UNKEYED = 1 << 7, // Indicates a component with dynamic slots (e.g. slot that references a v-for // iterated value, or dynamic slot names). // Components with this flag are always force updated. - DYNAMIC_SLOTS = 1 << 7, - - // Indicates an element with ref. This includes static string refs because the - // refs object is refreshed on each update and all refs need to set again. - REF = 1 << 8 + DYNAMIC_SLOTS = 1 << 8 } // runtime object for public consumption @@ -68,8 +69,8 @@ export const PublicPatchFlags = { CLASS: PatchFlags.CLASS, STYLE: PatchFlags.STYLE, PROPS: PatchFlags.PROPS, + NEED_PATCH: PatchFlags.NEED_PATCH, FULL_PROPS: PatchFlags.FULL_PROPS, KEYED: PatchFlags.KEYED, - UNKEYED: PatchFlags.UNKEYED, - REF: PatchFlags.REF + UNKEYED: PatchFlags.UNKEYED } diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 3a79c195..1fa122f5 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -157,8 +157,8 @@ export function createVNode( normalizeChildren(vnode, children) - // presence of a patch flag indicates this node is dynamic - // component nodes also should always be tracked, because even if the + // presence of a patch flag indicates this node needs patching on updates. + // component nodes also should always be patched, because even if the // component doesn't need to update, it needs to persist the instance on to // the next vnode so that it can be properly unmounted later. if (