wip(ssr): scopeId in slots
This commit is contained in:
parent
797cc18967
commit
7984a135ca
@ -56,8 +56,12 @@ describe('ssr: components', () => {
|
|||||||
const _component_foo = resolveComponent(\\"foo\\")
|
const _component_foo = resolveComponent(\\"foo\\")
|
||||||
|
|
||||||
_ssrRenderComponent(_component_foo, null, {
|
_ssrRenderComponent(_component_foo, null, {
|
||||||
default: (_, _push, _parent) => {
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
_push(\`hello<div></div>\`)
|
if (_scopeId) {
|
||||||
|
_push(\`hello<div \${_scopeId}></div>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`hello<div></div>\`)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_compiled: true
|
_compiled: true
|
||||||
}, _parent)
|
}, _parent)
|
||||||
@ -75,7 +79,7 @@ describe('ssr: components', () => {
|
|||||||
const _component_foo = resolveComponent(\\"foo\\")
|
const _component_foo = resolveComponent(\\"foo\\")
|
||||||
|
|
||||||
_ssrRenderComponent(_component_foo, null, {
|
_ssrRenderComponent(_component_foo, null, {
|
||||||
default: ({ msg }, _push, _parent) => {
|
default: ({ msg }, _push, _parent, _scopeId) => {
|
||||||
_push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
|
_push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
|
||||||
},
|
},
|
||||||
_compiled: true
|
_compiled: true
|
||||||
@ -98,10 +102,10 @@ describe('ssr: components', () => {
|
|||||||
const _component_foo = resolveComponent(\\"foo\\")
|
const _component_foo = resolveComponent(\\"foo\\")
|
||||||
|
|
||||||
_ssrRenderComponent(_component_foo, null, {
|
_ssrRenderComponent(_component_foo, null, {
|
||||||
default: (_, _push, _parent) => {
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
_push(\`foo\`)
|
_push(\`foo\`)
|
||||||
},
|
},
|
||||||
named: (_, _push, _parent) => {
|
named: (_, _push, _parent, _scopeId) => {
|
||||||
_push(\`bar\`)
|
_push(\`bar\`)
|
||||||
},
|
},
|
||||||
_compiled: true
|
_compiled: true
|
||||||
@ -126,7 +130,7 @@ describe('ssr: components', () => {
|
|||||||
(_ctx.ok)
|
(_ctx.ok)
|
||||||
? {
|
? {
|
||||||
name: \\"named\\",
|
name: \\"named\\",
|
||||||
fn: (_, _push, _parent) => {
|
fn: (_, _push, _parent, _scopeId) => {
|
||||||
_push(\`foo\`)
|
_push(\`foo\`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +156,7 @@ describe('ssr: components', () => {
|
|||||||
renderList(_ctx.names, (key) => {
|
renderList(_ctx.names, (key) => {
|
||||||
return {
|
return {
|
||||||
name: key,
|
name: key,
|
||||||
fn: ({ msg }, _push, _parent) => {
|
fn: ({ msg }, _push, _parent, _scopeId) => {
|
||||||
_push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
|
_push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
114
packages/compiler-ssr/__tests__/ssrScopeId.spec.ts
Normal file
114
packages/compiler-ssr/__tests__/ssrScopeId.spec.ts
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import { compile } from '../src'
|
||||||
|
|
||||||
|
const scopeId = 'data-v-xxxxxxx'
|
||||||
|
|
||||||
|
describe('ssr: scopeId', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
expect(
|
||||||
|
compile(`<div><span>hello</span></div>`, {
|
||||||
|
scopeId
|
||||||
|
}).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
_push(\`<div data-v-xxxxxxx><span data-v-xxxxxxx>hello</span></div>\`)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('inside slots (only text)', () => {
|
||||||
|
// should have no branching inside slot
|
||||||
|
expect(
|
||||||
|
compile(`<foo>foo</foo>`, {
|
||||||
|
scopeId
|
||||||
|
}).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const { resolveComponent } = require(\\"vue\\")
|
||||||
|
const { _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
const _component_foo = resolveComponent(\\"foo\\")
|
||||||
|
|
||||||
|
_ssrRenderComponent(_component_foo, null, {
|
||||||
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
|
_push(\`foo\`)
|
||||||
|
},
|
||||||
|
_compiled: true
|
||||||
|
}, _parent)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('inside slots (with elements)', () => {
|
||||||
|
expect(
|
||||||
|
compile(`<foo><span>hello</span></foo>`, {
|
||||||
|
scopeId
|
||||||
|
}).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const { resolveComponent } = require(\\"vue\\")
|
||||||
|
const { _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
const _component_foo = resolveComponent(\\"foo\\")
|
||||||
|
|
||||||
|
_ssrRenderComponent(_component_foo, null, {
|
||||||
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
|
if (_scopeId) {
|
||||||
|
_push(\`<span data-v-xxxxxxx \${_scopeId}>hello</span>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<span data-v-xxxxxxx>hello</span>\`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_compiled: true
|
||||||
|
}, _parent)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested slots', () => {
|
||||||
|
expect(
|
||||||
|
compile(`<foo><span>hello</span><bar><span/></bar></foo>`, {
|
||||||
|
scopeId
|
||||||
|
}).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const { resolveComponent } = require(\\"vue\\")
|
||||||
|
const { _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
const _component_bar = resolveComponent(\\"bar\\")
|
||||||
|
const _component_foo = resolveComponent(\\"foo\\")
|
||||||
|
|
||||||
|
_ssrRenderComponent(_component_foo, null, {
|
||||||
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
|
if (_scopeId) {
|
||||||
|
_push(\`<span data-v-xxxxxxx \${_scopeId}>hello</span>\`)
|
||||||
|
_ssrRenderComponent(_component_bar, null, {
|
||||||
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
|
if (_scopeId) {
|
||||||
|
_push(\`<span data-v-xxxxxxx \${_scopeId}></span>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<span data-v-xxxxxxx></span>\`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_compiled: true
|
||||||
|
}, _parent)
|
||||||
|
} else {
|
||||||
|
_push(\`<span data-v-xxxxxxx>hello</span>\`)
|
||||||
|
_ssrRenderComponent(_component_bar, null, {
|
||||||
|
default: (_, _push, _parent, _scopeId) => {
|
||||||
|
if (_scopeId) {
|
||||||
|
_push(\`<span data-v-xxxxxxx \${_scopeId}></span>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<span data-v-xxxxxxx></span>\`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_compiled: true
|
||||||
|
}, _parent)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_compiled: true
|
||||||
|
}, _parent)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
@ -13,12 +13,13 @@ import {
|
|||||||
IfStatement,
|
IfStatement,
|
||||||
CallExpression
|
CallExpression
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { isString, escapeHtml, NO } from '@vue/shared'
|
import { isString, escapeHtml } from '@vue/shared'
|
||||||
import { SSR_INTERPOLATE, ssrHelpers } from './runtimeHelpers'
|
import { SSR_INTERPOLATE, ssrHelpers } from './runtimeHelpers'
|
||||||
import { ssrProcessIf } from './transforms/ssrVIf'
|
import { ssrProcessIf } from './transforms/ssrVIf'
|
||||||
import { ssrProcessFor } from './transforms/ssrVFor'
|
import { ssrProcessFor } from './transforms/ssrVFor'
|
||||||
import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet'
|
import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet'
|
||||||
import { ssrProcessComponent } from './transforms/ssrTransformComponent'
|
import { ssrProcessComponent } from './transforms/ssrTransformComponent'
|
||||||
|
import { ssrProcessElement } from './transforms/ssrTransformElement'
|
||||||
|
|
||||||
// Because SSR codegen output is completely different from client-side output
|
// Because SSR codegen output is completely different from client-side output
|
||||||
// (e.g. multiple elements can be concatenated into a single template literal
|
// (e.g. multiple elements can be concatenated into a single template literal
|
||||||
@ -29,7 +30,7 @@ import { ssrProcessComponent } from './transforms/ssrTransformComponent'
|
|||||||
export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
||||||
const context = createSSRTransformContext(options)
|
const context = createSSRTransformContext(options)
|
||||||
const isFragment =
|
const isFragment =
|
||||||
ast.children.length > 1 && !ast.children.every(c => isText(c))
|
ast.children.length > 1 && ast.children.some(c => !isText(c))
|
||||||
processChildren(ast.children, context, isFragment)
|
processChildren(ast.children, context, isFragment)
|
||||||
ast.codegenNode = createBlockStatement(context.body)
|
ast.codegenNode = createBlockStatement(context.body)
|
||||||
|
|
||||||
@ -46,7 +47,8 @@ export type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
|||||||
|
|
||||||
function createSSRTransformContext(
|
function createSSRTransformContext(
|
||||||
options: CompilerOptions,
|
options: CompilerOptions,
|
||||||
helpers: Set<symbol> = new Set()
|
helpers: Set<symbol> = new Set(),
|
||||||
|
withSlotScopeId = false
|
||||||
) {
|
) {
|
||||||
const body: BlockStatement['body'] = []
|
const body: BlockStatement['body'] = []
|
||||||
let currentString: TemplateLiteral | null = null
|
let currentString: TemplateLiteral | null = null
|
||||||
@ -55,6 +57,7 @@ function createSSRTransformContext(
|
|||||||
options,
|
options,
|
||||||
body,
|
body,
|
||||||
helpers,
|
helpers,
|
||||||
|
withSlotScopeId,
|
||||||
helper<T extends symbol>(name: T): T {
|
helper<T extends symbol>(name: T): T {
|
||||||
helpers.add(name)
|
helpers.add(name)
|
||||||
return name
|
return name
|
||||||
@ -82,11 +85,16 @@ function createSSRTransformContext(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createChildContext(
|
function createChildContext(
|
||||||
parent: SSRTransformContext
|
parent: SSRTransformContext,
|
||||||
|
withSlotScopeId = parent.withSlotScopeId
|
||||||
): SSRTransformContext {
|
): SSRTransformContext {
|
||||||
// ensure child inherits parent helpers
|
// ensure child inherits parent helpers
|
||||||
return createSSRTransformContext(parent.options, parent.helpers)
|
return createSSRTransformContext(
|
||||||
|
parent.options,
|
||||||
|
parent.helpers,
|
||||||
|
withSlotScopeId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function processChildren(
|
export function processChildren(
|
||||||
@ -97,23 +105,11 @@ export function processChildren(
|
|||||||
if (asFragment) {
|
if (asFragment) {
|
||||||
context.pushStringPart(`<!---->`)
|
context.pushStringPart(`<!---->`)
|
||||||
}
|
}
|
||||||
const isVoidTag = context.options.isVoidTag || NO
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
const child = children[i]
|
const child = children[i]
|
||||||
if (child.type === NodeTypes.ELEMENT) {
|
if (child.type === NodeTypes.ELEMENT) {
|
||||||
if (child.tagType === ElementTypes.ELEMENT) {
|
if (child.tagType === ElementTypes.ELEMENT) {
|
||||||
const elementsToAdd = child.ssrCodegenNode!.elements
|
ssrProcessElement(child, context)
|
||||||
for (let j = 0; j < elementsToAdd.length; j++) {
|
|
||||||
context.pushStringPart(elementsToAdd[j])
|
|
||||||
}
|
|
||||||
if (child.children.length) {
|
|
||||||
processChildren(child.children, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isVoidTag(child.tag)) {
|
|
||||||
// push closing tag
|
|
||||||
context.pushStringPart(`</${child.tag}>`)
|
|
||||||
}
|
|
||||||
} else if (child.tagType === ElementTypes.COMPONENT) {
|
} else if (child.tagType === ElementTypes.COMPONENT) {
|
||||||
ssrProcessComponent(child, context)
|
ssrProcessComponent(child, context)
|
||||||
} else if (child.tagType === ElementTypes.SLOT) {
|
} else if (child.tagType === ElementTypes.SLOT) {
|
||||||
@ -135,3 +131,14 @@ export function processChildren(
|
|||||||
context.pushStringPart(`<!---->`)
|
context.pushStringPart(`<!---->`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function processChildrenAsStatement(
|
||||||
|
children: TemplateChildNode[],
|
||||||
|
parentContext: SSRTransformContext,
|
||||||
|
asFragment = false,
|
||||||
|
withSlotScopeId = parentContext.withSlotScopeId
|
||||||
|
): BlockStatement {
|
||||||
|
const childContext = createChildContext(parentContext, withSlotScopeId)
|
||||||
|
processChildren(children, childContext, asFragment)
|
||||||
|
return createBlockStatement(childContext.body)
|
||||||
|
}
|
||||||
|
@ -14,13 +14,16 @@ import {
|
|||||||
TemplateChildNode,
|
TemplateChildNode,
|
||||||
PORTAL,
|
PORTAL,
|
||||||
SUSPENSE,
|
SUSPENSE,
|
||||||
TRANSITION_GROUP
|
TRANSITION_GROUP,
|
||||||
|
createIfStatement,
|
||||||
|
createSimpleExpression,
|
||||||
|
isText
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { SSR_RENDER_COMPONENT } from '../runtimeHelpers'
|
import { SSR_RENDER_COMPONENT } from '../runtimeHelpers'
|
||||||
import {
|
import {
|
||||||
SSRTransformContext,
|
SSRTransformContext,
|
||||||
createChildContext,
|
processChildren,
|
||||||
processChildren
|
processChildrenAsStatement
|
||||||
} from '../ssrCodegenTransform'
|
} from '../ssrCodegenTransform'
|
||||||
import { isSymbol } from '@vue/shared'
|
import { isSymbol } from '@vue/shared'
|
||||||
|
|
||||||
@ -62,10 +65,10 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||||||
|
|
||||||
const buildSSRSlotFn: SlotFnBuilder = (props, children, loc) => {
|
const buildSSRSlotFn: SlotFnBuilder = (props, children, loc) => {
|
||||||
// An SSR slot function has the signature of
|
// An SSR slot function has the signature of
|
||||||
// (props, _push, _parent) => void
|
// (props, _push, _parent, _scopeId) => void
|
||||||
// See server-renderer/src/helpers/renderSlot.ts
|
// See server-renderer/src/helpers/renderSlot.ts
|
||||||
const fn = createFunctionExpression(
|
const fn = createFunctionExpression(
|
||||||
[props || `_`, `_push`, `_parent`],
|
[props || `_`, `_push`, `_parent`, `_scopeId`],
|
||||||
undefined, // no return, assign body later
|
undefined, // no return, assign body later
|
||||||
true, // newline
|
true, // newline
|
||||||
false, // isSlot: pass false since we don't need client scopeId codegen
|
false, // isSlot: pass false since we don't need client scopeId codegen
|
||||||
@ -111,9 +114,24 @@ export function ssrProcessComponent(
|
|||||||
const wipEntries = wipMap.get(node) || []
|
const wipEntries = wipMap.get(node) || []
|
||||||
for (let i = 0; i < wipEntries.length; i++) {
|
for (let i = 0; i < wipEntries.length; i++) {
|
||||||
const { fn, children } = wipEntries[i]
|
const { fn, children } = wipEntries[i]
|
||||||
const childContext = createChildContext(context)
|
const hasNonTextChild = children.some(c => !isText(c))
|
||||||
processChildren(children, childContext)
|
if (hasNonTextChild) {
|
||||||
fn.body = createBlockStatement(childContext.body)
|
// SSR slots need to handled potential presence of scopeId of the child
|
||||||
|
// component. To avoid the cost of concatenation when it's unnecessary,
|
||||||
|
// we split the code into two paths, one with slot scopeId and one without.
|
||||||
|
fn.body = createBlockStatement([
|
||||||
|
createIfStatement(
|
||||||
|
createSimpleExpression(`_scopeId`, false),
|
||||||
|
// branch with scopeId concatenation
|
||||||
|
processChildrenAsStatement(children, context, false, true),
|
||||||
|
// branch without scopeId concatenation
|
||||||
|
processChildrenAsStatement(children, context, false, false)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
// only text, no need for scopeId branching.
|
||||||
|
fn.body = processChildrenAsStatement(children, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
context.pushStatement(node.ssrCodegenNode)
|
context.pushStatement(node.ssrCodegenNode)
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
MERGE_PROPS,
|
MERGE_PROPS,
|
||||||
isBindKey
|
isBindKey
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { escapeHtml, isBooleanAttr, isSSRSafeAttrName } from '@vue/shared'
|
import { escapeHtml, isBooleanAttr, isSSRSafeAttrName, NO } from '@vue/shared'
|
||||||
import { createSSRCompilerError, SSRErrorCodes } from '../errors'
|
import { createSSRCompilerError, SSRErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
SSR_RENDER_ATTR,
|
SSR_RENDER_ATTR,
|
||||||
@ -35,6 +35,14 @@ import {
|
|||||||
SSR_INTERPOLATE,
|
SSR_INTERPOLATE,
|
||||||
SSR_GET_DYNAMIC_MODEL_PROPS
|
SSR_GET_DYNAMIC_MODEL_PROPS
|
||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
|
import { SSRTransformContext, processChildren } from '../ssrCodegenTransform'
|
||||||
|
|
||||||
|
// for directives with children overwrite (e.g. v-html & v-text), we need to
|
||||||
|
// store the raw children so that they can be added in the 2nd pass.
|
||||||
|
const rawChildrenMap = new WeakMap<
|
||||||
|
PlainElementNode,
|
||||||
|
TemplateLiteral['elements'][0]
|
||||||
|
>()
|
||||||
|
|
||||||
export const ssrTransformElement: NodeTransform = (node, context) => {
|
export const ssrTransformElement: NodeTransform = (node, context) => {
|
||||||
if (
|
if (
|
||||||
@ -45,7 +53,6 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||||||
// element
|
// element
|
||||||
// generate the template literal representing the open tag.
|
// generate the template literal representing the open tag.
|
||||||
const openTag: TemplateLiteral['elements'] = [`<${node.tag}`]
|
const openTag: TemplateLiteral['elements'] = [`<${node.tag}`]
|
||||||
let rawChildren
|
|
||||||
|
|
||||||
// v-bind="obj" or v-bind:[key] can potentially overwrite other static
|
// v-bind="obj" or v-bind:[key] can potentially overwrite other static
|
||||||
// attrs and can affect final rendering result, so when they are present
|
// attrs and can affect final rendering result, so when they are present
|
||||||
@ -70,10 +77,9 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||||||
props
|
props
|
||||||
)
|
)
|
||||||
const existingText = node.children[0] as TextNode | undefined
|
const existingText = node.children[0] as TextNode | undefined
|
||||||
node.children = []
|
rawChildrenMap.set(
|
||||||
rawChildren = createCallExpression(
|
node,
|
||||||
context.helper(SSR_INTERPOLATE),
|
createCallExpression(context.helper(SSR_INTERPOLATE), [
|
||||||
[
|
|
||||||
createConditionalExpression(
|
createConditionalExpression(
|
||||||
createSimpleExpression(`"value" in ${tempId}`, false),
|
createSimpleExpression(`"value" in ${tempId}`, false),
|
||||||
createSimpleExpression(`${tempId}.value`, false),
|
createSimpleExpression(`${tempId}.value`, false),
|
||||||
@ -83,7 +89,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||||||
),
|
),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
]
|
])
|
||||||
)
|
)
|
||||||
} else if (node.tag === 'input') {
|
} else if (node.tag === 'input') {
|
||||||
// <input v-bind="obj" v-model>
|
// <input v-bind="obj" v-model>
|
||||||
@ -126,8 +132,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||||||
// special cases with children override
|
// special cases with children override
|
||||||
if (prop.type === NodeTypes.DIRECTIVE) {
|
if (prop.type === NodeTypes.DIRECTIVE) {
|
||||||
if (prop.name === 'html' && prop.exp) {
|
if (prop.name === 'html' && prop.exp) {
|
||||||
node.children = []
|
rawChildrenMap.set(node, prop.exp)
|
||||||
rawChildren = prop.exp
|
|
||||||
} else if (prop.name === 'text' && prop.exp) {
|
} else if (prop.name === 'text' && prop.exp) {
|
||||||
node.children = [createInterpolation(prop.exp, prop.loc)]
|
node.children = [createInterpolation(prop.exp, prop.loc)]
|
||||||
} else if (prop.name === 'slot') {
|
} else if (prop.name === 'slot') {
|
||||||
@ -225,8 +230,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||||||
} else {
|
} else {
|
||||||
// special case: value on <textarea>
|
// special case: value on <textarea>
|
||||||
if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
|
if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
|
||||||
node.children = []
|
rawChildrenMap.set(node, escapeHtml(prop.value.content))
|
||||||
rawChildren = escapeHtml(prop.value.content)
|
|
||||||
} else if (!hasDynamicVBind) {
|
} else if (!hasDynamicVBind) {
|
||||||
// static prop
|
// static prop
|
||||||
if (prop.name === 'class' && prop.value) {
|
if (prop.name === 'class' && prop.value) {
|
||||||
@ -250,10 +254,6 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||||||
openTag.push(` ${context.scopeId}`)
|
openTag.push(` ${context.scopeId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
openTag.push(`>`)
|
|
||||||
if (rawChildren) {
|
|
||||||
openTag.push(rawChildren)
|
|
||||||
}
|
|
||||||
node.ssrCodegenNode = createTemplateLiteral(openTag)
|
node.ssrCodegenNode = createTemplateLiteral(openTag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,3 +296,35 @@ function findVModel(node: PlainElementNode): DirectiveNode | undefined {
|
|||||||
p => p.type === NodeTypes.DIRECTIVE && p.name === 'model' && p.exp
|
p => p.type === NodeTypes.DIRECTIVE && p.name === 'model' && p.exp
|
||||||
) as DirectiveNode | undefined
|
) as DirectiveNode | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ssrProcessElement(
|
||||||
|
node: PlainElementNode,
|
||||||
|
context: SSRTransformContext
|
||||||
|
) {
|
||||||
|
const isVoidTag = context.options.isVoidTag || NO
|
||||||
|
const elementsToAdd = node.ssrCodegenNode!.elements
|
||||||
|
for (let j = 0; j < elementsToAdd.length; j++) {
|
||||||
|
context.pushStringPart(elementsToAdd[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle slot scopeId
|
||||||
|
if (context.withSlotScopeId) {
|
||||||
|
context.pushStringPart(` `)
|
||||||
|
context.pushStringPart(createSimpleExpression(`_scopeId`, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// close open tag
|
||||||
|
context.pushStringPart(`>`)
|
||||||
|
|
||||||
|
const rawChildren = rawChildrenMap.get(node)
|
||||||
|
if (rawChildren) {
|
||||||
|
context.pushStringPart(rawChildren)
|
||||||
|
} else if (node.children.length) {
|
||||||
|
processChildren(node.children, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVoidTag(node.tag)) {
|
||||||
|
// push closing tag
|
||||||
|
context.pushStringPart(`</${node.tag}>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,14 +4,12 @@ import {
|
|||||||
processSlotOutlet,
|
processSlotOutlet,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
SlotOutletNode,
|
SlotOutletNode,
|
||||||
createFunctionExpression,
|
createFunctionExpression
|
||||||
createBlockStatement
|
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { SSR_RENDER_SLOT } from '../runtimeHelpers'
|
import { SSR_RENDER_SLOT } from '../runtimeHelpers'
|
||||||
import {
|
import {
|
||||||
SSRTransformContext,
|
SSRTransformContext,
|
||||||
createChildContext,
|
processChildrenAsStatement
|
||||||
processChildren
|
|
||||||
} from '../ssrCodegenTransform'
|
} from '../ssrCodegenTransform'
|
||||||
|
|
||||||
export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
|
export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
|
||||||
@ -38,10 +36,8 @@ export function ssrProcessSlotOutlet(
|
|||||||
const renderCall = node.ssrCodegenNode!
|
const renderCall = node.ssrCodegenNode!
|
||||||
// has fallback content
|
// has fallback content
|
||||||
if (node.children.length) {
|
if (node.children.length) {
|
||||||
const childContext = createChildContext(context)
|
|
||||||
processChildren(node.children, childContext)
|
|
||||||
const fallbackRenderFn = createFunctionExpression([])
|
const fallbackRenderFn = createFunctionExpression([])
|
||||||
fallbackRenderFn.body = createBlockStatement(childContext.body)
|
fallbackRenderFn.body = processChildrenAsStatement(node.children, context)
|
||||||
// _renderSlot(slots, name, props, fallback, ...)
|
// _renderSlot(slots, name, props, fallback, ...)
|
||||||
renderCall.arguments[3] = fallbackRenderFn
|
renderCall.arguments[3] = fallbackRenderFn
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,11 @@ import {
|
|||||||
createCallExpression,
|
createCallExpression,
|
||||||
createFunctionExpression,
|
createFunctionExpression,
|
||||||
createForLoopParams,
|
createForLoopParams,
|
||||||
createBlockStatement,
|
|
||||||
NodeTypes
|
NodeTypes
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import {
|
import {
|
||||||
SSRTransformContext,
|
SSRTransformContext,
|
||||||
createChildContext,
|
processChildrenAsStatement
|
||||||
processChildren
|
|
||||||
} from '../ssrCodegenTransform'
|
} from '../ssrCodegenTransform'
|
||||||
import { SSR_RENDER_LIST } from '../runtimeHelpers'
|
import { SSR_RENDER_LIST } from '../runtimeHelpers'
|
||||||
|
|
||||||
@ -24,14 +22,16 @@ export const ssrTransformFor = createStructuralDirectiveTransform(
|
|||||||
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
||||||
// codegen nodes.
|
// codegen nodes.
|
||||||
export function ssrProcessFor(node: ForNode, context: SSRTransformContext) {
|
export function ssrProcessFor(node: ForNode, context: SSRTransformContext) {
|
||||||
const childContext = createChildContext(context)
|
|
||||||
const needFragmentWrapper =
|
const needFragmentWrapper =
|
||||||
node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT
|
node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT
|
||||||
processChildren(node.children, childContext, needFragmentWrapper)
|
|
||||||
const renderLoop = createFunctionExpression(
|
const renderLoop = createFunctionExpression(
|
||||||
createForLoopParams(node.parseResult)
|
createForLoopParams(node.parseResult)
|
||||||
)
|
)
|
||||||
renderLoop.body = createBlockStatement(childContext.body)
|
renderLoop.body = processChildrenAsStatement(
|
||||||
|
node.children,
|
||||||
|
context,
|
||||||
|
needFragmentWrapper
|
||||||
|
)
|
||||||
|
|
||||||
// v-for always renders a fragment
|
// v-for always renders a fragment
|
||||||
context.pushStringPart(`<!---->`)
|
context.pushStringPart(`<!---->`)
|
||||||
|
@ -11,8 +11,7 @@ import {
|
|||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import {
|
import {
|
||||||
SSRTransformContext,
|
SSRTransformContext,
|
||||||
createChildContext,
|
processChildrenAsStatement
|
||||||
processChildren
|
|
||||||
} from '../ssrCodegenTransform'
|
} from '../ssrCodegenTransform'
|
||||||
|
|
||||||
// Plugin for the first transform pass, which simply constructs the AST node
|
// Plugin for the first transform pass, which simply constructs the AST node
|
||||||
@ -63,7 +62,5 @@ function processIfBranch(
|
|||||||
(children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) &&
|
(children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) &&
|
||||||
// optimize away nested fragments when the only child is a ForNode
|
// optimize away nested fragments when the only child is a ForNode
|
||||||
!(children.length === 1 && children[0].type === NodeTypes.FOR)
|
!(children.length === 1 && children[0].type === NodeTypes.FOR)
|
||||||
const childContext = createChildContext(context)
|
return processChildrenAsStatement(children, context, needFragmentWrapper)
|
||||||
processChildren(children, childContext, needFragmentWrapper)
|
|
||||||
return createBlockStatement(childContext.body)
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ export type SSRSlots = Record<string, SSRSlot>
|
|||||||
export type SSRSlot = (
|
export type SSRSlot = (
|
||||||
props: Props,
|
props: Props,
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
parentComponent: ComponentInternalInstance | null
|
parentComponent: ComponentInternalInstance | null,
|
||||||
|
scopeId: string | null
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
export function ssrRenderSlot(
|
export function ssrRenderSlot(
|
||||||
@ -23,7 +24,8 @@ export function ssrRenderSlot(
|
|||||||
if (slotFn) {
|
if (slotFn) {
|
||||||
if (slotFn.length > 1) {
|
if (slotFn.length > 1) {
|
||||||
// only ssr-optimized slot fns accept more than 1 arguments
|
// only ssr-optimized slot fns accept more than 1 arguments
|
||||||
slotFn(slotProps, push, parentComponent)
|
const scopeId = parentComponent && parentComponent.type.__scopeId
|
||||||
|
slotFn(slotProps, push, parentComponent, scopeId ? scopeId + `-s` : null)
|
||||||
} else {
|
} else {
|
||||||
// normal slot
|
// normal slot
|
||||||
renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
|
renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user