-
-
Notifications
You must be signed in to change notification settings - Fork 33.6k
/
create-functional-component.js
118 lines (105 loc) · 3.13 KB
/
create-functional-component.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/* @flow */
import VNode from './vnode'
import { createElement } from './create-element'
import { resolveInject } from '../instance/inject'
import { normalizeChildren } from '../vdom/helpers/normalize-children'
import { resolveSlots } from '../instance/render-helpers/resolve-slots'
import { installRenderHelpers } from '../instance/render-helpers/index'
import {
isDef,
isTrue,
camelize,
emptyObject,
validateProp
} from '../util/index'
function FunctionalRenderContext (
data,
props,
children,
parent,
Ctor
) {
const options = Ctor.options
this.data = data
this.props = props
this.children = children
this.parent = parent
this.listeners = data.on || emptyObject
this.injections = resolveInject(options.inject, parent)
this.slots = () => resolveSlots(children, parent)
// ensure the createElement function in functional components
// gets a unique context - this is necessary for correct named slot check
const contextVm = Object.create(parent)
const isCompiled = isTrue(options._compiled)
const needNormalization = !isCompiled
// support for compiled functional template
if (isCompiled) {
// exposing $options for renderStatic()
this.$options = options
// pre-resolve slots for renderSlot()
this.$slots = this.slots()
this.$scopedSlots = data.scopedSlots || emptyObject
}
if (options._scopeId) {
this._c = (a, b, c, d) => {
const vnode = createElement(contextVm, a, b, c, d, needNormalization)
if (vnode && !Array.isArray(vnode)) {
vnode.fnScopeId = options._scopeId
vnode.fnContext = parent
}
return vnode
}
} else {
this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization)
}
}
installRenderHelpers(FunctionalRenderContext.prototype)
export function createFunctionalComponent (
Ctor: Class<Component>,
propsData: ?Object,
data: VNodeData,
contextVm: Component,
children: ?Array<VNode>
): VNode | Array<VNode> | void {
const options = Ctor.options
const props = {}
const propOptions = options.props
if (isDef(propOptions)) {
for (const key in propOptions) {
props[key] = validateProp(key, propOptions, propsData || emptyObject)
}
} else {
if (isDef(data.attrs)) mergeProps(props, data.attrs)
if (isDef(data.props)) mergeProps(props, data.props)
}
const renderContext = new FunctionalRenderContext(
data,
props,
children,
contextVm,
Ctor
)
const vnode = options.render.call(null, renderContext._c, renderContext)
if (vnode instanceof VNode) {
setFunctionalContextForVNode(vnode, data, contextVm, options)
return vnode
} else if (Array.isArray(vnode)) {
const vnodes = normalizeChildren(vnode) || []
for (let i = 0; i < vnodes.length; i++) {
setFunctionalContextForVNode(vnodes[i], data, contextVm, options)
}
return vnodes
}
}
function setFunctionalContextForVNode (vnode, data, vm, options) {
vnode.fnContext = vm
vnode.fnOptions = options
if (data.slot) {
(vnode.data || (vnode.data = {})).slot = data.slot
}
}
function mergeProps (to, from) {
for (const key in from) {
to[camelize(key)] = from[key]
}
}