@@ -3,9 +3,12 @@ import {
|
||||
render,
|
||||
nodeOps,
|
||||
serializeInner,
|
||||
renderSlot
|
||||
renderSlot,
|
||||
withScopeId,
|
||||
pushScopeId,
|
||||
popScopeId
|
||||
} from '@vue/runtime-test'
|
||||
import { setScopeId, withCtx } from '../src/componentRenderContext'
|
||||
import { withCtx } from '../src/componentRenderContext'
|
||||
|
||||
describe('scopeId runtime support', () => {
|
||||
test('should attach scopeId', () => {
|
||||
@@ -40,7 +43,7 @@ describe('scopeId runtime support', () => {
|
||||
const Child = {
|
||||
__scopeId: 'child',
|
||||
render(this: any) {
|
||||
return h('div', renderSlot(this.$slots, 'default', {}, undefined, true))
|
||||
return h('div', renderSlot(this.$slots, 'default'))
|
||||
}
|
||||
}
|
||||
const Child2 = {
|
||||
@@ -82,7 +85,13 @@ describe('scopeId runtime support', () => {
|
||||
render(this: any) {
|
||||
// <div class="wrapper"><slot/></div>
|
||||
return h('div', { class: 'wrapper' }, [
|
||||
renderSlot(this.$slots, 'default')
|
||||
renderSlot(
|
||||
this.$slots,
|
||||
'default',
|
||||
{},
|
||||
undefined,
|
||||
true /* noSlotted */
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -92,17 +101,15 @@ describe('scopeId runtime support', () => {
|
||||
render(this: any) {
|
||||
// <Wrapper><slot/></Wrapper>
|
||||
return h(Wrapper, null, {
|
||||
default: withCtx(() => [
|
||||
renderSlot(this.$slots, 'default', {}, undefined, true)
|
||||
])
|
||||
default: withCtx(() => [renderSlot(this.$slots, 'default')])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// simulate hoisted node
|
||||
setScopeId('root')
|
||||
pushScopeId('root')
|
||||
const hoisted = h('div', 'hoisted')
|
||||
setScopeId(null)
|
||||
popScopeId()
|
||||
|
||||
const Root = {
|
||||
__scopeId: 'root',
|
||||
@@ -178,3 +185,124 @@ describe('scopeId runtime support', () => {
|
||||
expect(serializeInner(root)).toBe(`<div parent></div>`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('backwards compat with <=3.0.7', () => {
|
||||
const withParentId = withScopeId('parent')
|
||||
const withChildId = withScopeId('child')
|
||||
|
||||
test('should attach scopeId', () => {
|
||||
const App = {
|
||||
__scopeId: 'parent',
|
||||
render: withParentId(() => {
|
||||
return h('div', [h('div')])
|
||||
})
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
expect(serializeInner(root)).toBe(`<div parent><div parent></div></div>`)
|
||||
})
|
||||
|
||||
test('should attach scopeId to components in parent component', () => {
|
||||
const Child = {
|
||||
__scopeId: 'child',
|
||||
render: withChildId(() => {
|
||||
return h('div')
|
||||
})
|
||||
}
|
||||
const App = {
|
||||
__scopeId: 'parent',
|
||||
render: withParentId(() => {
|
||||
return h('div', [h(Child)])
|
||||
})
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<div parent><div child parent></div></div>`
|
||||
)
|
||||
})
|
||||
|
||||
test('should work on slots', () => {
|
||||
const Child = {
|
||||
__scopeId: 'child',
|
||||
render: withChildId(function(this: any) {
|
||||
return h('div', renderSlot(this.$slots, 'default'))
|
||||
})
|
||||
}
|
||||
const withChild2Id = withScopeId('child2')
|
||||
const Child2 = {
|
||||
__scopeId: 'child2',
|
||||
render: withChild2Id(() => h('span'))
|
||||
}
|
||||
const App = {
|
||||
__scopeId: 'parent',
|
||||
render: withParentId(() => {
|
||||
return h(
|
||||
Child,
|
||||
withParentId(() => {
|
||||
return [h('div'), h(Child2)]
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
// slot content should have:
|
||||
// - scopeId from parent
|
||||
// - slotted scopeId (with `-s` postfix) from child (the tree owner)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<div child parent>` +
|
||||
`<div parent child-s></div>` +
|
||||
// component inside slot should have:
|
||||
// - scopeId from template context
|
||||
// - slotted scopeId from slot owner
|
||||
// - its own scopeId
|
||||
`<span child2 parent child-s></span>` +
|
||||
`</div>`
|
||||
)
|
||||
})
|
||||
|
||||
// #1988
|
||||
test('should inherit scopeId through nested HOCs with inheritAttrs: false', () => {
|
||||
const withParentId = withScopeId('parent')
|
||||
const App = {
|
||||
__scopeId: 'parent',
|
||||
render: withParentId(() => {
|
||||
return h(Child)
|
||||
})
|
||||
}
|
||||
|
||||
function Child() {
|
||||
return h(Child2, { class: 'foo' })
|
||||
}
|
||||
|
||||
function Child2() {
|
||||
return h('div')
|
||||
}
|
||||
Child2.inheritAttrs = false
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serializeInner(root)).toBe(`<div parent></div>`)
|
||||
})
|
||||
|
||||
test('hoisted nodes', async () => {
|
||||
pushScopeId('foobar')
|
||||
const hoisted = h('div', 'hello')
|
||||
popScopeId()
|
||||
|
||||
const App = {
|
||||
__scopeId: 'foobar',
|
||||
render: () => h('div', [hoisted])
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<div foobar><div foobar>hello</div></div>`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,10 +32,25 @@ export function setCurrentRenderingInstance(
|
||||
* Set scope id when creating hoisted vnodes.
|
||||
* @private compiler helper
|
||||
*/
|
||||
export function setScopeId(id: string | null) {
|
||||
export function pushScopeId(id: string | null) {
|
||||
currentScopeId = id
|
||||
}
|
||||
|
||||
/**
|
||||
* Technically we no longer need this after 3.0.8 but we need to keep the same
|
||||
* API for backwards compat w/ code generated by compilers.
|
||||
* @private
|
||||
*/
|
||||
export function popScopeId() {
|
||||
currentScopeId = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for backwards compat
|
||||
* @private
|
||||
*/
|
||||
export const withScopeId = (_id: string) => withCtx
|
||||
|
||||
/**
|
||||
* Wrap a slot function to memoize current rendering instance
|
||||
* @private compiler helper
|
||||
|
||||
@@ -26,7 +26,7 @@ export function renderSlot(
|
||||
// this is not a user-facing function, so the fallback is always generated by
|
||||
// the compiler and guaranteed to be a function returning an array
|
||||
fallback?: () => VNodeArrayChildren,
|
||||
hasSlotted?: boolean
|
||||
noSlotted?: boolean
|
||||
): VNode {
|
||||
let slot = slots[name]
|
||||
|
||||
@@ -54,7 +54,7 @@ export function renderSlot(
|
||||
? PatchFlags.STABLE_FRAGMENT
|
||||
: PatchFlags.BAIL
|
||||
)
|
||||
if (hasSlotted && rendered.scopeId) {
|
||||
if (!noSlotted && rendered.scopeId) {
|
||||
rendered.slotScopeIds = [rendered.scopeId + '-s']
|
||||
}
|
||||
isRenderingCompiledSlot--
|
||||
|
||||
@@ -226,8 +226,13 @@ export { HMRRuntime } from './hmr'
|
||||
// user code should avoid relying on them.
|
||||
|
||||
// For compiler generated code
|
||||
// should sync with '@vue/compiler-core/src/runtimeConstants.ts'
|
||||
export { withCtx, setScopeId } from './componentRenderContext'
|
||||
// should sync with '@vue/compiler-core/src/runtimeHelpers.ts'
|
||||
export {
|
||||
withCtx,
|
||||
pushScopeId,
|
||||
popScopeId,
|
||||
withScopeId
|
||||
} from './componentRenderContext'
|
||||
export { renderList } from './helpers/renderList'
|
||||
export { toHandlers } from './helpers/toHandlers'
|
||||
export { renderSlot } from './helpers/renderSlot'
|
||||
|
||||
Reference in New Issue
Block a user