-import { EMPTY_OBJ, nativeOnRE } from './utils'
+import { EMPTY_OBJ, nativeOnRE, vnodeHookRE } from './utils'
import {
Component,
ComponentClass,
if (key === 'key' || key === 'ref' || key === 'slots') {
continue
}
- // class, style & nativeOn are always extracted into a separate `attrs`
- // object, which can then be merged onto child component root.
- // in addition, if the component has explicitly declared props, then
+ // class, style, nativeOn & directive hooks are always extracted into a
+ // separate `attrs` object, which can then be merged onto child component
+ // root. in addition, if the component has explicitly declared props, then
// any non-matching props are extracted into `attrs` as well.
let isNativeOn
if (
key === 'class' ||
key === 'style' ||
+ vnodeHookRE.test(key) ||
(isNativeOn = nativeOnRE.test(key)) ||
(hasDeclaredProps && !options.hasOwnProperty(key))
) {
}
}
- // lifecycle hooks -----------------------------------------------------------
+ // lifecycle lifecycleHooks -----------------------------------------------------------
- const hooks: Function[] = []
+ const lifecycleHooks: Function[] = []
+ const vnodeUpdatedHooks: Function[] = []
function flushHooks() {
let fn
- while ((fn = hooks.shift())) {
+ while ((fn = lifecycleHooks.shift())) {
fn()
}
}
for (const key in data) {
patchData(el, key, null, data[key], null, vnode, isSVG)
}
+ if (data.vnodeBeforeMount) {
+ data.vnodeBeforeMount(vnode)
+ }
}
if (childFlags !== ChildrenFlags.NO_CHILDREN) {
const hasSVGChildren = isSVG && tag !== 'foreignObject'
if (ref) {
mountRef(ref, el)
}
+ if (data != null && data.vnodeMounted) {
+ lifecycleHooks.push(() => {
+ data.vnodeMounted(vnode)
+ })
+ }
return el
}
function mountRef(ref: Ref, el: RenderNode | MountedComponent) {
- hooks.push(() => {
+ lifecycleHooks.push(() => {
ref(el)
})
}
}
const el = (nextVNode.el = prevVNode.el) as RenderNode
-
- // data
const prevData = prevVNode.data
const nextData = nextVNode.data
+
+ if (nextData != null && nextData.vnodeBeforeUpdate) {
+ nextData.vnodeBeforeUpdate(nextVNode, prevVNode)
+ }
+
+ // patch data
if (prevData !== nextData) {
const prevDataOrEmpty = prevData || EMPTY_OBJ
const nextDataOrEmpty = nextData || EMPTY_OBJ
isSVG && nextVNode.tag !== 'foreignObject',
null
)
+
+ if (nextData != null && nextData.vnodeUpdated) {
+ vnodeUpdatedHooks.push(() => {
+ nextData.vnodeUpdated(nextVNode, prevVNode)
+ })
+ }
}
function patchComponent(
// unmounting ----------------------------------------------------------------
function unmount(vnode: VNode) {
- const { flags, children, childFlags, ref } = vnode
- if (flags & VNodeFlags.ELEMENT || flags & VNodeFlags.FRAGMENT) {
+ const { flags, data, children, childFlags, ref } = vnode
+ const isElement = flags & VNodeFlags.ELEMENT
+ if (isElement || flags & VNodeFlags.FRAGMENT) {
+ if (isElement && data != null && data.vnodeBeforeUnmount) {
+ data.vnodeBeforeUnmount(vnode)
+ }
unmountChildren(children as VNodeChildren, childFlags)
if (teardownVNode !== void 0) {
teardownVNode(vnode)
}
+ if (isElement && data != null && data.vnodeUnmounted) {
+ data.vnodeUnmounted(vnode)
+ }
} else if (flags & VNodeFlags.COMPONENT) {
if (flags & VNodeFlags.COMPONENT_STATEFUL) {
unmountComponentInstance(children as MountedComponent)
mountRef(ref, instance)
}
if (instance.mounted) {
- hooks.push(() => {
+ lifecycleHooks.push(() => {
;(instance as any).mounted.call(instance.$proxy)
})
}
// will be added to the queue AFTER the parent's, but they should be
// invoked BEFORE the parent's. Therefore we add them to the head of the
// queue instead.
- hooks.unshift(() => {
+ lifecycleHooks.unshift(() => {
;(instance as any).updated.call(instance.$proxy, nextVNode)
})
}
+
+ if (vnodeUpdatedHooks.length > 0) {
+ const vnodeUpdatedHooksForCurrentInstance = vnodeUpdatedHooks.slice()
+ vnodeUpdatedHooks.length = 0
+ lifecycleHooks.unshift(() => {
+ for (let i = 0; i < vnodeUpdatedHooksForCurrentInstance.length; i++) {
+ vnodeUpdatedHooksForCurrentInstance[i]()
+ }
+ })
+ }
}
function unmountComponentInstance(instance: MountedComponent) {
} from './component'
import { VNodeFlags, ChildrenFlags } from './flags'
import { createComponentClassFromOptions } from './componentUtils'
-import { normalizeClass, normalizeStyle, onRE, nativeOnRE } from './utils'
+import { normalizeClass, normalizeStyle, handlersRE } from './utils'
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
export interface RenderNode {
clonedData.class = normalizeClass([clonedData.class, extraData.class])
} else if (key === 'style') {
clonedData.style = normalizeStyle([clonedData.style, extraData.style])
- } else if (onRE.test(key) || nativeOnRE.test(key)) {
+ } else if (handlersRE.test(key)) {
+ // on*, nativeOn*, vnode*
const existing = clonedData[key]
clonedData[key] = existing
? [].concat(existing, extraData[key])