]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): set context for manual slot functions as well
authorEvan You <yyx990803@gmail.com>
Mon, 16 Mar 2020 16:20:52 +0000 (12:20 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 16 Mar 2020 17:06:46 +0000 (13:06 -0400)
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentSlots.ts
packages/runtime-core/src/helpers/scopeId.ts
packages/runtime-core/src/helpers/withRenderContext.ts
packages/runtime-core/src/vnode.ts

index ccba387d52bd9c6b97ca7acd6a6c7e8b82261301..0e619b709c9c3c4a257408c25f1188c2fe84c961 100644 (file)
@@ -494,6 +494,9 @@ const SetupProxyHandlers: { [key: string]: ProxyHandler<any> } = {}
       if (__DEV__) {
         markAttrsAccessed()
       }
+      // if the user pass the slots proxy to h(), normalizeChildren should not
+      // attempt to attach ctx to the object
+      if (key === '_') return 1
       return instance[type][key]
     },
     has: (instance, key) => key === SetupProxySymbol || key in instance[type],
index 2958903e1527c66eeb48250a16d3b7a36120e54e..db049246e7ce5d241b9c1846904aa5a60b6c75b0 100644 (file)
@@ -8,6 +8,7 @@ import {
 import { isArray, isFunction, EMPTY_OBJ, ShapeFlags } from '@vue/shared'
 import { warn } from './warning'
 import { isKeepAlive } from './components/KeepAlive'
+import { withCtx } from './helpers/withRenderContext'
 
 export type Slot = (...args: any[]) => VNode[]
 
@@ -21,6 +22,9 @@ export type RawSlots = {
   [name: string]: unknown
   // manual render fn hint to skip forced children updates
   $stable?: boolean
+  // internal, for tracking slot owner instance. This is attached during
+  // normalizeChildren when the component vnode is created.
+  _ctx?: ComponentInternalInstance | null
   // internal, indicates compiler generated slots = can skip normalization
   _?: 1
 }
@@ -30,18 +34,21 @@ const normalizeSlotValue = (value: unknown): VNode[] =>
     ? value.map(normalizeVNode)
     : [normalizeVNode(value as VNodeChild)]
 
-const normalizeSlot = (key: string, rawSlot: Function): Slot => (
-  props: any
-) => {
-  if (__DEV__ && currentInstance != null) {
-    warn(
-      `Slot "${key}" invoked outside of the render function: ` +
-        `this will not track dependencies used in the slot. ` +
-        `Invoke the slot function inside the render function instead.`
-    )
-  }
-  return normalizeSlotValue(rawSlot(props))
-}
+const normalizeSlot = (
+  key: string,
+  rawSlot: Function,
+  ctx: ComponentInternalInstance | null | undefined
+): Slot =>
+  withCtx((props: any) => {
+    if (__DEV__ && currentInstance != null) {
+      warn(
+        `Slot "${key}" invoked outside of the render function: ` +
+          `this will not track dependencies used in the slot. ` +
+          `Invoke the slot function inside the render function instead.`
+      )
+    }
+    return normalizeSlotValue(rawSlot(props))
+  }, ctx)
 
 export function resolveSlots(
   instance: ComponentInternalInstance,
@@ -55,11 +62,12 @@ export function resolveSlots(
       slots = children as Slots
     } else {
       slots = {}
+      const ctx = rawSlots._ctx
       for (const key in rawSlots) {
-        if (key === '$stable') continue
+        if (key === '$stable' || key === '_ctx') continue
         const value = rawSlots[key]
         if (isFunction(value)) {
-          slots[key] = normalizeSlot(key, value)
+          slots[key] = normalizeSlot(key, value, ctx)
         } else if (value != null) {
           if (__DEV__) {
             warn(
index a8ee24016df896ebc2cb92e6b58989fc0c7839b5..0cc88046f32d5633d69f1db6ae59dbed39b353cf 100644 (file)
@@ -24,13 +24,12 @@ export function popScopeId() {
 export function withScopeId(id: string): <T extends Function>(fn: T) => T {
   if (__BUNDLER__) {
     return ((fn: Function, ctx?: ComponentInternalInstance) => {
-      function renderWithId(this: any) {
+      return withCtx(function(this: any) {
         pushScopeId(id)
         const res = fn.apply(this, arguments)
         popScopeId()
         return res
-      }
-      return ctx ? withCtx(renderWithId, ctx) : renderWithId
+      }, ctx)
     }) as any
   } else {
     return undefined as any
index 47ed8091d413957f792f5a6e2941820269221481..b3642a1f84052c603b224b9da3250a5038942784 100644 (file)
@@ -5,7 +5,11 @@ import {
   currentRenderingInstance
 } from '../componentRenderUtils'
 
-export function withCtx(fn: Slot, ctx: ComponentInternalInstance) {
+export function withCtx(
+  fn: Slot,
+  ctx: ComponentInternalInstance | null | undefined
+) {
+  if (!ctx) return fn
   return function renderFnWithContext() {
     const owner = currentRenderingInstance
     setCurrentRenderingInstance(ctx)
index 272731e8a0502263e191b7f7d2e9d0e94fee4479..ce91483c8cba5421f26e8ae574e143e6b574f38d 100644 (file)
@@ -30,6 +30,7 @@ import { TransitionHooks } from './components/BaseTransition'
 import { warn } from './warning'
 import { currentScopeId } from './helpers/scopeId'
 import { PortalImpl, isPortal } from './components/Portal'
+import { currentRenderingInstance } from './componentRenderUtils'
 
 export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
   __isFragment: true
@@ -387,8 +388,11 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
     type = ShapeFlags.ARRAY_CHILDREN
   } else if (typeof children === 'object') {
     type = ShapeFlags.SLOTS_CHILDREN
+    if (!(children as RawSlots)._) {
+      ;(children as RawSlots)._ctx = currentRenderingInstance
+    }
   } else if (isFunction(children)) {
-    children = { default: children }
+    children = { default: children, _ctx: currentRenderingInstance }
     type = ShapeFlags.SLOTS_CHILDREN
   } else {
     children = String(children)