feat: vnode hooks
This commit is contained in:
parent
8cafad1fd3
commit
1106e2208d
@ -1,4 +1,4 @@
|
|||||||
import { EMPTY_OBJ, nativeOnRE } from './utils'
|
import { EMPTY_OBJ, nativeOnRE, vnodeHookRE } from './utils'
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ComponentClass,
|
ComponentClass,
|
||||||
@ -92,14 +92,15 @@ export function resolveProps(
|
|||||||
if (key === 'key' || key === 'ref' || key === 'slots') {
|
if (key === 'key' || key === 'ref' || key === 'slots') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// class, style & nativeOn are always extracted into a separate `attrs`
|
// class, style, nativeOn & directive hooks are always extracted into a
|
||||||
// object, which can then be merged onto child component root.
|
// separate `attrs` object, which can then be merged onto child component
|
||||||
// in addition, if the component has explicitly declared props, then
|
// root. in addition, if the component has explicitly declared props, then
|
||||||
// any non-matching props are extracted into `attrs` as well.
|
// any non-matching props are extracted into `attrs` as well.
|
||||||
let isNativeOn
|
let isNativeOn
|
||||||
if (
|
if (
|
||||||
key === 'class' ||
|
key === 'class' ||
|
||||||
key === 'style' ||
|
key === 'style' ||
|
||||||
|
vnodeHookRE.test(key) ||
|
||||||
(isNativeOn = nativeOnRE.test(key)) ||
|
(isNativeOn = nativeOnRE.test(key)) ||
|
||||||
(hasDeclaredProps && !options.hasOwnProperty(key))
|
(hasDeclaredProps && !options.hasOwnProperty(key))
|
||||||
) {
|
) {
|
||||||
|
@ -156,13 +156,14 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lifecycle hooks -----------------------------------------------------------
|
// lifecycle lifecycleHooks -----------------------------------------------------------
|
||||||
|
|
||||||
const hooks: Function[] = []
|
const lifecycleHooks: Function[] = []
|
||||||
|
const vnodeUpdatedHooks: Function[] = []
|
||||||
|
|
||||||
function flushHooks() {
|
function flushHooks() {
|
||||||
let fn
|
let fn
|
||||||
while ((fn = hooks.shift())) {
|
while ((fn = lifecycleHooks.shift())) {
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,6 +223,9 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
patchData(el, key, null, data[key], null, vnode, isSVG)
|
patchData(el, key, null, data[key], null, vnode, isSVG)
|
||||||
}
|
}
|
||||||
|
if (data.vnodeBeforeMount) {
|
||||||
|
data.vnodeBeforeMount(vnode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (childFlags !== ChildrenFlags.NO_CHILDREN) {
|
if (childFlags !== ChildrenFlags.NO_CHILDREN) {
|
||||||
const hasSVGChildren = isSVG && tag !== 'foreignObject'
|
const hasSVGChildren = isSVG && tag !== 'foreignObject'
|
||||||
@ -243,11 +247,16 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (ref) {
|
if (ref) {
|
||||||
mountRef(ref, el)
|
mountRef(ref, el)
|
||||||
}
|
}
|
||||||
|
if (data != null && data.vnodeMounted) {
|
||||||
|
lifecycleHooks.push(() => {
|
||||||
|
data.vnodeMounted(vnode)
|
||||||
|
})
|
||||||
|
}
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
function mountRef(ref: Ref, el: RenderNode | MountedComponent) {
|
function mountRef(ref: Ref, el: RenderNode | MountedComponent) {
|
||||||
hooks.push(() => {
|
lifecycleHooks.push(() => {
|
||||||
ref(el)
|
ref(el)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -438,10 +447,14 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const el = (nextVNode.el = prevVNode.el) as RenderNode
|
const el = (nextVNode.el = prevVNode.el) as RenderNode
|
||||||
|
|
||||||
// data
|
|
||||||
const prevData = prevVNode.data
|
const prevData = prevVNode.data
|
||||||
const nextData = nextVNode.data
|
const nextData = nextVNode.data
|
||||||
|
|
||||||
|
if (nextData != null && nextData.vnodeBeforeUpdate) {
|
||||||
|
nextData.vnodeBeforeUpdate(nextVNode, prevVNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch data
|
||||||
if (prevData !== nextData) {
|
if (prevData !== nextData) {
|
||||||
const prevDataOrEmpty = prevData || EMPTY_OBJ
|
const prevDataOrEmpty = prevData || EMPTY_OBJ
|
||||||
const nextDataOrEmpty = nextData || EMPTY_OBJ
|
const nextDataOrEmpty = nextData || EMPTY_OBJ
|
||||||
@ -483,6 +496,12 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
isSVG && nextVNode.tag !== 'foreignObject',
|
isSVG && nextVNode.tag !== 'foreignObject',
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (nextData != null && nextData.vnodeUpdated) {
|
||||||
|
vnodeUpdatedHooks.push(() => {
|
||||||
|
nextData.vnodeUpdated(nextVNode, prevVNode)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchComponent(
|
function patchComponent(
|
||||||
@ -1064,12 +1083,19 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
// unmounting ----------------------------------------------------------------
|
// unmounting ----------------------------------------------------------------
|
||||||
|
|
||||||
function unmount(vnode: VNode) {
|
function unmount(vnode: VNode) {
|
||||||
const { flags, children, childFlags, ref } = vnode
|
const { flags, data, children, childFlags, ref } = vnode
|
||||||
if (flags & VNodeFlags.ELEMENT || flags & VNodeFlags.FRAGMENT) {
|
const isElement = flags & VNodeFlags.ELEMENT
|
||||||
|
if (isElement || flags & VNodeFlags.FRAGMENT) {
|
||||||
|
if (isElement && data != null && data.vnodeBeforeUnmount) {
|
||||||
|
data.vnodeBeforeUnmount(vnode)
|
||||||
|
}
|
||||||
unmountChildren(children as VNodeChildren, childFlags)
|
unmountChildren(children as VNodeChildren, childFlags)
|
||||||
if (teardownVNode !== void 0) {
|
if (teardownVNode !== void 0) {
|
||||||
teardownVNode(vnode)
|
teardownVNode(vnode)
|
||||||
}
|
}
|
||||||
|
if (isElement && data != null && data.vnodeUnmounted) {
|
||||||
|
data.vnodeUnmounted(vnode)
|
||||||
|
}
|
||||||
} else if (flags & VNodeFlags.COMPONENT) {
|
} else if (flags & VNodeFlags.COMPONENT) {
|
||||||
if (flags & VNodeFlags.COMPONENT_STATEFUL) {
|
if (flags & VNodeFlags.COMPONENT_STATEFUL) {
|
||||||
unmountComponentInstance(children as MountedComponent)
|
unmountComponentInstance(children as MountedComponent)
|
||||||
@ -1187,7 +1213,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
mountRef(ref, instance)
|
mountRef(ref, instance)
|
||||||
}
|
}
|
||||||
if (instance.mounted) {
|
if (instance.mounted) {
|
||||||
hooks.push(() => {
|
lifecycleHooks.push(() => {
|
||||||
;(instance as any).mounted.call(instance.$proxy)
|
;(instance as any).mounted.call(instance.$proxy)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1227,10 +1253,20 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
// will be added to the queue AFTER the parent's, but they should be
|
// 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
|
// invoked BEFORE the parent's. Therefore we add them to the head of the
|
||||||
// queue instead.
|
// queue instead.
|
||||||
hooks.unshift(() => {
|
lifecycleHooks.unshift(() => {
|
||||||
;(instance as any).updated.call(instance.$proxy, nextVNode)
|
;(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) {
|
function unmountComponentInstance(instance: MountedComponent) {
|
||||||
|
@ -4,7 +4,9 @@ export const NOOP = () => {}
|
|||||||
|
|
||||||
export const onRE = /^on/
|
export const onRE = /^on/
|
||||||
export const nativeOnRE = /^nativeOn/
|
export const nativeOnRE = /^nativeOn/
|
||||||
export const reservedPropRE = /^(?:key|ref|slots)$|^nativeOn/
|
export const vnodeHookRE = /^vnode/
|
||||||
|
export const handlersRE = /^on|^nativeOn|^vnode/
|
||||||
|
export const reservedPropRE = /^(?:key|ref|slots)$|^nativeOn|^vnode/
|
||||||
|
|
||||||
export function normalizeStyle(
|
export function normalizeStyle(
|
||||||
value: any
|
value: any
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from './component'
|
} from './component'
|
||||||
import { VNodeFlags, ChildrenFlags } from './flags'
|
import { VNodeFlags, ChildrenFlags } from './flags'
|
||||||
import { createComponentClassFromOptions } from './componentUtils'
|
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.
|
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
|
||||||
export interface RenderNode {
|
export interface RenderNode {
|
||||||
@ -270,7 +270,8 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
|
|||||||
clonedData.class = normalizeClass([clonedData.class, extraData.class])
|
clonedData.class = normalizeClass([clonedData.class, extraData.class])
|
||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
clonedData.style = normalizeStyle([clonedData.style, extraData.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]
|
const existing = clonedData[key]
|
||||||
clonedData[key] = existing
|
clonedData[key] = existing
|
||||||
? [].concat(existing, extraData[key])
|
? [].concat(existing, extraData[key])
|
||||||
|
Loading…
Reference in New Issue
Block a user