refactor: separate vnode hooks and directive hooks
This commit is contained in:
parent
6679799540
commit
aa4ab39c1a
@ -131,7 +131,6 @@ export interface ComponentInternalInstance {
|
|||||||
data: Data
|
data: Data
|
||||||
props: Data
|
props: Data
|
||||||
attrs: Data
|
attrs: Data
|
||||||
vnodeHooks: Data
|
|
||||||
slots: Slots
|
slots: Slots
|
||||||
proxy: ComponentPublicInstance | null
|
proxy: ComponentPublicInstance | null
|
||||||
// alternative proxy used only for runtime-compiled render functions using
|
// alternative proxy used only for runtime-compiled render functions using
|
||||||
@ -204,7 +203,6 @@ export function createComponentInstance(
|
|||||||
data: EMPTY_OBJ,
|
data: EMPTY_OBJ,
|
||||||
props: EMPTY_OBJ,
|
props: EMPTY_OBJ,
|
||||||
attrs: EMPTY_OBJ,
|
attrs: EMPTY_OBJ,
|
||||||
vnodeHooks: EMPTY_OBJ,
|
|
||||||
slots: EMPTY_OBJ,
|
slots: EMPTY_OBJ,
|
||||||
refs: EMPTY_OBJ,
|
refs: EMPTY_OBJ,
|
||||||
|
|
||||||
|
@ -106,7 +106,6 @@ export function resolveProps(
|
|||||||
const { 0: options, 1: needCastKeys } = normalizePropsOptions(_options)!
|
const { 0: options, 1: needCastKeys } = normalizePropsOptions(_options)!
|
||||||
const props: Data = {}
|
const props: Data = {}
|
||||||
let attrs: Data | undefined = undefined
|
let attrs: Data | undefined = undefined
|
||||||
let vnodeHooks: Data | undefined = undefined
|
|
||||||
|
|
||||||
// update the instance propsProxy (passed to setup()) to trigger potential
|
// update the instance propsProxy (passed to setup()) to trigger potential
|
||||||
// changes
|
// changes
|
||||||
@ -128,10 +127,6 @@ export function resolveProps(
|
|||||||
const value = rawProps[key]
|
const value = rawProps[key]
|
||||||
// key, ref are reserved and never passed down
|
// key, ref are reserved and never passed down
|
||||||
if (isReservedProp(key)) {
|
if (isReservedProp(key)) {
|
||||||
if (key !== 'key' && key !== 'ref') {
|
|
||||||
// vnode hooks.
|
|
||||||
;(vnodeHooks || (vnodeHooks = {}))[key] = value
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// prop option names are camelized during normalization, so to support
|
// prop option names are camelized during normalization, so to support
|
||||||
@ -208,7 +203,6 @@ export function resolveProps(
|
|||||||
|
|
||||||
instance.props = props
|
instance.props = props
|
||||||
instance.attrs = attrs || EMPTY_OBJ
|
instance.attrs = attrs || EMPTY_OBJ
|
||||||
instance.vnodeHooks = vnodeHooks || EMPTY_OBJ
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizationMap = new WeakMap<
|
const normalizationMap = new WeakMap<
|
||||||
|
@ -45,7 +45,6 @@ export function renderComponentRoot(
|
|||||||
props,
|
props,
|
||||||
slots,
|
slots,
|
||||||
attrs,
|
attrs,
|
||||||
vnodeHooks,
|
|
||||||
emit,
|
emit,
|
||||||
renderCache
|
renderCache
|
||||||
} = instance
|
} = instance
|
||||||
@ -104,10 +103,6 @@ export function renderComponentRoot(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// inherit vnode hooks
|
|
||||||
if (vnodeHooks !== EMPTY_OBJ) {
|
|
||||||
result = cloneVNode(result, vnodeHooks)
|
|
||||||
}
|
|
||||||
// inherit scopeId
|
// inherit scopeId
|
||||||
const parentScopeId = parent && parent.type.__scopeId
|
const parentScopeId = parent && parent.type.__scopeId
|
||||||
if (parentScopeId) {
|
if (parentScopeId) {
|
||||||
|
@ -11,8 +11,8 @@ return withDirectives(h(comp), [
|
|||||||
])
|
])
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { VNode, VNodeHook } from './vnode'
|
import { VNode } from './vnode'
|
||||||
import { isFunction, EMPTY_OBJ, makeMap, EMPTY_ARR } from '@vue/shared'
|
import { isFunction, EMPTY_OBJ, makeMap } from '@vue/shared'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { ComponentInternalInstance, Data } from './component'
|
import { ComponentInternalInstance, Data } from './component'
|
||||||
import { currentRenderingInstance } from './componentRenderUtils'
|
import { currentRenderingInstance } from './componentRenderUtils'
|
||||||
@ -72,36 +72,6 @@ export function validateDirectiveName(name: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const directiveToVnodeHooksMap = /*#__PURE__*/ [
|
|
||||||
'beforeMount',
|
|
||||||
'mounted',
|
|
||||||
'beforeUpdate',
|
|
||||||
'updated',
|
|
||||||
'beforeUnmount',
|
|
||||||
'unmounted'
|
|
||||||
].reduce(
|
|
||||||
(map, key: keyof ObjectDirective) => {
|
|
||||||
const vnodeKey = `onVnode` + key[0].toUpperCase() + key.slice(1)
|
|
||||||
const vnodeHook = (vnode: VNode, prevVnode: VNode | null) => {
|
|
||||||
const bindings = vnode.dirs!
|
|
||||||
const prevBindings = prevVnode ? prevVnode.dirs! : EMPTY_ARR
|
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
|
||||||
const binding = bindings[i]
|
|
||||||
const hook = binding.dir[key] as DirectiveHook
|
|
||||||
if (hook != null) {
|
|
||||||
if (prevVnode != null) {
|
|
||||||
binding.oldValue = prevBindings[i].value
|
|
||||||
}
|
|
||||||
hook(vnode.el, binding, vnode, prevVnode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map[key] = [vnodeKey, vnodeHook]
|
|
||||||
return map
|
|
||||||
},
|
|
||||||
{} as Record<string, [string, Function]>
|
|
||||||
)
|
|
||||||
|
|
||||||
// Directive, value, argument, modifiers
|
// Directive, value, argument, modifiers
|
||||||
export type DirectiveArguments = Array<
|
export type DirectiveArguments = Array<
|
||||||
| [Directive]
|
| [Directive]
|
||||||
@ -120,9 +90,7 @@ export function withDirectives<T extends VNode>(
|
|||||||
return vnode
|
return vnode
|
||||||
}
|
}
|
||||||
const instance = internalInstance.proxy
|
const instance = internalInstance.proxy
|
||||||
const props = vnode.props || (vnode.props = {})
|
const bindings: DirectiveBinding[] = vnode.dirs || (vnode.dirs = [])
|
||||||
const bindings = vnode.dirs || (vnode.dirs = new Array(directives.length))
|
|
||||||
const injected: Record<string, true> = {}
|
|
||||||
for (let i = 0; i < directives.length; i++) {
|
for (let i = 0; i < directives.length; i++) {
|
||||||
let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]
|
let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]
|
||||||
if (isFunction(dir)) {
|
if (isFunction(dir)) {
|
||||||
@ -131,36 +99,39 @@ export function withDirectives<T extends VNode>(
|
|||||||
updated: dir
|
updated: dir
|
||||||
} as ObjectDirective
|
} as ObjectDirective
|
||||||
}
|
}
|
||||||
bindings[i] = {
|
bindings.push({
|
||||||
dir,
|
dir,
|
||||||
instance,
|
instance,
|
||||||
value,
|
value,
|
||||||
oldValue: void 0,
|
oldValue: void 0,
|
||||||
arg,
|
arg,
|
||||||
modifiers
|
modifiers
|
||||||
}
|
})
|
||||||
// inject onVnodeXXX hooks
|
|
||||||
for (const key in dir) {
|
|
||||||
const mapped = directiveToVnodeHooksMap[key]
|
|
||||||
if (mapped && !injected[key]) {
|
|
||||||
const { 0: hookName, 1: hook } = mapped
|
|
||||||
const existing = props[hookName]
|
|
||||||
props[hookName] = existing ? [].concat(existing, hook as any) : hook
|
|
||||||
injected[key] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return vnode
|
return vnode
|
||||||
}
|
}
|
||||||
|
|
||||||
export function invokeDirectiveHook(
|
export function invokeDirectiveHook(
|
||||||
hook: VNodeHook | VNodeHook[],
|
|
||||||
instance: ComponentInternalInstance | null,
|
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
prevVNode: VNode | null = null
|
prevVNode: VNode | null,
|
||||||
|
instance: ComponentInternalInstance | null,
|
||||||
|
name: keyof ObjectDirective
|
||||||
) {
|
) {
|
||||||
|
const bindings = vnode.dirs!
|
||||||
|
const oldBindings = prevVNode && prevVNode.dirs!
|
||||||
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
|
const binding = bindings[i]
|
||||||
|
if (oldBindings) {
|
||||||
|
binding.oldValue = oldBindings[i].value
|
||||||
|
}
|
||||||
|
const hook = binding.dir[name] as DirectiveHook | undefined
|
||||||
|
if (hook) {
|
||||||
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
|
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
|
||||||
|
vnode.el,
|
||||||
|
binding,
|
||||||
vnode,
|
vnode,
|
||||||
prevVNode
|
prevVNode
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ export const enum ErrorCodes {
|
|||||||
WATCH_CLEANUP,
|
WATCH_CLEANUP,
|
||||||
NATIVE_EVENT_HANDLER,
|
NATIVE_EVENT_HANDLER,
|
||||||
COMPONENT_EVENT_HANDLER,
|
COMPONENT_EVENT_HANDLER,
|
||||||
|
VNODE_HOOK,
|
||||||
DIRECTIVE_HOOK,
|
DIRECTIVE_HOOK,
|
||||||
TRANSITION_HOOK,
|
TRANSITION_HOOK,
|
||||||
APP_ERROR_HANDLER,
|
APP_ERROR_HANDLER,
|
||||||
@ -42,6 +43,7 @@ export const ErrorTypeStrings: Record<number | string, string> = {
|
|||||||
[ErrorCodes.WATCH_CLEANUP]: 'watcher cleanup function',
|
[ErrorCodes.WATCH_CLEANUP]: 'watcher cleanup function',
|
||||||
[ErrorCodes.NATIVE_EVENT_HANDLER]: 'native event handler',
|
[ErrorCodes.NATIVE_EVENT_HANDLER]: 'native event handler',
|
||||||
[ErrorCodes.COMPONENT_EVENT_HANDLER]: 'component event handler',
|
[ErrorCodes.COMPONENT_EVENT_HANDLER]: 'component event handler',
|
||||||
|
[ErrorCodes.VNODE_HOOK]: 'vnode hook',
|
||||||
[ErrorCodes.DIRECTIVE_HOOK]: 'directive hook',
|
[ErrorCodes.DIRECTIVE_HOOK]: 'directive hook',
|
||||||
[ErrorCodes.TRANSITION_HOOK]: 'transition hook',
|
[ErrorCodes.TRANSITION_HOOK]: 'transition hook',
|
||||||
[ErrorCodes.APP_ERROR_HANDLER]: 'app errorHandler',
|
[ErrorCodes.APP_ERROR_HANDLER]: 'app errorHandler',
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
import { VNode, normalizeVNode, Text, Comment, Static, Fragment } from './vnode'
|
import {
|
||||||
|
VNode,
|
||||||
|
normalizeVNode,
|
||||||
|
Text,
|
||||||
|
Comment,
|
||||||
|
Static,
|
||||||
|
Fragment,
|
||||||
|
VNodeHook
|
||||||
|
} from './vnode'
|
||||||
import { flushPostFlushCbs } from './scheduler'
|
import { flushPostFlushCbs } from './scheduler'
|
||||||
import { ComponentInternalInstance } from './component'
|
import { ComponentInternalInstance } from './component'
|
||||||
import { invokeDirectiveHook } from './directives'
|
import { invokeDirectiveHook } from './directives'
|
||||||
@ -10,7 +18,7 @@ import {
|
|||||||
isOn,
|
isOn,
|
||||||
isString
|
isString
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { RendererInternals } from './renderer'
|
import { RendererInternals, invokeVNodeHook } from './renderer'
|
||||||
import {
|
import {
|
||||||
SuspenseImpl,
|
SuspenseImpl,
|
||||||
SuspenseBoundary,
|
SuspenseBoundary,
|
||||||
@ -192,7 +200,7 @@ export function createHydrationFunctions(
|
|||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
optimized = optimized || vnode.dynamicChildren !== null
|
optimized = optimized || vnode.dynamicChildren !== null
|
||||||
const { props, patchFlag, shapeFlag } = vnode
|
const { props, patchFlag, shapeFlag, dirs } = vnode
|
||||||
// skip props & children if this is hoisted static nodes
|
// skip props & children if this is hoisted static nodes
|
||||||
if (patchFlag !== PatchFlags.HOISTED) {
|
if (patchFlag !== PatchFlags.HOISTED) {
|
||||||
// props
|
// props
|
||||||
@ -212,17 +220,24 @@ export function createHydrationFunctions(
|
|||||||
// iterating through props.
|
// iterating through props.
|
||||||
patchProp(el, 'onClick', null, props.onClick)
|
patchProp(el, 'onClick', null, props.onClick)
|
||||||
}
|
}
|
||||||
// vnode hooks
|
|
||||||
const { onVnodeBeforeMount, onVnodeMounted } = props
|
|
||||||
if (onVnodeBeforeMount != null) {
|
|
||||||
invokeDirectiveHook(onVnodeBeforeMount, parentComponent, vnode)
|
|
||||||
}
|
}
|
||||||
if (onVnodeMounted != null) {
|
// vnode / directive hooks
|
||||||
|
let vnodeHooks: VNodeHook | null | undefined
|
||||||
|
if ((vnodeHooks = props && props.onVnodeBeforeMount) != null) {
|
||||||
|
invokeVNodeHook(vnodeHooks, parentComponent, vnode)
|
||||||
|
}
|
||||||
|
if (dirs != null) {
|
||||||
|
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(vnodeHooks = props && props.onVnodeMounted) != null ||
|
||||||
|
dirs != null
|
||||||
|
) {
|
||||||
queueEffectWithSuspense(() => {
|
queueEffectWithSuspense(() => {
|
||||||
invokeDirectiveHook(onVnodeMounted, parentComponent, vnode)
|
vnodeHooks && invokeVNodeHook(vnodeHooks, parentComponent, vnode)
|
||||||
|
dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
|
||||||
}, parentSuspense)
|
}, parentSuspense)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// children
|
// children
|
||||||
if (
|
if (
|
||||||
shapeFlag & ShapeFlags.ARRAY_CHILDREN &&
|
shapeFlag & ShapeFlags.ARRAY_CHILDREN &&
|
||||||
|
@ -51,7 +51,6 @@ import {
|
|||||||
import { resolveProps } from './componentProps'
|
import { resolveProps } from './componentProps'
|
||||||
import { resolveSlots } from './componentSlots'
|
import { resolveSlots } from './componentSlots'
|
||||||
import { pushWarningContext, popWarningContext, warn } from './warning'
|
import { pushWarningContext, popWarningContext, warn } from './warning'
|
||||||
import { invokeDirectiveHook } from './directives'
|
|
||||||
import { ComponentPublicInstance } from './componentProxy'
|
import { ComponentPublicInstance } from './componentProxy'
|
||||||
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
|
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
|
||||||
import {
|
import {
|
||||||
@ -62,8 +61,13 @@ import {
|
|||||||
import { PortalImpl } from './components/Portal'
|
import { PortalImpl } from './components/Portal'
|
||||||
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
|
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
|
||||||
import { registerHMR, unregisterHMR } from './hmr'
|
import { registerHMR, unregisterHMR } from './hmr'
|
||||||
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
import {
|
||||||
|
ErrorCodes,
|
||||||
|
callWithErrorHandling,
|
||||||
|
callWithAsyncErrorHandling
|
||||||
|
} from './errorHandling'
|
||||||
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
||||||
|
import { invokeDirectiveHook } from './directives'
|
||||||
|
|
||||||
const __HMR__ = __BUNDLER__ && __DEV__
|
const __HMR__ = __BUNDLER__ && __DEV__
|
||||||
|
|
||||||
@ -526,7 +530,15 @@ function baseCreateRenderer<
|
|||||||
) => {
|
) => {
|
||||||
let el: HostElement
|
let el: HostElement
|
||||||
let vnodeHook: VNodeHook | undefined | null
|
let vnodeHook: VNodeHook | undefined | null
|
||||||
const { type, props, shapeFlag, transition, scopeId, patchFlag } = vnode
|
const {
|
||||||
|
type,
|
||||||
|
props,
|
||||||
|
shapeFlag,
|
||||||
|
transition,
|
||||||
|
scopeId,
|
||||||
|
patchFlag,
|
||||||
|
dirs
|
||||||
|
} = vnode
|
||||||
if (
|
if (
|
||||||
vnode.el !== null &&
|
vnode.el !== null &&
|
||||||
hostCloneNode !== undefined &&
|
hostCloneNode !== undefined &&
|
||||||
@ -546,9 +558,12 @@ function baseCreateRenderer<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((vnodeHook = props.onVnodeBeforeMount) != null) {
|
if ((vnodeHook = props.onVnodeBeforeMount) != null) {
|
||||||
invokeDirectiveHook(vnodeHook, parentComponent, vnode)
|
invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dirs != null) {
|
||||||
|
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
|
||||||
|
}
|
||||||
|
|
||||||
// scopeId
|
// scopeId
|
||||||
if (__BUNDLER__) {
|
if (__BUNDLER__) {
|
||||||
@ -585,11 +600,13 @@ function baseCreateRenderer<
|
|||||||
hostInsert(el, container, anchor)
|
hostInsert(el, container, anchor)
|
||||||
if (
|
if (
|
||||||
(vnodeHook = props && props.onVnodeMounted) != null ||
|
(vnodeHook = props && props.onVnodeMounted) != null ||
|
||||||
(transition != null && !transition.persisted)
|
(transition != null && !transition.persisted) ||
|
||||||
|
dirs != null
|
||||||
) {
|
) {
|
||||||
queuePostRenderEffect(() => {
|
queuePostRenderEffect(() => {
|
||||||
vnodeHook && invokeDirectiveHook(vnodeHook, parentComponent, vnode)
|
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
||||||
transition && !transition.persisted && transition.enter(el)
|
transition && !transition.persisted && transition.enter(el)
|
||||||
|
dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
|
||||||
}, parentSuspense)
|
}, parentSuspense)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,13 +647,16 @@ function baseCreateRenderer<
|
|||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
const el = (n2.el = n1.el) as HostElement
|
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 oldProps = (n1 && n1.props) || EMPTY_OBJ
|
||||||
const newProps = n2.props || EMPTY_OBJ
|
const newProps = n2.props || EMPTY_OBJ
|
||||||
let vnodeHook: VNodeHook | undefined | null
|
let vnodeHook: VNodeHook | undefined | null
|
||||||
|
|
||||||
if ((vnodeHook = newProps.onVnodeBeforeUpdate) != 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) {
|
if (__HMR__ && parentComponent && parentComponent.renderUpdated) {
|
||||||
@ -750,9 +770,10 @@ function baseCreateRenderer<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((vnodeHook = newProps.onVnodeUpdated) != null) {
|
if ((vnodeHook = newProps.onVnodeUpdated) != null || dirs != null) {
|
||||||
queuePostRenderEffect(() => {
|
queuePostRenderEffect(() => {
|
||||||
invokeDirectiveHook(vnodeHook!, parentComponent, n2, n1)
|
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
|
||||||
|
dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated')
|
||||||
}, parentSuspense)
|
}, parentSuspense)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1063,12 +1084,19 @@ function baseCreateRenderer<
|
|||||||
// create reactive effect for rendering
|
// create reactive effect for rendering
|
||||||
instance.update = effect(function componentEffect() {
|
instance.update = effect(function componentEffect() {
|
||||||
if (!instance.isMounted) {
|
if (!instance.isMounted) {
|
||||||
|
let vnodeHook: VNodeHook | null | undefined
|
||||||
|
const { el, props } = initialVNode
|
||||||
|
const { bm, m, a, parent } = instance
|
||||||
const subTree = (instance.subTree = renderComponentRoot(instance))
|
const subTree = (instance.subTree = renderComponentRoot(instance))
|
||||||
// beforeMount hook
|
// beforeMount hook
|
||||||
if (instance.bm !== null) {
|
if (bm !== null) {
|
||||||
invokeHooks(instance.bm)
|
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.
|
// vnode has adopted host node - perform hydration instead of mount.
|
||||||
hydrateNode(
|
hydrateNode(
|
||||||
initialVNode.el as Node,
|
initialVNode.el as Node,
|
||||||
@ -1089,36 +1117,50 @@ function baseCreateRenderer<
|
|||||||
initialVNode.el = subTree.el
|
initialVNode.el = subTree.el
|
||||||
}
|
}
|
||||||
// mounted hook
|
// mounted hook
|
||||||
if (instance.m !== null) {
|
if (m !== null) {
|
||||||
queuePostRenderEffect(instance.m, parentSuspense)
|
queuePostRenderEffect(m, parentSuspense)
|
||||||
|
}
|
||||||
|
// onVnodeMounted
|
||||||
|
if ((vnodeHook = props && props.onVnodeMounted) != null) {
|
||||||
|
queuePostRenderEffect(() => {
|
||||||
|
invokeVNodeHook(vnodeHook!, parent, initialVNode)
|
||||||
|
}, parentSuspense)
|
||||||
}
|
}
|
||||||
// activated hook for keep-alive roots.
|
// activated hook for keep-alive roots.
|
||||||
if (
|
if (
|
||||||
instance.a !== null &&
|
a !== null &&
|
||||||
instance.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
||||||
) {
|
) {
|
||||||
queuePostRenderEffect(instance.a, parentSuspense)
|
queuePostRenderEffect(a, parentSuspense)
|
||||||
}
|
}
|
||||||
instance.isMounted = true
|
instance.isMounted = true
|
||||||
} else {
|
} else {
|
||||||
// updateComponent
|
// updateComponent
|
||||||
// This is triggered by mutation of component's own state (next: null)
|
// This is triggered by mutation of component's own state (next: null)
|
||||||
// OR parent calling processComponent (next: HostVNode)
|
// OR parent calling processComponent (next: HostVNode)
|
||||||
const { next } = instance
|
let { next, bu, u, parent, vnode } = instance
|
||||||
|
let vnodeHook: VNodeHook | null | undefined
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
pushWarningContext(next || instance.vnode)
|
pushWarningContext(next || instance.vnode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next !== null) {
|
if (next !== null) {
|
||||||
updateComponentPreRender(instance, next)
|
updateComponentPreRender(instance, next)
|
||||||
|
} else {
|
||||||
|
next = vnode
|
||||||
}
|
}
|
||||||
const nextTree = renderComponentRoot(instance)
|
const nextTree = renderComponentRoot(instance)
|
||||||
const prevTree = instance.subTree
|
const prevTree = instance.subTree
|
||||||
instance.subTree = nextTree
|
instance.subTree = nextTree
|
||||||
// beforeUpdate hook
|
// beforeUpdate hook
|
||||||
if (instance.bu !== null) {
|
if (bu !== null) {
|
||||||
invokeHooks(instance.bu)
|
invokeHooks(bu)
|
||||||
|
}
|
||||||
|
// onVnodeBeforeUpdate
|
||||||
|
if (
|
||||||
|
(vnodeHook = next.props && next.props.onVnodeBeforeUpdate) != null
|
||||||
|
) {
|
||||||
|
invokeVNodeHook(vnodeHook, parent, next, vnode)
|
||||||
}
|
}
|
||||||
// reset refs
|
// reset refs
|
||||||
// only needed if previous patch had refs
|
// only needed if previous patch had refs
|
||||||
@ -1136,7 +1178,7 @@ function baseCreateRenderer<
|
|||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
isSVG
|
||||||
)
|
)
|
||||||
instance.vnode.el = nextTree.el
|
next.el = nextTree.el
|
||||||
if (next === null) {
|
if (next === null) {
|
||||||
// self-triggered update. In case of HOC, update parent component
|
// self-triggered update. In case of HOC, update parent component
|
||||||
// vnode el. HOC is indicated by parent instance's subTree pointing
|
// vnode el. HOC is indicated by parent instance's subTree pointing
|
||||||
@ -1144,10 +1186,15 @@ function baseCreateRenderer<
|
|||||||
updateHOCHostEl(instance, nextTree.el)
|
updateHOCHostEl(instance, nextTree.el)
|
||||||
}
|
}
|
||||||
// updated hook
|
// updated hook
|
||||||
if (instance.u !== null) {
|
if (u !== null) {
|
||||||
queuePostRenderEffect(instance.u, parentSuspense)
|
queuePostRenderEffect(u, parentSuspense)
|
||||||
|
}
|
||||||
|
// onVnodeUpdated
|
||||||
|
if ((vnodeHook = next.props && next.props.onVnodeUpdated) != null) {
|
||||||
|
queuePostRenderEffect(() => {
|
||||||
|
invokeVNodeHook(vnodeHook!, parent, next!, vnode)
|
||||||
|
}, parentSuspense)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
popWarningContext()
|
popWarningContext()
|
||||||
}
|
}
|
||||||
@ -1617,7 +1664,8 @@ function baseCreateRenderer<
|
|||||||
parentSuspense,
|
parentSuspense,
|
||||||
doRemove = false
|
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
|
let vnodeHook: VNodeHook | undefined | null
|
||||||
|
|
||||||
// unset ref
|
// unset ref
|
||||||
@ -1640,7 +1688,10 @@ function baseCreateRenderer<
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
|
if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
|
||||||
invokeDirectiveHook(vnodeHook, parentComponent, vnode)
|
invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
||||||
|
}
|
||||||
|
if (shouldInvokeDirs) {
|
||||||
|
invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dynamicChildren != null) {
|
if (dynamicChildren != null) {
|
||||||
@ -1654,9 +1705,14 @@ function baseCreateRenderer<
|
|||||||
remove(vnode)
|
remove(vnode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((vnodeHook = props && props.onVnodeUnmounted) != null) {
|
if (
|
||||||
|
(vnodeHook = props && props.onVnodeUnmounted) != null ||
|
||||||
|
shouldInvokeDirs
|
||||||
|
) {
|
||||||
queuePostRenderEffect(() => {
|
queuePostRenderEffect(() => {
|
||||||
invokeDirectiveHook(vnodeHook!, parentComponent, vnode)
|
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
||||||
|
shouldInvokeDirs &&
|
||||||
|
invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')
|
||||||
}, parentSuspense)
|
}, 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
|
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
|
||||||
function getSequence(arr: number[]): number[] {
|
function getSequence(arr: number[]): number[] {
|
||||||
const p = arr.slice()
|
const p = arr.slice()
|
||||||
|
@ -61,7 +61,11 @@ export type VNodeNormalizedRef = [ComponentInternalInstance, VNodeRef]
|
|||||||
|
|
||||||
type VNodeMountHook = (vnode: VNode) => void
|
type VNodeMountHook = (vnode: VNode) => void
|
||||||
type VNodeUpdateHook = (vnode: VNode, oldVNode: VNode) => void
|
type VNodeUpdateHook = (vnode: VNode, oldVNode: VNode) => void
|
||||||
export type VNodeHook = VNodeMountHook | VNodeUpdateHook
|
export type VNodeHook =
|
||||||
|
| VNodeMountHook
|
||||||
|
| VNodeUpdateHook
|
||||||
|
| VNodeMountHook[]
|
||||||
|
| VNodeUpdateHook[]
|
||||||
|
|
||||||
export interface VNodeProps {
|
export interface VNodeProps {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
@ -69,12 +73,12 @@ export interface VNodeProps {
|
|||||||
ref?: VNodeRef
|
ref?: VNodeRef
|
||||||
|
|
||||||
// vnode hooks
|
// vnode hooks
|
||||||
onVnodeBeforeMount?: VNodeMountHook
|
onVnodeBeforeMount?: VNodeMountHook | VNodeMountHook[]
|
||||||
onVnodeMounted?: VNodeMountHook
|
onVnodeMounted?: VNodeMountHook | VNodeMountHook[]
|
||||||
onVnodeBeforeUpdate?: VNodeUpdateHook
|
onVnodeBeforeUpdate?: VNodeUpdateHook | VNodeUpdateHook[]
|
||||||
onVnodeUpdated?: VNodeUpdateHook
|
onVnodeUpdated?: VNodeUpdateHook | VNodeUpdateHook[]
|
||||||
onVnodeBeforeUnmount?: VNodeMountHook
|
onVnodeBeforeUnmount?: VNodeMountHook | VNodeMountHook[]
|
||||||
onVnodeUnmounted?: VNodeMountHook
|
onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type VNodeChildAtom<HostNode, HostElement> =
|
type VNodeChildAtom<HostNode, HostElement> =
|
||||||
|
Loading…
Reference in New Issue
Block a user