create-element.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /* @flow */
  2. import config from '../config'
  3. import VNode, { createEmptyVNode } from './vnode'
  4. import { createComponent } from './create-component'
  5. import {
  6. warn,
  7. isDef,
  8. isUndef,
  9. isTrue,
  10. isPrimitive,
  11. resolveAsset
  12. } from '../util/index'
  13. import {
  14. normalizeChildren,
  15. simpleNormalizeChildren
  16. } from './helpers/index'
  17. const SIMPLE_NORMALIZE = 1
  18. const ALWAYS_NORMALIZE = 2
  19. // wrapper function for providing a more flexible interface
  20. // without getting yelled at by flow
  21. export function createElement (
  22. context: Component,
  23. tag: any,
  24. data: any,
  25. children: any,
  26. normalizationType: any,
  27. alwaysNormalize: boolean
  28. ): VNode {
  29. if (Array.isArray(data) || isPrimitive(data)) {
  30. normalizationType = children
  31. children = data
  32. data = undefined
  33. }
  34. if (isTrue(alwaysNormalize)) {
  35. normalizationType = ALWAYS_NORMALIZE
  36. }
  37. return _createElement(context, tag, data, children, normalizationType)
  38. }
  39. export function _createElement (
  40. context: Component,
  41. tag?: string | Class<Component> | Function | Object,
  42. data?: VNodeData,
  43. children?: any,
  44. normalizationType?: number
  45. ): VNode {
  46. if (isDef(data) && isDef((data: any).__ob__)) {
  47. process.env.NODE_ENV !== 'production' && warn(
  48. `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
  49. 'Always create fresh vnode data objects in each render!',
  50. context
  51. )
  52. return createEmptyVNode()
  53. }
  54. if (!tag) {
  55. // in case of component :is set to falsy value
  56. return createEmptyVNode()
  57. }
  58. // support single function children as default scoped slot
  59. if (Array.isArray(children) &&
  60. typeof children[0] === 'function') {
  61. data = data || {}
  62. data.scopedSlots = { default: children[0] }
  63. children.length = 0
  64. }
  65. if (normalizationType === ALWAYS_NORMALIZE) {
  66. children = normalizeChildren(children)
  67. } else if (normalizationType === SIMPLE_NORMALIZE) {
  68. children = simpleNormalizeChildren(children)
  69. }
  70. let vnode, ns
  71. if (typeof tag === 'string') {
  72. let Ctor
  73. ns = config.getTagNamespace(tag)
  74. if (config.isReservedTag(tag)) {
  75. // platform built-in elements
  76. vnode = new VNode(
  77. config.parsePlatformTagName(tag), data, children,
  78. undefined, undefined, context
  79. )
  80. } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
  81. // component
  82. vnode = createComponent(Ctor, data, context, children, tag)
  83. } else {
  84. // unknown or unlisted namespaced elements
  85. // check at runtime because it may get assigned a namespace when its
  86. // parent normalizes children
  87. vnode = new VNode(
  88. tag, data, children,
  89. undefined, undefined, context
  90. )
  91. }
  92. } else {
  93. // direct component options / constructor
  94. vnode = createComponent(tag, data, context, children)
  95. }
  96. if (isDef(vnode)) {
  97. if (ns) applyNS(vnode, ns)
  98. return vnode
  99. } else {
  100. return createEmptyVNode()
  101. }
  102. }
  103. function applyNS (vnode, ns) {
  104. vnode.ns = ns
  105. if (vnode.tag === 'foreignObject') {
  106. // use default namespace inside foreignObject
  107. return
  108. }
  109. if (isDef(vnode.children)) {
  110. for (let i = 0, l = vnode.children.length; i < l; i++) {
  111. const child = vnode.children[i]
  112. if (isDef(child.tag) && isUndef(child.ns)) {
  113. applyNS(child, ns)
  114. }
  115. }
  116. }
  117. }