1 |
|
2 |
|
3 | import VNode, { cloneVNode } from './vnode'
|
4 | import { createElement } from './create-element'
|
5 | import { resolveInject } from '../instance/inject'
|
6 | import { normalizeChildren } from '../vdom/helpers/normalize-children'
|
7 | import { resolveSlots } from '../instance/render-helpers/resolve-slots'
|
8 | import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
|
9 | import { installRenderHelpers } from '../instance/render-helpers/index'
|
10 |
|
11 | import {
|
12 | isDef,
|
13 | isTrue,
|
14 | hasOwn,
|
15 | camelize,
|
16 | emptyObject,
|
17 | validateProp
|
18 | } from '../util/index'
|
19 |
|
20 | export function FunctionalRenderContext (
|
21 | data: VNodeData,
|
22 | props: Object,
|
23 | children: ?Array<VNode>,
|
24 | parent: Component,
|
25 | Ctor: Class<Component>
|
26 | ) {
|
27 | const options = Ctor.options
|
28 |
|
29 |
|
30 | let contextVm
|
31 | if (hasOwn(parent, '_uid')) {
|
32 | contextVm = Object.create(parent)
|
33 |
|
34 | contextVm._original = parent
|
35 | } else {
|
36 |
|
37 |
|
38 |
|
39 | contextVm = parent
|
40 |
|
41 | parent = parent._original
|
42 | }
|
43 | const isCompiled = isTrue(options._compiled)
|
44 | const needNormalization = !isCompiled
|
45 |
|
46 | this.data = data
|
47 | this.props = props
|
48 | this.children = children
|
49 | this.parent = parent
|
50 | this.listeners = data.on || emptyObject
|
51 | this.injections = resolveInject(options.inject, parent)
|
52 | this.slots = () => resolveSlots(children, parent)
|
53 |
|
54 | Object.defineProperty(this, 'scopedSlots', ({
|
55 | enumerable: true,
|
56 | get () {
|
57 | return normalizeScopedSlots(data.scopedSlots, this.slots())
|
58 | }
|
59 | }: any))
|
60 |
|
61 |
|
62 | if (isCompiled) {
|
63 |
|
64 | this.$options = options
|
65 |
|
66 | this.$slots = this.slots()
|
67 | this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots)
|
68 | }
|
69 |
|
70 | if (options._scopeId) {
|
71 | this._c = (a, b, c, d) => {
|
72 | const vnode = createElement(contextVm, a, b, c, d, needNormalization)
|
73 | if (vnode && !Array.isArray(vnode)) {
|
74 | vnode.fnScopeId = options._scopeId
|
75 | vnode.fnContext = parent
|
76 | }
|
77 | return vnode
|
78 | }
|
79 | } else {
|
80 | this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization)
|
81 | }
|
82 | }
|
83 |
|
84 | installRenderHelpers(FunctionalRenderContext.prototype)
|
85 |
|
86 | export function createFunctionalComponent (
|
87 | Ctor: Class<Component>,
|
88 | propsData: ?Object,
|
89 | data: VNodeData,
|
90 | contextVm: Component,
|
91 | children: ?Array<VNode>
|
92 | ): VNode | Array<VNode> | void {
|
93 | const options = Ctor.options
|
94 | const props = {}
|
95 | const propOptions = options.props
|
96 | if (isDef(propOptions)) {
|
97 | for (const key in propOptions) {
|
98 | props[key] = validateProp(key, propOptions, propsData || emptyObject)
|
99 | }
|
100 | } else {
|
101 | if (isDef(data.attrs)) mergeProps(props, data.attrs)
|
102 | if (isDef(data.props)) mergeProps(props, data.props)
|
103 | }
|
104 |
|
105 | const renderContext = new FunctionalRenderContext(
|
106 | data,
|
107 | props,
|
108 | children,
|
109 | contextVm,
|
110 | Ctor
|
111 | )
|
112 |
|
113 | const vnode = options.render.call(null, renderContext._c, renderContext)
|
114 |
|
115 | if (vnode instanceof VNode) {
|
116 | return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext)
|
117 | } else if (Array.isArray(vnode)) {
|
118 | const vnodes = normalizeChildren(vnode) || []
|
119 | const res = new Array(vnodes.length)
|
120 | for (let i = 0; i < vnodes.length; i++) {
|
121 | res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext)
|
122 | }
|
123 | return res
|
124 | }
|
125 | }
|
126 |
|
127 | function cloneAndMarkFunctionalResult (vnode, data, contextVm, options, renderContext) {
|
128 |
|
129 |
|
130 |
|
131 | const clone = cloneVNode(vnode)
|
132 | clone.fnContext = contextVm
|
133 | clone.fnOptions = options
|
134 | if (process.env.NODE_ENV !== 'production') {
|
135 | (clone.devtoolsMeta = clone.devtoolsMeta || {}).renderContext = renderContext
|
136 | }
|
137 | if (data.slot) {
|
138 | (clone.data || (clone.data = {})).slot = data.slot
|
139 | }
|
140 | return clone
|
141 | }
|
142 |
|
143 | function mergeProps (to, from) {
|
144 | for (const key in from) {
|
145 | to[camelize(key)] = from[key]
|
146 | }
|
147 | }
|