| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- /**
- 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, makeMap, EMPTY_ARR } from '@vue/shared'
- import { warn } from './warning'
- import { ComponentInternalInstance } from './component'
- import { currentRenderingInstance } from './componentRenderUtils'
- import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
- import { ComponentPublicInstance } from './componentProxy'
- export interface DirectiveBinding {
- instance: ComponentPublicInstance | null
- value: any
- oldValue: any
- arg?: string
- modifiers: DirectiveModifiers
- dir: ObjectDirective
- }
- export type DirectiveHook<T = any> = (
- el: T,
- binding: DirectiveBinding,
- vnode: VNode<any, T>,
- prevVNode: VNode<any, T> | null
- ) => void
- export interface ObjectDirective<T = any> {
- beforeMount?: DirectiveHook<T>
- mounted?: DirectiveHook<T>
- beforeUpdate?: DirectiveHook<T>
- updated?: DirectiveHook<T>
- beforeUnmount?: DirectiveHook<T>
- unmounted?: DirectiveHook<T>
- }
- export type FunctionDirective<T = any> = DirectiveHook<T>
- export type Directive<T = any> = ObjectDirective<T> | FunctionDirective<T>
- export type DirectiveModifiers = Record<string, boolean>
- export type VNodeDirectiveData = [
- unknown,
- string | undefined,
- DirectiveModifiers
- ]
- 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)
- }
- }
- 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)
- }
- }
- }
- map[key] = [vnodeKey, vnodeHook]
- return map
- },
- {} as Record<string, [string, Function]>
- )
- // Directive, value, argument, modifiers
- export type DirectiveArguments = Array<
- | [Directive]
- | [Directive, any]
- | [Directive, any, string]
- | [Directive, any, string, DirectiveModifiers]
- >
- 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 = 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
- }
- }
- }
- return vnode
- }
- export function invokeDirectiveHook(
- hook: Function | Function[],
- instance: ComponentInternalInstance | null,
- vnode: VNode,
- prevVNode: VNode | null = null
- ) {
- callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
- vnode,
- prevVNode
- ])
- }
|