UNPKG

2.34 kBJavaScriptView Raw
1/* @flow */
2
3import { def } from 'core/util/lang'
4import { normalizeChildren } from 'core/vdom/helpers/normalize-children'
5import { emptyObject } from 'shared/util'
6
7export function normalizeScopedSlots (
8 slots: { [key: string]: Function } | void,
9 normalSlots: { [key: string]: Array<VNode> },
10 prevSlots?: { [key: string]: Function } | void
11): any {
12 let res
13 const isStable = slots ? !!slots.$stable : true
14 const key = slots && slots.$key
15 if (!slots) {
16 res = {}
17 } else if (slots._normalized) {
18 // fast path 1: child component re-render only, parent did not change
19 return slots._normalized
20 } else if (
21 isStable &&
22 prevSlots &&
23 prevSlots !== emptyObject &&
24 key === prevSlots.$key &&
25 Object.keys(normalSlots).length === 0
26 ) {
27 // fast path 2: stable scoped slots w/ no normal slots to proxy,
28 // only need to normalize once
29 return prevSlots
30 } else {
31 res = {}
32 for (const key in slots) {
33 if (slots[key] && key[0] !== '$') {
34 res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
35 }
36 }
37 }
38 // expose normal slots on scopedSlots
39 for (const key in normalSlots) {
40 if (!(key in res)) {
41 res[key] = proxyNormalSlot(normalSlots, key)
42 }
43 }
44 // avoriaz seems to mock a non-extensible $scopedSlots object
45 // and when that is passed down this would cause an error
46 if (slots && Object.isExtensible(slots)) {
47 (slots: any)._normalized = res
48 }
49 def(res, '$stable', isStable)
50 def(res, '$key', key)
51 return res
52}
53
54function normalizeScopedSlot(normalSlots, key, fn) {
55 const normalized = function () {
56 let res = arguments.length ? fn.apply(null, arguments) : fn({})
57 res = res && typeof res === 'object' && !Array.isArray(res)
58 ? [res] // single vnode
59 : normalizeChildren(res)
60 return res && res.length === 0
61 ? undefined
62 : res
63 }
64 // this is a slot using the new v-slot syntax without scope. although it is
65 // compiled as a scoped slot, render fn users would expect it to be present
66 // on this.$slots because the usage is semantically a normal slot.
67 if (fn.proxy) {
68 Object.defineProperty(normalSlots, key, {
69 get: normalized,
70 enumerable: true,
71 configurable: true
72 })
73 }
74 return normalized
75}
76
77function proxyNormalSlot(slots, key) {
78 return () => slots[key]
79}