Skip to content

Commit

Permalink
feat(core): expose all slots on $scopedSlots as functions
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
yyx990803 committed Jan 9, 2019
1 parent 7988a55 commit 5d52262
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 9 deletions.
7 changes: 4 additions & 3 deletions src/core/instance/render-helpers/resolve-slots.js
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/core/instance/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/core/vdom/create-functional-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
20 changes: 16 additions & 4 deletions src/core/vdom/helpers/normalize-scoped-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
18 changes: 18 additions & 0 deletions test/unit/features/component/component-scoped-slot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down

0 comments on commit 5d52262

Please sign in to comment.