componentRenderContext.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { ComponentInternalInstance } from './component'
  2. import { devtoolsComponentUpdated } from './devtools'
  3. import { setBlockTracking } from './vnode'
  4. /**
  5. * mark the current rendering instance for asset resolution (e.g.
  6. * resolveComponent, resolveDirective) during render
  7. */
  8. export let currentRenderingInstance: ComponentInternalInstance | null = null
  9. export let currentScopeId: string | null = null
  10. /**
  11. * Note: rendering calls maybe nested. The function returns the parent rendering
  12. * instance if present, which should be restored after the render is done:
  13. *
  14. * ```js
  15. * const prev = setCurrentRenderingInstance(i)
  16. * // ...render
  17. * setCurrentRenderingInstance(prev)
  18. * ```
  19. */
  20. export function setCurrentRenderingInstance(
  21. instance: ComponentInternalInstance | null
  22. ): ComponentInternalInstance | null {
  23. const prev = currentRenderingInstance
  24. currentRenderingInstance = instance
  25. currentScopeId = (instance && instance.type.__scopeId) || null
  26. // v2 pre-compiled components uses _scopeId instead of __scopeId
  27. if (__COMPAT__ && !currentScopeId) {
  28. currentScopeId = (instance && (instance.type as any)._scopeId) || null
  29. }
  30. return prev
  31. }
  32. /**
  33. * Set scope id when creating hoisted vnodes.
  34. * @private compiler helper
  35. */
  36. export function pushScopeId(id: string | null) {
  37. currentScopeId = id
  38. }
  39. /**
  40. * Technically we no longer need this after 3.0.8 but we need to keep the same
  41. * API for backwards compat w/ code generated by compilers.
  42. * @private
  43. */
  44. export function popScopeId() {
  45. currentScopeId = null
  46. }
  47. /**
  48. * Only for backwards compat
  49. * @private
  50. */
  51. export const withScopeId = (_id: string) => withCtx
  52. export type ContextualRenderFn = {
  53. (...args: any[]): any
  54. _n: boolean /* already normalized */
  55. _c: boolean /* compiled */
  56. _d: boolean /* disableTracking */
  57. _ns: boolean /* nonScoped */
  58. }
  59. /**
  60. * Wrap a slot function to memoize current rendering instance
  61. * @private compiler helper
  62. */
  63. export function withCtx(
  64. fn: Function,
  65. ctx: ComponentInternalInstance | null = currentRenderingInstance,
  66. isNonScopedSlot?: boolean // __COMPAT__ only
  67. ) {
  68. if (!ctx) return fn
  69. // already normalized
  70. if ((fn as ContextualRenderFn)._n) {
  71. return fn
  72. }
  73. const renderFnWithContext: ContextualRenderFn = (...args: any[]) => {
  74. // If a user calls a compiled slot inside a template expression (#1745), it
  75. // can mess up block tracking, so by default we disable block tracking and
  76. // force bail out when invoking a compiled slot (indicated by the ._d flag).
  77. // This isn't necessary if rendering a compiled `<slot>`, so we flip the
  78. // ._d flag off when invoking the wrapped fn inside `renderSlot`.
  79. if (renderFnWithContext._d) {
  80. setBlockTracking(-1)
  81. }
  82. const prevInstance = setCurrentRenderingInstance(ctx)
  83. let res
  84. try {
  85. res = fn(...args)
  86. } finally {
  87. setCurrentRenderingInstance(prevInstance)
  88. if (renderFnWithContext._d) {
  89. setBlockTracking(1)
  90. }
  91. }
  92. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  93. devtoolsComponentUpdated(ctx)
  94. }
  95. return res
  96. }
  97. // mark normalized to avoid duplicated wrapping
  98. renderFnWithContext._n = true
  99. // mark this as compiled by default
  100. // this is used in vnode.ts -> normalizeChildren() to set the slot
  101. // rendering flag.
  102. renderFnWithContext._c = true
  103. // disable block tracking by default
  104. renderFnWithContext._d = true
  105. // compat build only flag to distinguish scoped slots from non-scoped ones
  106. if (__COMPAT__ && isNonScopedSlot) {
  107. renderFnWithContext._ns = true
  108. }
  109. return renderFnWithContext
  110. }