UNPKG

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