d22a76ff8d0cd26b515384a92200f9e82a0e6a8dbeb440d466908ae56c05c7b99db6e32d8a519bceb3cfebb389b38060eb9708cb115393d3d68254c3460553 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) {
  96. warn(
  97. `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
  98. context
  99. )
  100. }
  101. vnode = new VNode(
  102. config.parsePlatformTagName(tag), data, children,
  103. undefined, undefined, context
  104. )
  105. } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
  106. // component
  107. vnode = createComponent(Ctor, data, context, children, tag)
  108. } else {
  109. // unknown or unlisted namespaced elements
  110. // check at runtime because it may get assigned a namespace when its
  111. // parent normalizes children
  112. vnode = new VNode(
  113. tag, data, children,
  114. undefined, undefined, context
  115. )
  116. }
  117. } else {
  118. // direct component options / constructor
  119. vnode = createComponent(tag, data, context, children)
  120. }
  121. if (Array.isArray(vnode)) {
  122. return vnode
  123. } else if (isDef(vnode)) {
  124. if (isDef(ns)) applyNS(vnode, ns)
  125. if (isDef(data)) registerDeepBindings(data)
  126. return vnode
  127. } else {
  128. return createEmptyVNode()
  129. }
  130. }
  131. function applyNS (vnode, ns, force) {
  132. vnode.ns = ns
  133. if (vnode.tag === 'foreignObject') {
  134. // use default namespace inside foreignObject
  135. ns = undefined
  136. force = true
  137. }
  138. if (isDef(vnode.children)) {
  139. for (let i = 0, l = vnode.children.length; i < l; i++) {
  140. const child = vnode.children[i]
  141. if (isDef(child.tag) && (
  142. isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))) {
  143. applyNS(child, ns, force)
  144. }
  145. }
  146. }
  147. }
  148. // ref #5318
  149. // necessary to ensure parent re-render when deep bindings like :style and
  150. // :class are used on slot nodes
  151. function registerDeepBindings (data) {
  152. if (isObject(data.style)) {
  153. traverse(data.style)
  154. }
  155. if (isObject(data.class)) {
  156. traverse(data.class)
  157. }
  158. }