Browse Source

feat(core): expose all slots on $scopedSlots as functions

After this change, users using render functions can always make use
of slots via `this.$scopedSlots` without worrying about whether the
slot is being passed in as scoped or not.

This should also make it easier to migrate to 3.0 where all slots are
exposed as functions that return Array of VNodes on `this.$slots`.
dev
Evan You 6 years ago
parent
commit
5d52262f1c
  1. 7
      src/core/instance/render-helpers/resolve-slots.js
  2. 5
      src/core/instance/render.js
  3. 2
      src/core/vdom/create-functional-component.js
  4. 20
      src/core/vdom/helpers/normalize-scoped-slots.js
  5. 18
      test/unit/features/component/component-scoped-slot.spec.js

7
src/core/instance/render-helpers/resolve-slots.js

@ -1,6 +1,7 @@
/* @flow */
import type VNode from 'core/vdom/vnode'
import { emptyObject } from 'core/util/index'
/**
* Runtime helper for resolving raw children VNodes into a slot object.
@ -9,10 +10,10 @@ export function resolveSlots (
children: ?Array<VNode>,
context: ?Component
): { [key: string]: Array<VNode> } {
const slots = {}
if (!children) {
return slots
if (!children || !children.length) {
return emptyObject
}
const slots = {}
for (let i = 0, l = children.length; i < l; i++) {
const child = children[i]
const data = child.data

5
src/core/instance/render.js

@ -64,7 +64,10 @@ export function renderMixin (Vue: Class<Component>) {
const { render, _parentVnode } = vm.$options
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(_parentVnode.data.scopedSlots)
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots
)
}
// set parent vnode. this allows render functions to have access

2
src/core/vdom/create-functional-component.js

@ -57,7 +57,7 @@ export function FunctionalRenderContext (
this.$options = options
// pre-resolve slots for renderSlot()
this.$slots = this.slots()
this.$scopedSlots = normalizeScopedSlots(data.scopedSlots)
this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots)
}
if (options._scopeId) {

20
src/core/vdom/helpers/normalize-scoped-slots.js

@ -2,19 +2,31 @@
import { emptyObject } from 'core/util/index'
export function normalizeScopedSlots (slots: { [key: string]: Function } | void): any {
export function normalizeScopedSlots (
slots: { [key: string]: Function } | void,
normalSlots: { [key: string]: Array<VNode> }
): any {
let res
if (!slots) {
return emptyObject
if (normalSlots === emptyObject) {
return emptyObject
}
res = {}
} else if (slots._normalized) {
return slots
} else {
const res = {}
res = {}
for (const key in slots) {
res[key] = normalizeScopedSlot(slots[key])
}
res._normalized = true
return res
}
if (normalSlots !== emptyObject) {
for (const key in normalSlots) {
res[key] = () => normalSlots[key]
}
}
return res
}
function normalizeScopedSlot(fn: Function) {

18
test/unit/features/component/component-scoped-slot.spec.js

@ -455,6 +455,24 @@ describe('Component scoped slot', () => {
expect(vm.$el.outerHTML).toBe('<span>hello</span>')
})
// new in 2.6, unifying all slots as functions
it('non-scoped slots should also be available on $scopedSlots', () => {
const vm = new Vue({
template: `<foo>before <div slot="bar">{{ $slot.msg }}</div> after</foo>`,
components: {
foo: {
render(h) {
return h('div', [
this.$scopedSlots.default(),
this.$scopedSlots.bar({ msg: 'hi' })
])
}
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe(`before after<div>hi</div>`)
})
// #4779
it('should support dynamic slot target', done => {
const Child = {

Loading…
Cancel
Save