| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- /**
- 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 { VNode } from './vnode'
- import { isFunction, EMPTY_OBJ, isBuiltInDirective } from '@vue/shared'
- import { warn } from './warning'
- import { ComponentInternalInstance, Data, getExposeProxy } from './component'
- import { currentRenderingInstance } from './componentRenderContext'
- import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
- import { ComponentPublicInstance } from './componentPublicInstance'
- import { mapCompatDirectiveHook } from './compat/customDirective'
- import { pauseTracking, resetTracking } from '@vue/reactivity'
- import { traverse } from './apiWatch'
- export interface DirectiveBinding<V = any> {
- instance: ComponentPublicInstance | null
- value: V
- oldValue: V | null
- arg?: string
- modifiers: DirectiveModifiers
- dir: ObjectDirective<any, V>
- }
- export type DirectiveHook<T = any, Prev = VNode<any, T> | null, V = any> = (
- el: T,
- binding: DirectiveBinding<V>,
- vnode: VNode<any, T>,
- prevVNode: Prev
- ) => void
- export type SSRDirectiveHook = (
- binding: DirectiveBinding,
- vnode: VNode
- ) => Data | undefined
- export interface ObjectDirective<T = any, V = any> {
- created?: DirectiveHook<T, null, V>
- beforeMount?: DirectiveHook<T, null, V>
- mounted?: DirectiveHook<T, null, V>
- beforeUpdate?: DirectiveHook<T, VNode<any, T>, V>
- updated?: DirectiveHook<T, VNode<any, T>, V>
- beforeUnmount?: DirectiveHook<T, null, V>
- unmounted?: DirectiveHook<T, null, V>
- getSSRProps?: SSRDirectiveHook
- deep?: boolean
- }
- export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>
- export type Directive<T = any, V = any> =
- | ObjectDirective<T, V>
- | FunctionDirective<T, V>
- export type DirectiveModifiers = Record<string, boolean>
- export function validateDirectiveName(name: string) {
- 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, DirectiveModifiers]
- >
- /**
- * Adds directives to a VNode.
- */
- export function withDirectives<T extends VNode>(
- vnode: T,
- directives: DirectiveArguments
- ): T {
- const internalInstance = currentRenderingInstance
- if (internalInstance === null) {
- __DEV__ && warn(`withDirectives can only be used inside render functions.`)
- return vnode
- }
- const instance =
- (getExposeProxy(internalInstance) as ComponentPublicInstance) ||
- internalInstance.proxy
- 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
- ) {
- 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()
- }
- }
- }
|