feat: applyDirectives
This commit is contained in:
		
							parent
							
								
									6801885f57
								
							
						
					
					
						commit
						a3b0f2bd1c
					
				@ -8,7 +8,7 @@ import {
 | 
				
			|||||||
import { queueJob, queuePostFlushCb } from './scheduler'
 | 
					import { queueJob, queuePostFlushCb } from './scheduler'
 | 
				
			||||||
import { EMPTY_OBJ, isObject, isArray, isFunction } from '@vue/shared'
 | 
					import { EMPTY_OBJ, isObject, isArray, isFunction } from '@vue/shared'
 | 
				
			||||||
import { recordEffect } from './apiReactivity'
 | 
					import { recordEffect } from './apiReactivity'
 | 
				
			||||||
import { getCurrentInstance } from './component'
 | 
					import { currentInstance } from './component'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ErrorTypes,
 | 
					  ErrorTypes,
 | 
				
			||||||
  callWithErrorHandling,
 | 
					  callWithErrorHandling,
 | 
				
			||||||
@ -83,7 +83,7 @@ function doWatch(
 | 
				
			|||||||
    | null,
 | 
					    | null,
 | 
				
			||||||
  { lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
 | 
					  { lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
 | 
				
			||||||
): StopHandle {
 | 
					): StopHandle {
 | 
				
			||||||
  const instance = getCurrentInstance()
 | 
					  const instance = currentInstance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let getter: Function
 | 
					  let getter: Function
 | 
				
			||||||
  if (isArray(source)) {
 | 
					  if (isArray(source)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -355,6 +355,10 @@ function createSetupContext(instance: ComponentInstance): SetupContext {
 | 
				
			|||||||
  return __DEV__ ? Object.freeze(context) : context
 | 
					  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 {
 | 
					export function renderComponentRoot(instance: ComponentInstance): VNode {
 | 
				
			||||||
  const {
 | 
					  const {
 | 
				
			||||||
    type: Component,
 | 
					    type: Component,
 | 
				
			||||||
@ -367,9 +371,12 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
 | 
				
			|||||||
    refs,
 | 
					    refs,
 | 
				
			||||||
    emit
 | 
					    emit
 | 
				
			||||||
  } = instance
 | 
					  } = instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let result
 | 
				
			||||||
 | 
					  currentRenderingInstance = instance
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
 | 
					    if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
 | 
				
			||||||
      return normalizeVNode(
 | 
					      result = normalizeVNode(
 | 
				
			||||||
        (instance.render as RenderFunction).call(
 | 
					        (instance.render as RenderFunction).call(
 | 
				
			||||||
          renderProxy,
 | 
					          renderProxy,
 | 
				
			||||||
          props,
 | 
					          props,
 | 
				
			||||||
@ -379,7 +386,7 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // functional
 | 
					      // functional
 | 
				
			||||||
      const render = Component as FunctionalComponent
 | 
					      const render = Component as FunctionalComponent
 | 
				
			||||||
      return normalizeVNode(
 | 
					      result = normalizeVNode(
 | 
				
			||||||
        render.length > 1
 | 
					        render.length > 1
 | 
				
			||||||
          ? render(props, {
 | 
					          ? render(props, {
 | 
				
			||||||
              attrs,
 | 
					              attrs,
 | 
				
			||||||
@ -392,8 +399,10 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
    handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
 | 
					    handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
 | 
				
			||||||
    return createVNode(Empty)
 | 
					    result = createVNode(Empty)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  currentRenderingInstance = null
 | 
				
			||||||
 | 
					  return result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function shouldUpdateComponent(
 | 
					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
 | 
					  callWithAsyncErrorHandling
 | 
				
			||||||
} from './errorHandling'
 | 
					} from './errorHandling'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For the compiler
 | 
				
			||||||
 | 
					export { applyDirectives, resolveDirective } from './directives'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Types -----------------------------------------------------------------------
 | 
					// Types -----------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { VNode } from './vnode'
 | 
					export { VNode } from './vnode'
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ export type NormalizedChildren = string | VNodeChildren | RawSlots | null
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface VNode {
 | 
					export interface VNode {
 | 
				
			||||||
  type: VNodeTypes
 | 
					  type: VNodeTypes
 | 
				
			||||||
  props: { [key: string]: any } | null
 | 
					  props: Record<any, any> | null
 | 
				
			||||||
  key: string | number | null
 | 
					  key: string | number | null
 | 
				
			||||||
  ref: string | Function | null
 | 
					  ref: string | Function | null
 | 
				
			||||||
  children: NormalizedChildren
 | 
					  children: NormalizedChildren
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user