test(ssr): test for rendering components
This commit is contained in:
parent
6e06810add
commit
8cdaf28515
@ -64,7 +64,12 @@ export interface ComponentOptionsBase<
|
|||||||
// type.
|
// type.
|
||||||
render?: Function
|
render?: Function
|
||||||
// SSR only. This is produced by compiler-ssr and attached in compiler-sfc
|
// SSR only. This is produced by compiler-ssr and attached in compiler-sfc
|
||||||
ssrRender?: Function
|
// not user facing, so the typing is lax and for test only.
|
||||||
|
ssrRender?: (
|
||||||
|
ctx: any,
|
||||||
|
push: (item: any) => void,
|
||||||
|
parentInstance: ComponentInternalInstance
|
||||||
|
) => void
|
||||||
components?: Record<
|
components?: Record<
|
||||||
string,
|
string,
|
||||||
Component | { new (): ComponentPublicInstance<any, any, any, any, any> }
|
Component | { new (): ComponentPublicInstance<any, any, any, any, any> }
|
||||||
|
@ -1,7 +1,261 @@
|
|||||||
// import { renderToString, renderComponent } from '../src'
|
import { createApp, h } from 'vue'
|
||||||
|
import { renderToString, renderComponent, renderSlot } from '../src'
|
||||||
|
|
||||||
describe('ssr: renderToString', () => {
|
describe('ssr: renderToString', () => {
|
||||||
describe('elements', () => {
|
describe('components', () => {
|
||||||
|
test('vnode components', async () => {
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
data() {
|
||||||
|
return { msg: 'hello' }
|
||||||
|
},
|
||||||
|
render(this: any) {
|
||||||
|
return h('div', this.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>hello</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('optimized components', async () => {
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
data() {
|
||||||
|
return { msg: 'hello' }
|
||||||
|
},
|
||||||
|
ssrRender(ctx, push) {
|
||||||
|
push(`<div>${ctx.msg}</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>hello</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested vnode components', async () => {
|
||||||
|
const Child = {
|
||||||
|
props: ['msg'],
|
||||||
|
render(this: any) {
|
||||||
|
return h('div', this.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return h('div', ['parent', h(Child, { msg: 'hello' })])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>parent<div>hello</div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested optimized components', async () => {
|
||||||
|
const Child = {
|
||||||
|
props: ['msg'],
|
||||||
|
ssrRender(ctx: any, push: any) {
|
||||||
|
push(`<div>${ctx.msg}</div>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
ssrRender(_ctx, push, parent) {
|
||||||
|
push(`<div>parent`)
|
||||||
|
push(renderComponent(Child, { msg: 'hello' }, null, parent))
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>parent<div>hello</div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixing optimized / vnode components', async () => {
|
||||||
|
const OptimizedChild = {
|
||||||
|
props: ['msg'],
|
||||||
|
ssrRender(ctx: any, push: any) {
|
||||||
|
push(`<div>${ctx.msg}</div>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VNodeChild = {
|
||||||
|
props: ['msg'],
|
||||||
|
render(this: any) {
|
||||||
|
return h('div', this.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
ssrRender(_ctx, push, parent) {
|
||||||
|
push(`<div>parent`)
|
||||||
|
push(
|
||||||
|
renderComponent(OptimizedChild, { msg: 'opt' }, null, parent)
|
||||||
|
)
|
||||||
|
push(renderComponent(VNodeChild, { msg: 'vnode' }, null, parent))
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>parent<div>opt</div><div>vnode</div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested components with optimized slots', async () => {
|
||||||
|
const Child = {
|
||||||
|
props: ['msg'],
|
||||||
|
ssrRender(ctx: any, push: any, parent: any) {
|
||||||
|
push(`<div class="child">`)
|
||||||
|
renderSlot(ctx.$slots.default, { msg: 'from slot' }, push, parent)
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
ssrRender(_ctx, push, parent) {
|
||||||
|
push(`<div>parent`)
|
||||||
|
push(
|
||||||
|
renderComponent(
|
||||||
|
Child,
|
||||||
|
{ msg: 'hello' },
|
||||||
|
{
|
||||||
|
// optimized slot using string push
|
||||||
|
default: ({ msg }: any, push: any) => {
|
||||||
|
push(`<span>${msg}</span>`)
|
||||||
|
},
|
||||||
|
_compiled: true // important to avoid slots being normalized
|
||||||
|
},
|
||||||
|
parent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(
|
||||||
|
`<div>parent<div class="child">` +
|
||||||
|
`<!----><span>from slot</span><!---->` +
|
||||||
|
`</div></div>`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested components with vnode slots', async () => {
|
||||||
|
const Child = {
|
||||||
|
props: ['msg'],
|
||||||
|
ssrRender(ctx: any, push: any, parent: any) {
|
||||||
|
push(`<div class="child">`)
|
||||||
|
renderSlot(ctx.$slots.default, { msg: 'from slot' }, push, parent)
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
ssrRender(_ctx, push, parent) {
|
||||||
|
push(`<div>parent`)
|
||||||
|
push(
|
||||||
|
renderComponent(
|
||||||
|
Child,
|
||||||
|
{ msg: 'hello' },
|
||||||
|
{
|
||||||
|
// bailed slots returning raw vnodes
|
||||||
|
default: ({ msg }: any) => {
|
||||||
|
return h('span', msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(
|
||||||
|
`<div>parent<div class="child">` +
|
||||||
|
`<!----><span>from slot</span><!---->` +
|
||||||
|
`</div></div>`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('async components', async () => {
|
||||||
|
const Child = {
|
||||||
|
// should wait for resovled render context from setup()
|
||||||
|
async setup() {
|
||||||
|
return {
|
||||||
|
msg: 'hello'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ssrRender(ctx: any, push: any) {
|
||||||
|
push(`<div>${ctx.msg}</div>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
ssrRender(_ctx, push, parent) {
|
||||||
|
push(`<div>parent`)
|
||||||
|
push(renderComponent(Child, null, null, parent))
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>parent<div>hello</div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parallel async components', async () => {
|
||||||
|
const OptimizedChild = {
|
||||||
|
props: ['msg'],
|
||||||
|
async setup(props: any) {
|
||||||
|
return {
|
||||||
|
localMsg: props.msg + '!'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ssrRender(ctx: any, push: any) {
|
||||||
|
push(`<div>${ctx.localMsg}</div>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VNodeChild = {
|
||||||
|
props: ['msg'],
|
||||||
|
async setup(props: any) {
|
||||||
|
return {
|
||||||
|
localMsg: props.msg + '!'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render(this: any) {
|
||||||
|
return h('div', this.localMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
ssrRender(_ctx, push, parent) {
|
||||||
|
push(`<div>parent`)
|
||||||
|
push(
|
||||||
|
renderComponent(OptimizedChild, { msg: 'opt' }, null, parent)
|
||||||
|
)
|
||||||
|
push(renderComponent(VNodeChild, { msg: 'vnode' }, null, parent))
|
||||||
|
push(`</div>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<div>parent<div>opt!</div><div>vnode!</div></div>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('scopeId', () => {
|
||||||
|
// TODO
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('vnode', () => {
|
||||||
test('text children', () => {})
|
test('text children', () => {})
|
||||||
|
|
||||||
test('array children', () => {})
|
test('array children', () => {})
|
||||||
@ -14,18 +268,4 @@ describe('ssr: renderToString', () => {
|
|||||||
|
|
||||||
test('textarea value', () => {})
|
test('textarea value', () => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('components', () => {
|
|
||||||
test('nested components', () => {})
|
|
||||||
|
|
||||||
test('nested components with optimized slots', () => {})
|
|
||||||
|
|
||||||
test('mixing optimized / vnode components', () => {})
|
|
||||||
|
|
||||||
test('nested components with vnode slots', () => {})
|
|
||||||
|
|
||||||
test('async components', () => {})
|
|
||||||
|
|
||||||
test('parallel async components', () => {})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -125,7 +125,7 @@ function renderComponentSubTree(
|
|||||||
} else {
|
} else {
|
||||||
if (comp.ssrRender) {
|
if (comp.ssrRender) {
|
||||||
// optimized
|
// optimized
|
||||||
comp.ssrRender(push, instance.proxy)
|
comp.ssrRender(instance.proxy, push, instance)
|
||||||
} else if (comp.render) {
|
} else if (comp.render) {
|
||||||
renderVNode(push, renderComponentRoot(instance), instance)
|
renderVNode(push, renderComponentRoot(instance), instance)
|
||||||
} else {
|
} else {
|
||||||
@ -260,8 +260,8 @@ export function renderSlot(
|
|||||||
) {
|
) {
|
||||||
// template-compiled slots are always rendered as fragments
|
// template-compiled slots are always rendered as fragments
|
||||||
push(`<!---->`)
|
push(`<!---->`)
|
||||||
if (slotFn.length > 2) {
|
if (slotFn.length > 1) {
|
||||||
// only ssr-optimized slot fns accept 3 arguments
|
// only ssr-optimized slot fns accept more than 1 arguments
|
||||||
slotFn(slotProps, push, parentComponent)
|
slotFn(slotProps, push, parentComponent)
|
||||||
} else {
|
} else {
|
||||||
// normal slot
|
// normal slot
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"types": ["jest", "puppeteer", "node"],
|
"types": ["jest", "puppeteer", "node"],
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@vue/*": ["packages/*/src"]
|
"@vue/*": ["packages/*/src"],
|
||||||
|
"vue": ["packages/vue/src"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
Loading…
Reference in New Issue
Block a user