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,
| null,
{ lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): StopHandle {
- const instance = getCurrentInstance()
+ const instance = currentInstance
let getter: Function
if (isArray(source)) {
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,
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,
} else {
// functional
const render = Component as FunctionalComponent
- return normalizeVNode(
+ result = normalizeVNode(
render.length > 1
? render(props, {
attrs,
}
} catch (err) {
handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
- return createVNode(Empty)
+ result = createVNode(Empty)
}
+ currentRenderingInstance = null
+ return result
}
export function shouldUpdateComponent(
--- /dev/null
+/**
+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
+}