2019-09-01 04:36:36 +08: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 22:00:23 +08:00
|
|
|
return withDirectives(h(comp), [
|
2019-09-01 04:36:36 +08:00
|
|
|
[foo, this.x],
|
|
|
|
[bar, this.y]
|
2019-09-04 06:11:04 +08:00
|
|
|
])
|
2019-09-01 04:36:36 +08:00
|
|
|
*/
|
|
|
|
|
2020-03-18 23:43:32 +08:00
|
|
|
import type { VNode } from './vnode'
|
2022-02-04 08:58:28 +08:00
|
|
|
import { EMPTY_OBJ, isBuiltInDirective, isFunction } from '@vue/shared'
|
2019-09-01 04:36:36 +08:00
|
|
|
import { warn } from './warning'
|
2022-04-12 15:54:03 +08:00
|
|
|
import {
|
|
|
|
type ComponentInternalInstance,
|
|
|
|
type Data,
|
2024-06-10 17:37:32 +08:00
|
|
|
getComponentPublicInstance,
|
2022-04-12 15:54:03 +08:00
|
|
|
} from './component'
|
2021-03-06 00:10:06 +08:00
|
|
|
import { currentRenderingInstance } from './componentRenderContext'
|
2019-09-07 00:58:31 +08:00
|
|
|
import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
|
2020-08-20 04:11:29 +08:00
|
|
|
import type { ComponentPublicInstance } from './componentPublicInstance'
|
2021-04-06 23:57:10 +08:00
|
|
|
import { mapCompatDirectiveHook } from './compat/customDirective'
|
2024-08-20 08:21:44 +08:00
|
|
|
import { pauseTracking, resetTracking, traverse } from '@vue/reactivity'
|
2019-09-01 04:36:36 +08:00
|
|
|
|
2024-04-25 16:04:03 +08:00
|
|
|
export interface DirectiveBinding<
|
|
|
|
Value = any,
|
|
|
|
Modifiers extends string = string,
|
|
|
|
Arg extends string = string,
|
|
|
|
> {
|
2024-06-10 17:37:32 +08:00
|
|
|
instance: ComponentPublicInstance | Record<string, any> | null
|
2024-04-25 16:04:03 +08:00
|
|
|
value: Value
|
|
|
|
oldValue: Value | null
|
|
|
|
arg?: Arg
|
|
|
|
modifiers: DirectiveModifiers<Modifiers>
|
|
|
|
dir: ObjectDirective<any, Value>
|
2019-09-01 04:36:36 +08:00
|
|
|
}
|
|
|
|
|
2024-04-25 16:04:03 +08:00
|
|
|
export type DirectiveHook<
|
|
|
|
HostElement = any,
|
|
|
|
Prev = VNode<any, HostElement> | null,
|
|
|
|
Value = any,
|
|
|
|
Modifiers extends string = string,
|
|
|
|
Arg extends string = string,
|
|
|
|
> = (
|
|
|
|
el: HostElement,
|
|
|
|
binding: DirectiveBinding<Value, Modifiers, Arg>,
|
|
|
|
vnode: VNode<any, HostElement>,
|
2020-03-19 00:30:20 +08:00
|
|
|
prevVNode: Prev,
|
2019-09-01 04:36:36 +08:00
|
|
|
) => void
|
|
|
|
|
2024-05-04 07:57:47 +08:00
|
|
|
export type SSRDirectiveHook<
|
|
|
|
Value = any,
|
|
|
|
Modifiers extends string = string,
|
|
|
|
Arg extends string = string,
|
|
|
|
> = (
|
|
|
|
binding: DirectiveBinding<Value, Modifiers, Arg>,
|
2020-03-17 06:36:19 +08:00
|
|
|
vnode: VNode,
|
|
|
|
) => Data | undefined
|
|
|
|
|
2024-04-25 16:04:03 +08:00
|
|
|
export interface ObjectDirective<
|
|
|
|
HostElement = any,
|
|
|
|
Value = any,
|
|
|
|
Modifiers extends string = string,
|
|
|
|
Arg extends string = string,
|
|
|
|
> {
|
2024-05-04 07:57:47 +08:00
|
|
|
/**
|
|
|
|
* @internal without this, ts-expect-error in directives.test-d.ts somehow
|
|
|
|
* fails when running tsc, but passes in IDE and when testing against built
|
|
|
|
* dts. Could be a TS bug.
|
|
|
|
*/
|
|
|
|
__mod?: Modifiers
|
2024-04-25 16:04:03 +08:00
|
|
|
created?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
|
|
|
|
beforeMount?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
|
|
|
|
mounted?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
|
|
|
|
beforeUpdate?: DirectiveHook<
|
|
|
|
HostElement,
|
|
|
|
VNode<any, HostElement>,
|
|
|
|
Value,
|
|
|
|
Modifiers,
|
|
|
|
Arg
|
|
|
|
>
|
|
|
|
updated?: DirectiveHook<
|
|
|
|
HostElement,
|
|
|
|
VNode<any, HostElement>,
|
|
|
|
Value,
|
|
|
|
Modifiers,
|
|
|
|
Arg
|
|
|
|
>
|
|
|
|
beforeUnmount?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
|
|
|
|
unmounted?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
|
2024-05-04 07:57:47 +08:00
|
|
|
getSSRProps?: SSRDirectiveHook<Value, Modifiers, Arg>
|
2021-07-16 00:14:19 +08:00
|
|
|
deep?: boolean
|
2019-09-01 04:36:36 +08:00
|
|
|
}
|
|
|
|
|
2024-04-25 16:04:03 +08:00
|
|
|
export type FunctionDirective<
|
|
|
|
HostElement = any,
|
|
|
|
V = any,
|
|
|
|
Modifiers extends string = string,
|
|
|
|
Arg extends string = string,
|
|
|
|
> = DirectiveHook<HostElement, any, V, Modifiers, Arg>
|
2019-10-16 14:12:26 +08:00
|
|
|
|
2024-04-25 16:04:03 +08:00
|
|
|
export type Directive<
|
|
|
|
HostElement = any,
|
|
|
|
Value = any,
|
|
|
|
Modifiers extends string = string,
|
|
|
|
Arg extends string = string,
|
|
|
|
> =
|
|
|
|
| ObjectDirective<HostElement, Value, Modifiers, Arg>
|
|
|
|
| FunctionDirective<HostElement, Value, Modifiers, Arg>
|
2019-10-16 14:12:26 +08:00
|
|
|
|
2024-04-25 16:04:03 +08:00
|
|
|
export type DirectiveModifiers<K extends string = string> = Record<K, boolean>
|
2019-09-01 04:36:36 +08:00
|
|
|
|
2024-08-08 23:05:21 +08:00
|
|
|
export function validateDirectiveName(name: string): void {
|
2019-10-19 00:34:45 +08:00
|
|
|
if (isBuiltInDirective(name)) {
|
|
|
|
warn('Do not use built-in directive ids as custom directive id: ' + name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 00:09:29 +08:00
|
|
|
// Directive, value, argument, modifiers
|
2019-09-07 00:58:31 +08:00
|
|
|
export type DirectiveArguments = Array<
|
2022-11-08 10:49:49 +08:00
|
|
|
| [Directive | undefined]
|
|
|
|
| [Directive | undefined, any]
|
|
|
|
| [Directive | undefined, any, string]
|
2024-08-07 17:02:15 +08:00
|
|
|
| [Directive | undefined, any, string | undefined, DirectiveModifiers]
|
2019-09-03 00:09:29 +08:00
|
|
|
>
|
2019-09-01 04:36:36 +08:00
|
|
|
|
2020-05-01 05:04:35 +08:00
|
|
|
/**
|
|
|
|
* Adds directives to a VNode.
|
|
|
|
*/
|
2019-10-27 04:32:27 +08:00
|
|
|
export function withDirectives<T extends VNode>(
|
|
|
|
vnode: T,
|
|
|
|
directives: DirectiveArguments,
|
|
|
|
): T {
|
2024-01-09 15:26:07 +08:00
|
|
|
if (currentRenderingInstance === null) {
|
2020-03-17 00:47:49 +08:00
|
|
|
__DEV__ && warn(`withDirectives can only be used inside render functions.`)
|
|
|
|
return vnode
|
|
|
|
}
|
2024-06-10 17:37:32 +08:00
|
|
|
const instance = getComponentPublicInstance(currentRenderingInstance)
|
2020-03-18 23:43:32 +08:00
|
|
|
const bindings: DirectiveBinding[] = vnode.dirs || (vnode.dirs = [])
|
2019-10-27 04:00:07 +08:00
|
|
|
for (let i = 0; i < directives.length; i++) {
|
|
|
|
let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]
|
2022-11-08 10:49:49 +08:00
|
|
|
if (dir) {
|
|
|
|
if (isFunction(dir)) {
|
|
|
|
dir = {
|
|
|
|
mounted: dir,
|
|
|
|
updated: dir,
|
|
|
|
} as ObjectDirective
|
|
|
|
}
|
|
|
|
if (dir.deep) {
|
|
|
|
traverse(value)
|
|
|
|
}
|
|
|
|
bindings.push({
|
|
|
|
dir,
|
|
|
|
instance,
|
|
|
|
value,
|
|
|
|
oldValue: void 0,
|
|
|
|
arg,
|
|
|
|
modifiers,
|
|
|
|
})
|
2019-10-27 04:00:07 +08:00
|
|
|
}
|
2019-09-01 04:36:36 +08:00
|
|
|
}
|
|
|
|
return vnode
|
|
|
|
}
|
|
|
|
|
2019-09-01 05:06:39 +08:00
|
|
|
export function invokeDirectiveHook(
|
2019-09-03 00:09:29 +08:00
|
|
|
vnode: VNode,
|
2020-03-18 23:43:32 +08:00
|
|
|
prevVNode: VNode | null,
|
|
|
|
instance: ComponentInternalInstance | null,
|
|
|
|
name: keyof ObjectDirective,
|
2024-08-08 23:05:21 +08:00
|
|
|
): void {
|
2020-03-18 23:43:32 +08:00
|
|
|
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
|
|
|
|
}
|
2021-04-06 23:57:10 +08:00
|
|
|
let hook = binding.dir[name] as DirectiveHook | DirectiveHook[] | undefined
|
|
|
|
if (__COMPAT__ && !hook) {
|
2021-04-10 06:52:14 +08:00
|
|
|
hook = mapCompatDirectiveHook(name, binding.dir, instance)
|
2021-04-06 23:57:10 +08:00
|
|
|
}
|
2020-03-18 23:43:32 +08:00
|
|
|
if (hook) {
|
2021-05-28 04:53:19 +08:00
|
|
|
// disable tracking inside all lifecycle hooks
|
|
|
|
// since they can potentially be called inside effects.
|
|
|
|
pauseTracking()
|
2020-03-18 23:43:32 +08:00
|
|
|
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
|
|
|
|
vnode.el,
|
|
|
|
binding,
|
|
|
|
vnode,
|
|
|
|
prevVNode,
|
|
|
|
])
|
2021-05-28 04:53:19 +08:00
|
|
|
resetTracking()
|
2020-03-18 23:43:32 +08:00
|
|
|
}
|
|
|
|
}
|
2019-09-01 05:06:39 +08:00
|
|
|
}
|