vue3-yuanma/packages/runtime-core/src/directives.ts

147 lines
3.6 KiB
TypeScript
Raw Normal View History

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')
return applyDirectives(
h(comp),
[foo, this.x],
[bar, this.y]
)
*/
import { VNode, cloneVNode } from './vnode'
2019-08-31 21:06:39 +00:00
import { extend, isArray, isFunction } from '@vue/shared'
2019-08-31 20:36:36 +00:00
import { warn } from './warning'
import {
ComponentInstance,
currentRenderingInstance,
ComponentRenderProxy
} from './component'
2019-08-31 21:06:39 +00:00
import { callWithAsyncErrorHandling, ErrorTypes } from './errorHandling'
2019-09-02 20:09:34 +00:00
import { HostNode } from './createRenderer'
2019-08-31 20:36:36 +00:00
2019-09-02 16:09:29 +00:00
export interface DirectiveBinding {
2019-08-31 20:36:36 +00:00
instance: ComponentRenderProxy | null
value?: any
oldValue?: any
arg?: string
modifiers?: DirectiveModifiers
}
2019-09-02 16:09:29 +00:00
export type DirectiveHook = (
2019-09-02 20:09:34 +00:00
el: HostNode,
2019-08-31 20:36:36 +00:00
binding: DirectiveBinding,
vnode: VNode,
2019-09-02 16:09:29 +00:00
prevVNode: VNode | null
2019-08-31 20:36:36 +00:00
) => void
2019-09-02 16:09:29 +00:00
export interface Directive {
beforeMount?: DirectiveHook
mounted?: DirectiveHook
beforeUpdate?: DirectiveHook
updated?: DirectiveHook
beforeUnmount?: DirectiveHook
unmounted?: DirectiveHook
2019-08-31 20:36:36 +00:00
}
type DirectiveModifiers = Record<string, boolean>
const valueCache = new WeakMap<Directive, WeakMap<any, any>>()
function applyDirective(
props: Record<any, any>,
instance: ComponentInstance,
directive: Directive,
value?: any,
arg?: string,
modifiers?: DirectiveModifiers
) {
let valueCacheForDir = valueCache.get(directive) as WeakMap<VNode, any>
if (!valueCacheForDir) {
valueCacheForDir = new WeakMap<VNode, any>()
valueCache.set(directive, valueCacheForDir)
}
for (const key in directive) {
2019-09-02 16:09:29 +00:00
const hook = directive[key as keyof Directive] as DirectiveHook
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
? [].concat(existing as any, vnodeHook as any)
: vnodeHook
}
}
2019-09-02 16:09:29 +00:00
// Directive, value, argument, modifiers
type DirectiveArguments = Array<
| [Directive]
| [Directive, any]
| [Directive, any, string]
| [Directive, any, string, DirectiveModifiers]
>
2019-08-31 20:36:36 +00:00
export function applyDirectives(
vnode: VNode,
...directives: DirectiveArguments
) {
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-09-02 16:09:29 +00:00
;(applyDirective as any)(vnode.props, instance, ...directives[i])
2019-08-31 20:36:36 +00:00
}
} else if (__DEV__) {
warn(`applyDirectives can only be used inside render functions.`)
}
return vnode
}
export function resolveDirective(name: string): Directive {
// TODO
return {} as any
}
2019-08-31 21:06:39 +00:00
export function invokeDirectiveHook(
hook: Function | Function[],
instance: ComponentInstance | 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,
ErrorTypes.DIRECTIVE_HOOK,
args
)
}
} else if (isFunction(hook)) {
callWithAsyncErrorHandling(hook, instance, ErrorTypes.DIRECTIVE_HOOK, args)
}
}