h.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import {
  2. VNode,
  3. VNodeProps,
  4. createVNode,
  5. VNodeArrayChildren,
  6. Fragment,
  7. Text,
  8. Comment,
  9. isVNode
  10. } from './vnode'
  11. import { Teleport, TeleportProps } from './components/Teleport'
  12. import { Suspense, SuspenseProps } from './components/Suspense'
  13. import { isObject, isArray } from '@vue/shared'
  14. import { RawSlots } from './componentSlots'
  15. import {
  16. FunctionalComponent,
  17. Component,
  18. ComponentOptions,
  19. ConcreteComponent
  20. } from './component'
  21. import { EmitsOptions } from './componentEmits'
  22. import { DefineComponent } from './apiDefineComponent'
  23. // `h` is a more user-friendly version of `createVNode` that allows omitting the
  24. // props when possible. It is intended for manually written render functions.
  25. // Compiler-generated code uses `createVNode` because
  26. // 1. it is monomorphic and avoids the extra call overhead
  27. // 2. it allows specifying patchFlags for optimization
  28. /*
  29. // type only
  30. h('div')
  31. // type + props
  32. h('div', {})
  33. // type + omit props + children
  34. // Omit props does NOT support named slots
  35. h('div', []) // array
  36. h('div', 'foo') // text
  37. h('div', h('br')) // vnode
  38. h(Component, () => {}) // default slot
  39. // type + props + children
  40. h('div', {}, []) // array
  41. h('div', {}, 'foo') // text
  42. h('div', {}, h('br')) // vnode
  43. h(Component, {}, () => {}) // default slot
  44. h(Component, {}, {}) // named slots
  45. // named slots without props requires explicit `null` to avoid ambiguity
  46. h(Component, null, {})
  47. **/
  48. type RawProps = VNodeProps & {
  49. // used to differ from a single VNode object as children
  50. __v_isVNode?: never
  51. // used to differ from Array children
  52. [Symbol.iterator]?: never
  53. } & Record<string, any>
  54. type RawChildren =
  55. | string
  56. | number
  57. | boolean
  58. | VNode
  59. | VNodeArrayChildren
  60. | (() => any)
  61. // fake constructor type returned from `defineComponent`
  62. interface Constructor<P = any> {
  63. __isFragment?: never
  64. __isTeleport?: never
  65. __isSuspense?: never
  66. new (...args: any[]): { $props: P }
  67. }
  68. // The following is a series of overloads for providing props validation of
  69. // manually written render functions.
  70. // element
  71. export function h(type: string, children?: RawChildren): VNode
  72. export function h(
  73. type: string,
  74. props?: RawProps | null,
  75. children?: RawChildren | RawSlots
  76. ): VNode
  77. // text/comment
  78. export function h(
  79. type: typeof Text | typeof Comment,
  80. children?: string | number | boolean
  81. ): VNode
  82. export function h(
  83. type: typeof Text | typeof Comment,
  84. props?: null,
  85. children?: string | number | boolean
  86. ): VNode
  87. // fragment
  88. export function h(type: typeof Fragment, children?: VNodeArrayChildren): VNode
  89. export function h(
  90. type: typeof Fragment,
  91. props?: RawProps | null,
  92. children?: VNodeArrayChildren
  93. ): VNode
  94. // teleport (target prop is required)
  95. export function h(
  96. type: typeof Teleport,
  97. props: RawProps & TeleportProps,
  98. children: RawChildren
  99. ): VNode
  100. // suspense
  101. export function h(type: typeof Suspense, children?: RawChildren): VNode
  102. export function h(
  103. type: typeof Suspense,
  104. props?: (RawProps & SuspenseProps) | null,
  105. children?: RawChildren | RawSlots
  106. ): VNode
  107. // functional component
  108. export function h<P, E extends EmitsOptions = {}>(
  109. type: FunctionalComponent<P, E>,
  110. props?: (RawProps & P) | ({} extends P ? null : never),
  111. children?: RawChildren | RawSlots
  112. ): VNode
  113. // catch-all for generic component types
  114. export function h(type: Component, children?: RawChildren): VNode
  115. // concrete component
  116. export function h<P>(
  117. type: ConcreteComponent | string,
  118. children?: RawChildren
  119. ): VNode
  120. export function h<P>(
  121. type: ConcreteComponent<P> | string,
  122. props?: (RawProps & P) | ({} extends P ? null : never),
  123. children?: RawChildren
  124. ): VNode
  125. // component without props
  126. export function h(
  127. type: Component,
  128. props: null,
  129. children?: RawChildren | RawSlots
  130. ): VNode
  131. // exclude `defineComponent` constructors
  132. export function h<P>(
  133. type: ComponentOptions<P>,
  134. props?: (RawProps & P) | ({} extends P ? null : never),
  135. children?: RawChildren | RawSlots
  136. ): VNode
  137. // fake constructor type returned by `defineComponent` or class component
  138. export function h(type: Constructor, children?: RawChildren): VNode
  139. export function h<P>(
  140. type: Constructor<P>,
  141. props?: (RawProps & P) | ({} extends P ? null : never),
  142. children?: RawChildren | RawSlots
  143. ): VNode
  144. // fake constructor type returned by `defineComponent`
  145. export function h(type: DefineComponent, children?: RawChildren): VNode
  146. export function h<P>(
  147. type: DefineComponent<P>,
  148. props?: (RawProps & P) | ({} extends P ? null : never),
  149. children?: RawChildren | RawSlots
  150. ): VNode
  151. // Actual implementation
  152. export function h(type: any, propsOrChildren?: any, children?: any): VNode {
  153. const l = arguments.length
  154. if (l === 2) {
  155. if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
  156. // single vnode without props
  157. if (isVNode(propsOrChildren)) {
  158. return createVNode(type, null, [propsOrChildren])
  159. }
  160. // props without children
  161. return createVNode(type, propsOrChildren)
  162. } else {
  163. // omit props
  164. return createVNode(type, null, propsOrChildren)
  165. }
  166. } else {
  167. if (l > 3) {
  168. children = Array.prototype.slice.call(arguments, 2)
  169. } else if (l === 3 && isVNode(children)) {
  170. children = [children]
  171. }
  172. return createVNode(type, propsOrChildren, children)
  173. }
  174. }