2019-08-31 20:36:36 +00:00
|
|
|
/**
|
|
|
|
Runtime helper for applying directives to a vnode. Example usage:
|
|
|
|
|
|
|
|
const comp = resolveComponent('comp')
|
|
|
|
const foo = resolveDirective('foo')
|
|
|
|
const bar = resolveDirective('bar')
|
|
|
|
|
2019-10-21 14:00:23 +00:00
|
|
|
return withDirectives(h(comp), [
|
2019-08-31 20:36:36 +00:00
|
|
|
[foo, this.x],
|
|
|
|
[bar, this.y]
|
2019-09-03 22:11:04 +00:00
|
|
|
])
|
2019-08-31 20:36:36 +00:00
|
|
|
*/
|
|
|
|
|
2019-10-18 20:35:01 +00:00
|
|
|
import { VNode } from './vnode'
|
2019-10-26 20:00:07 +00:00
|
|
|
import { isFunction, EMPTY_OBJ, makeMap, EMPTY_ARR } from '@vue/shared'
|
2019-08-31 20:36:36 +00:00
|
|
|
import { warn } from './warning'
|
2019-09-06 16:58:31 +00:00
|
|
|
import { ComponentInternalInstance } from './component'
|
2019-09-06 15:25:11 +00:00
|
|
|
import { currentRenderingInstance } from './componentRenderUtils'
|
2019-09-06 16:58:31 +00:00
|
|
|
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
|
2019-10-02 14:03:43 +00:00
|
|
|
import { ComponentPublicInstance } from './componentProxy'
|
2019-08-31 20:36:36 +00:00
|
|
|
|
2019-09-02 16:09:29 +00:00
|
|
|
export interface DirectiveBinding {
|
2019-09-06 16:58:31 +00:00
|
|
|
instance: ComponentPublicInstance | null
|
2019-10-11 21:55:20 +00:00
|
|
|
value: any
|
|
|
|
oldValue: any
|
2019-08-31 20:36:36 +00:00
|
|
|
arg?: string
|
2019-10-11 21:55:20 +00:00
|
|
|
modifiers: DirectiveModifiers
|
2019-10-26 20:00:07 +00:00
|
|
|
dir: ObjectDirective
|
2019-08-31 20:36:36 +00:00
|
|
|
}
|
|
|
|
|
2019-10-11 21:55:20 +00:00
|
|
|
export type DirectiveHook<T = any> = (
|
|
|
|
el: T,
|
2019-08-31 20:36:36 +00:00
|
|
|
binding: DirectiveBinding,
|
2019-10-11 21:55:20 +00:00
|
|
|
vnode: VNode<any, T>,
|
2019-10-14 02:40:29 +00:00
|
|
|
prevVNode: VNode<any, T> | null
|
2019-08-31 20:36:36 +00:00
|
|
|
) => void
|
|
|
|
|
2019-10-16 06:12:26 +00:00
|
|
|
export interface ObjectDirective<T = any> {
|
2019-10-11 21:55:20 +00:00
|
|
|
beforeMount?: DirectiveHook<T>
|
|
|
|
mounted?: DirectiveHook<T>
|
|
|
|
beforeUpdate?: DirectiveHook<T>
|
|
|
|
updated?: DirectiveHook<T>
|
|
|
|
beforeUnmount?: DirectiveHook<T>
|
|
|
|
unmounted?: DirectiveHook<T>
|
2019-08-31 20:36:36 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 06:12:26 +00:00
|
|
|
export type FunctionDirective<T = any> = DirectiveHook<T>
|
|
|
|
|
|
|
|
export type Directive<T = any> = ObjectDirective<T> | FunctionDirective<T>
|
|
|
|
|
2019-10-26 20:00:07 +00:00
|
|
|
export type DirectiveModifiers = Record<string, boolean>
|
2019-08-31 20:36:36 +00:00
|
|
|
|
2019-10-26 20:00:07 +00:00
|
|
|
export type VNodeDirectiveData = [
|
|
|
|
unknown,
|
|
|
|
string | undefined,
|
|
|
|
DirectiveModifiers
|
|
|
|
]
|
2019-08-31 20:36:36 +00:00
|
|
|
|
2019-10-18 16:34:45 +00:00
|
|
|
const isBuiltInDirective = /*#__PURE__*/ makeMap(
|
|
|
|
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text'
|
|
|
|
)
|
|
|
|
|
|
|
|
export function validateDirectiveName(name: string) {
|
|
|
|
if (isBuiltInDirective(name)) {
|
|
|
|
warn('Do not use built-in directive ids as custom directive id: ' + name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-26 20:00:07 +00:00
|
|
|
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]
|
|
|
|
if (hook != null) {
|
|
|
|
if (prevVnode != null) {
|
|
|
|
binding.oldValue = prevBindings[i].value
|
|
|
|
}
|
|
|
|
hook(vnode.el, binding, vnode, prevVnode)
|
|
|
|
}
|
2019-08-31 20:36:36 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-26 20:00:07 +00:00
|
|
|
map[key] = [vnodeKey, vnodeHook]
|
|
|
|
return map
|
|
|
|
},
|
|
|
|
{} as Record<string, [string, Function]>
|
|
|
|
)
|
2019-08-31 20:36:36 +00:00
|
|
|
|
2019-09-02 16:09:29 +00:00
|
|
|
// Directive, value, argument, modifiers
|
2019-09-06 16:58:31 +00:00
|
|
|
export type DirectiveArguments = Array<
|
2019-09-02 16:09:29 +00:00
|
|
|
| [Directive]
|
|
|
|
| [Directive, any]
|
|
|
|
| [Directive, any, string]
|
|
|
|
| [Directive, any, string, DirectiveModifiers]
|
|
|
|
>
|
2019-08-31 20:36:36 +00:00
|
|
|
|
2019-10-18 20:35:01 +00:00
|
|
|
export function withDirectives(vnode: VNode, directives: DirectiveArguments) {
|
2019-10-26 20:00:07 +00:00
|
|
|
const internalInstance = currentRenderingInstance
|
|
|
|
if (internalInstance === null) {
|
|
|
|
__DEV__ && warn(`withDirectives can only be used inside render functions.`)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const instance = internalInstance.renderProxy
|
|
|
|
const props = vnode.props || (vnode.props = {})
|
|
|
|
const bindings = vnode.dirs || (vnode.dirs = new Array(directives.length))
|
|
|
|
const injected: Record<string, true> = {}
|
|
|
|
for (let i = 0; i < directives.length; i++) {
|
|
|
|
let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]
|
|
|
|
if (isFunction(dir)) {
|
|
|
|
dir = {
|
|
|
|
mounted: dir,
|
|
|
|
updated: dir
|
|
|
|
} as ObjectDirective
|
|
|
|
}
|
|
|
|
bindings[i] = {
|
|
|
|
dir,
|
|
|
|
instance,
|
|
|
|
value,
|
|
|
|
oldValue: void 0,
|
|
|
|
arg,
|
|
|
|
modifiers
|
|
|
|
}
|
|
|
|
// inject onVnodeXXX hooks
|
|
|
|
for (const key in dir) {
|
|
|
|
if (!injected[key]) {
|
|
|
|
const { 0: hookName, 1: hook } = directiveToVnodeHooksMap[key]
|
|
|
|
const existing = props[hookName]
|
|
|
|
props[hookName] = existing ? [].concat(existing, hook as any) : hook
|
|
|
|
injected[key] = true
|
|
|
|
}
|
2019-08-31 20:36:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return vnode
|
|
|
|
}
|
|
|
|
|
2019-08-31 21:06:39 +00:00
|
|
|
export function invokeDirectiveHook(
|
|
|
|
hook: Function | Function[],
|
2019-09-06 16:58:31 +00:00
|
|
|
instance: ComponentInternalInstance | null,
|
2019-09-02 16:09:29 +00:00
|
|
|
vnode: VNode,
|
|
|
|
prevVNode: VNode | null = null
|
2019-08-31 21:06:39 +00:00
|
|
|
) {
|
2019-10-21 17:59:10 +00:00
|
|
|
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
|
|
|
|
vnode,
|
|
|
|
prevVNode
|
|
|
|
])
|
2019-08-31 21:06:39 +00:00
|
|
|
}
|