| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- /**
- Runtime helper for applying directives to a vnode. Example usage:
- const comp = resolveComponent('comp')
- const foo = resolveDirective('foo')
- const bar = resolveDirective('bar')
- return withDirectives(h(comp), [
- [foo, this.x],
- [bar, this.y]
- ])
- */
- import type { VNode } from './vnode'
- import { EMPTY_OBJ, isBuiltInDirective, isFunction } from '@vue/shared'
- import { warn } from './warning'
- import {
- type ComponentInternalInstance,
- type Data,
- getComponentPublicInstance,
- } from './component'
- import { currentRenderingInstance } from './componentRenderContext'
- import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
- import type { ComponentPublicInstance } from './componentPublicInstance'
- import { mapCompatDirectiveHook } from './compat/customDirective'
- import { pauseTracking, resetTracking, traverse } from '@vue/reactivity'
- export interface DirectiveBinding<
- Value = any,
- Modifiers extends string = string,
- Arg extends string = string,
- > {
- instance: ComponentPublicInstance | Record<string, any> | null
- value: Value
- oldValue: Value | null
- arg?: Arg
- modifiers: DirectiveModifiers<Modifiers>
- dir: ObjectDirective<any, Value>
- }
- 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>,
- prevVNode: Prev,
- ) => void
- export type SSRDirectiveHook<
- Value = any,
- Modifiers extends string = string,
- Arg extends string = string,
- > = (
- binding: DirectiveBinding<Value, Modifiers, Arg>,
- vnode: VNode,
- ) => Data | undefined
- export interface ObjectDirective<
- HostElement = any,
- Value = any,
- Modifiers extends string = string,
- Arg extends string = string,
- > {
- /**
- * @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
- 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>
- getSSRProps?: SSRDirectiveHook<Value, Modifiers, Arg>
- deep?: boolean
- }
- export type FunctionDirective<
- HostElement = any,
- V = any,
- Modifiers extends string = string,
- Arg extends string = string,
- > = DirectiveHook<HostElement, any, V, Modifiers, Arg>
- 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>
- export type DirectiveModifiers<K extends string = string> = Partial<
- Record<K, boolean>
- >
- export function validateDirectiveName(name: string): void {
- if (isBuiltInDirective(name)) {
- warn('Do not use built-in directive ids as custom directive id: ' + name)
- }
- }
- // Directive, value, argument, modifiers
- export type DirectiveArguments = Array<
- | [Directive | undefined]
- | [Directive | undefined, any]
- | [Directive | undefined, any, string]
- | [Directive | undefined, any, string | undefined, DirectiveModifiers]
- >
- /**
- * Adds directives to a VNode.
- */
- export function withDirectives<T extends VNode>(
- vnode: T,
- directives: DirectiveArguments,
- ): T {
- if (currentRenderingInstance === null) {
- __DEV__ && warn(`withDirectives can only be used inside render functions.`)
- return vnode
- }
- const instance = getComponentPublicInstance(currentRenderingInstance)
- const bindings: DirectiveBinding[] = vnode.dirs || (vnode.dirs = [])
- for (let i = 0; i < directives.length; i++) {
- let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]
- 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,
- })
- }
- }
- return vnode
- }
- export function invokeDirectiveHook(
- vnode: VNode,
- prevVNode: VNode | null,
- instance: ComponentInternalInstance | null,
- name: keyof ObjectDirective,
- ): void {
- 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
- }
- let hook = binding.dir[name] as DirectiveHook | DirectiveHook[] | undefined
- if (__COMPAT__ && !hook) {
- hook = mapCompatDirectiveHook(name, binding.dir, instance)
- }
- if (hook) {
- // disable tracking inside all lifecycle hooks
- // since they can potentially be called inside effects.
- pauseTracking()
- callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
- vnode.el,
- binding,
- vnode,
- prevVNode,
- ])
- resetTracking()
- }
- }
- }
|