20e54f4c7e6775f7b1b426485770e16483b7672497dd0249a0dd9ad6481021a39a4257dc3915be432da5ba0c7e0bb7e4eab33a9c105f86adee91646f05aa23 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /* @flow */
  2. import { def } from 'core/util/lang'
  3. import { normalizeChildren } from 'core/vdom/helpers/normalize-children'
  4. import { emptyObject } from 'shared/util'
  5. export function normalizeScopedSlots (
  6. slots: { [key: string]: Function } | void,
  7. normalSlots: { [key: string]: Array<VNode> },
  8. prevSlots?: { [key: string]: Function } | void
  9. ): any {
  10. let res
  11. const hasNormalSlots = Object.keys(normalSlots).length > 0
  12. const isStable = slots ? !!slots.$stable : !hasNormalSlots
  13. const key = slots && slots.$key
  14. if (!slots) {
  15. res = {}
  16. } else if (slots._normalized) {
  17. // fast path 1: child component re-render only, parent did not change
  18. return slots._normalized
  19. } else if (
  20. isStable &&
  21. prevSlots &&
  22. prevSlots !== emptyObject &&
  23. key === prevSlots.$key &&
  24. !hasNormalSlots &&
  25. !prevSlots.$hasNormal
  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. def(res, '$hasNormal', hasNormalSlots)
  52. return res
  53. }
  54. function 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 && (
  61. res.length === 0 ||
  62. (res.length === 1 && res[0].isComment) // #9658
  63. ) ? undefined
  64. : res
  65. }
  66. // this is a slot using the new v-slot syntax without scope. although it is
  67. // compiled as a scoped slot, render fn users would expect it to be present
  68. // on this.$slots because the usage is semantically a normal slot.
  69. if (fn.proxy) {
  70. Object.defineProperty(normalSlots, key, {
  71. get: normalized,
  72. enumerable: true,
  73. configurable: true
  74. })
  75. }
  76. return normalized
  77. }
  78. function proxyNormalSlot(slots, key) {
  79. return () => slots[key]
  80. }