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 = () => {
|
53 | if (!this.$slots) {
|
54 | normalizeScopedSlots(
|
55 | data.scopedSlots,
|
56 | this.$slots = resolveSlots(children, parent)
|
57 | )
|
58 | }
|
59 | return this.$slots
|
60 | }
|
61 |
|
62 | Object.defineProperty(this, 'scopedSlots', ({
|
63 | enumerable: true,
|
64 | get () {
|
65 | return normalizeScopedSlots(data.scopedSlots, this.slots())
|
66 | }
|
67 | }: any))
|
68 |
|
69 |
|
70 | if (isCompiled) {
|
71 |
|
72 | this.$options = options
|
73 |
|
74 | this.$slots = this.slots()
|
75 | this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots)
|
76 | }
|
77 |
|
78 | if (options._scopeId) {
|
79 | this._c = (a, b, c, d) => {
|
80 | const vnode = createElement(contextVm, a, b, c, d, needNormalization)
|
81 | if (vnode && !Array.isArray(vnode)) {
|
82 | vnode.fnScopeId = options._scopeId
|
83 | vnode.fnContext = parent
|
84 | }
|
85 | return vnode
|
86 | }
|
87 | } else {
|
88 | this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization)
|
89 | }
|
90 | }
|
91 |
|
92 | installRenderHelpers(FunctionalRenderContext.prototype)
|
93 |
|
94 | export function createFunctionalComponent (
|
95 | Ctor: Class<Component>,
|
96 | propsData: ?Object,
|
97 | data: VNodeData,
|
98 | contextVm: Component,
|
99 | children: ?Array<VNode>
|
100 | ): VNode | Array<VNode> | void {
|
101 | const options = Ctor.options
|
102 | const props = {}
|
103 | const propOptions = options.props
|
104 | if (isDef(propOptions)) {
|
105 | for (const key in propOptions) {
|
106 | props[key] = validateProp(key, propOptions, propsData || emptyObject)
|
107 | }
|
108 | } else {
|
109 | if (isDef(data.attrs)) mergeProps(props, data.attrs)
|
110 | if (isDef(data.props)) mergeProps(props, data.props)
|
111 | }
|
112 |
|
113 | const renderContext = new FunctionalRenderContext(
|
114 | data,
|
115 | props,
|
116 | children,
|
117 | contextVm,
|
118 | Ctor
|
119 | )
|
120 |
|
121 | const vnode = options.render.call(null, renderContext._c, renderContext)
|
122 |
|
123 | if (vnode instanceof VNode) {
|
124 | return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext)
|
125 | } else if (Array.isArray(vnode)) {
|
126 | const vnodes = normalizeChildren(vnode) || []
|
127 | const res = new Array(vnodes.length)
|
128 | for (let i = 0; i < vnodes.length; i++) {
|
129 | res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext)
|
130 | }
|
131 | return res
|
132 | }
|
133 | }
|
134 |
|
135 | function cloneAndMarkFunctionalResult (vnode, data, contextVm, options, renderContext) {
|
136 |
|
137 |
|
138 |
|
139 | const clone = cloneVNode(vnode)
|
140 | clone.fnContext = contextVm
|
141 | clone.fnOptions = options
|
142 | if (process.env.NODE_ENV !== 'production') {
|
143 | (clone.devtoolsMeta = clone.devtoolsMeta || {}).renderContext = renderContext
|
144 | }
|
145 | if (data.slot) {
|
146 | (clone.data || (clone.data = {})).slot = data.slot
|
147 | }
|
148 | return clone
|
149 | }
|
150 |
|
151 | function mergeProps (to, from) {
|
152 | for (const key in from) {
|
153 | to[camelize(key)] = from[key]
|
154 | }
|
155 | }
|