]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: applyDirectives
authorEvan You <yyx990803@gmail.com>
Sat, 31 Aug 2019 20:36:36 +0000 (16:36 -0400)
committerEvan You <yyx990803@gmail.com>
Sat, 31 Aug 2019 20:36:36 +0000 (16:36 -0400)
packages/runtime-core/src/apiWatch.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/directives.ts [new file with mode: 0644]
packages/runtime-core/src/index.ts
packages/runtime-core/src/vnode.ts

index 6d092b2a4dd47919f7b94368b6d77f3cbf141aaa..9ff82eaf2bdedecca7f514283ce4df5c1fb7f87c 100644 (file)
@@ -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)) {
index 396313490aaf095abae38c22386d28ddac4d91ef..a76095aabcadee886bc5b2a73130ecf830ca5e58 100644 (file)
@@ -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(
diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts
new file mode 100644 (file)
index 0000000..46a697e
--- /dev/null
@@ -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
+}
index 795fab7cf06cbec1c458e9b22c5bf9f3cf0b0190..f555bb9806fc8557957bdbbd8c2fa5645bc9580d 100644 (file)
@@ -35,6 +35,9 @@ export {
   callWithAsyncErrorHandling
 } from './errorHandling'
 
+// For the compiler
+export { applyDirectives, resolveDirective } from './directives'
+
 // Types -----------------------------------------------------------------------
 
 export { VNode } from './vnode'
index d2f77f661a197c921c9c5f9ad1c805569cd761d0..3a79c1953ca639b8d9fcc279d00cd433fd14a6a3 100644 (file)
@@ -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