feat: applyDirectives
This commit is contained in:
parent
6801885f57
commit
a3b0f2bd1c
@ -8,7 +8,7 @@ import {
|
||||
import { queueJob, queuePostFlushCb } from './scheduler'
|
||||
import { EMPTY_OBJ, isObject, isArray, isFunction } from '@vue/shared'
|
||||
import { recordEffect } from './apiReactivity'
|
||||
import { getCurrentInstance } from './component'
|
||||
import { currentInstance } from './component'
|
||||
import {
|
||||
ErrorTypes,
|
||||
callWithErrorHandling,
|
||||
@ -83,7 +83,7 @@ function doWatch(
|
||||
| null,
|
||||
{ lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
|
||||
): StopHandle {
|
||||
const instance = getCurrentInstance()
|
||||
const instance = currentInstance
|
||||
|
||||
let getter: Function
|
||||
if (isArray(source)) {
|
||||
|
@ -355,6 +355,10 @@ function createSetupContext(instance: ComponentInstance): SetupContext {
|
||||
return __DEV__ ? Object.freeze(context) : context
|
||||
}
|
||||
|
||||
// mark the current rendering instance for asset resolution (e.g.
|
||||
// resolveComponent, resolveDirective) during render
|
||||
export let currentRenderingInstance: ComponentInstance | null = null
|
||||
|
||||
export function renderComponentRoot(instance: ComponentInstance): VNode {
|
||||
const {
|
||||
type: Component,
|
||||
@ -367,9 +371,12 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
|
||||
refs,
|
||||
emit
|
||||
} = instance
|
||||
|
||||
let result
|
||||
currentRenderingInstance = instance
|
||||
try {
|
||||
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
||||
return normalizeVNode(
|
||||
result = normalizeVNode(
|
||||
(instance.render as RenderFunction).call(
|
||||
renderProxy,
|
||||
props,
|
||||
@ -379,7 +386,7 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
|
||||
} else {
|
||||
// functional
|
||||
const render = Component as FunctionalComponent
|
||||
return normalizeVNode(
|
||||
result = normalizeVNode(
|
||||
render.length > 1
|
||||
? render(props, {
|
||||
attrs,
|
||||
@ -392,8 +399,10 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
|
||||
}
|
||||
} catch (err) {
|
||||
handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
|
||||
return createVNode(Empty)
|
||||
result = createVNode(Empty)
|
||||
}
|
||||
currentRenderingInstance = null
|
||||
return result
|
||||
}
|
||||
|
||||
export function shouldUpdateComponent(
|
||||
|
122
packages/runtime-core/src/directives.ts
Normal file
122
packages/runtime-core/src/directives.ts
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
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'
|
||||
import { extend } from '@vue/shared'
|
||||
import { warn } from './warning'
|
||||
import {
|
||||
ComponentInstance,
|
||||
currentRenderingInstance,
|
||||
ComponentRenderProxy
|
||||
} from './component'
|
||||
|
||||
interface DirectiveBinding {
|
||||
instance: ComponentRenderProxy | null
|
||||
value?: any
|
||||
oldValue?: any
|
||||
arg?: string
|
||||
modifiers?: DirectiveModifiers
|
||||
}
|
||||
|
||||
type DirectiveHook = (
|
||||
el: any,
|
||||
binding: DirectiveBinding,
|
||||
vnode: VNode,
|
||||
prevVNode: VNode | void
|
||||
) => void
|
||||
|
||||
interface Directive {
|
||||
beforeMount: DirectiveHook
|
||||
mounted: DirectiveHook
|
||||
beforeUpdate: DirectiveHook
|
||||
updated: DirectiveHook
|
||||
beforeUnmount: DirectiveHook
|
||||
unmounted: DirectiveHook
|
||||
}
|
||||
|
||||
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) {
|
||||
const hook = directive[key as keyof Directive]
|
||||
const hookKey = `vnode` + key[0].toUpperCase() + key.slice(1)
|
||||
const vnodeHook = (vnode: VNode, prevVNode?: VNode) => {
|
||||
let oldValue
|
||||
if (prevVNode !== void 0) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
type DirectiveArguments = [
|
||||
Directive,
|
||||
any,
|
||||
string | undefined,
|
||||
DirectiveModifiers | undefined
|
||||
][]
|
||||
|
||||
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++) {
|
||||
applyDirective(vnode.props, instance, ...directives[i])
|
||||
}
|
||||
} else if (__DEV__) {
|
||||
warn(`applyDirectives can only be used inside render functions.`)
|
||||
}
|
||||
return vnode
|
||||
}
|
||||
|
||||
export function resolveDirective(name: string): Directive {
|
||||
// TODO
|
||||
return {} as any
|
||||
}
|
@ -35,6 +35,9 @@ export {
|
||||
callWithAsyncErrorHandling
|
||||
} from './errorHandling'
|
||||
|
||||
// For the compiler
|
||||
export { applyDirectives, resolveDirective } from './directives'
|
||||
|
||||
// Types -----------------------------------------------------------------------
|
||||
|
||||
export { VNode } from './vnode'
|
||||
|
@ -34,7 +34,7 @@ export type NormalizedChildren = string | VNodeChildren | RawSlots | null
|
||||
|
||||
export interface VNode {
|
||||
type: VNodeTypes
|
||||
props: { [key: string]: any } | null
|
||||
props: Record<any, any> | null
|
||||
key: string | number | null
|
||||
ref: string | Function | null
|
||||
children: NormalizedChildren
|
||||
|
Loading…
Reference in New Issue
Block a user