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-09-03 22:11:04 +00:00
|
|
|
return applyDirectives(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
|
|
|
*/
|
|
|
|
|
|
|
|
import { VNode, cloneVNode } from './vnode'
|
2019-10-18 16:34:45 +00:00
|
|
|
import { extend, isArray, isFunction, EMPTY_OBJ, makeMap } 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-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-08-31 20:36:36 +00:00
|
|
|
type DirectiveModifiers = Record<string, boolean>
|
|
|
|
|
|
|
|
const valueCache = new WeakMap<Directive, WeakMap<any, any>>()
|
|
|
|
|
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-08-31 20:36:36 +00:00
|
|
|
function applyDirective(
|
|
|
|
props: Record<any, any>,
|
2019-09-06 16:58:31 +00:00
|
|
|
instance: ComponentInternalInstance,
|
2019-08-31 20:36:36 +00:00
|
|
|
directive: Directive,
|
|
|
|
value?: any,
|
|
|
|
arg?: string,
|
2019-10-11 21:55:20 +00:00
|
|
|
modifiers: DirectiveModifiers = EMPTY_OBJ
|
2019-08-31 20:36:36 +00:00
|
|
|
) {
|
2019-10-05 14:09:34 +00:00
|
|
|
let valueCacheForDir = valueCache.get(directive)!
|
2019-08-31 20:36:36 +00:00
|
|
|
if (!valueCacheForDir) {
|
|
|
|
valueCacheForDir = new WeakMap<VNode, any>()
|
|
|
|
valueCache.set(directive, valueCacheForDir)
|
|
|
|
}
|
2019-10-16 06:12:26 +00:00
|
|
|
|
|
|
|
if (isFunction(directive)) {
|
|
|
|
directive = {
|
|
|
|
mounted: directive,
|
|
|
|
updated: directive
|
|
|
|
} as ObjectDirective
|
|
|
|
}
|
|
|
|
|
2019-08-31 20:36:36 +00:00
|
|
|
for (const key in directive) {
|
2019-10-16 06:12:26 +00:00
|
|
|
const hook = directive[key as keyof ObjectDirective]!
|
2019-08-31 20:36:36 +00:00
|
|
|
const hookKey = `vnode` + key[0].toUpperCase() + key.slice(1)
|
2019-09-02 16:09:29 +00:00
|
|
|
const vnodeHook = (vnode: VNode, prevVNode: VNode | null) => {
|
2019-08-31 20:36:36 +00:00
|
|
|
let oldValue
|
2019-09-02 16:09:29 +00:00
|
|
|
if (prevVNode != null) {
|
2019-08-31 20:36:36 +00:00
|
|
|
oldValue = valueCacheForDir.get(prevVNode)
|
|
|
|
valueCacheForDir.delete(prevVNode)
|
|
|
|
}
|
|
|
|
valueCacheForDir.set(vnode, value)
|
|
|
|
hook(
|
|
|
|
vnode.el,
|
|
|
|
{
|
|
|
|
instance: instance.renderProxy,
|
|
|
|
value,
|
|
|
|
oldValue,
|
|
|
|
arg,
|
|
|
|
modifiers
|
|
|
|
},
|
|
|
|
vnode,
|
|
|
|
prevVNode
|
|
|
|
)
|
|
|
|
}
|
|
|
|
const existing = props[hookKey]
|
|
|
|
props[hookKey] = existing
|
2019-10-05 14:09:34 +00:00
|
|
|
? [].concat(existing, vnodeHook as any)
|
2019-08-31 20:36:36 +00:00
|
|
|
: vnodeHook
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-09-03 22:11:04 +00:00
|
|
|
export function applyDirectives(vnode: VNode, directives: DirectiveArguments) {
|
2019-08-31 20:36:36 +00:00
|
|
|
const instance = currentRenderingInstance
|
|
|
|
if (instance !== null) {
|
|
|
|
vnode = cloneVNode(vnode)
|
|
|
|
vnode.props = vnode.props != null ? extend({}, vnode.props) : {}
|
|
|
|
for (let i = 0; i < directives.length; i++) {
|
2019-10-08 16:43:13 +00:00
|
|
|
const [dir, value, arg, modifiers] = directives[i]
|
|
|
|
applyDirective(vnode.props, instance, dir, value, arg, modifiers)
|
2019-08-31 20:36:36 +00:00
|
|
|
}
|
|
|
|
} else if (__DEV__) {
|
|
|
|
warn(`applyDirectives can only be used inside render functions.`)
|
|
|
|
}
|
|
|
|
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-09-02 16:09:29 +00:00
|
|
|
const args = [vnode, prevVNode]
|
2019-08-31 21:06:39 +00:00
|
|
|
if (isArray(hook)) {
|
|
|
|
for (let i = 0; i < hook.length; i++) {
|
|
|
|
callWithAsyncErrorHandling(
|
|
|
|
hook[i],
|
|
|
|
instance,
|
2019-09-06 16:58:31 +00:00
|
|
|
ErrorCodes.DIRECTIVE_HOOK,
|
2019-08-31 21:06:39 +00:00
|
|
|
args
|
|
|
|
)
|
|
|
|
}
|
|
|
|
} else if (isFunction(hook)) {
|
2019-09-06 16:58:31 +00:00
|
|
|
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, args)
|
2019-08-31 21:06:39 +00:00
|
|
|
}
|
|
|
|
}
|