create-element.js 4.2 KB

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