@vue/compiler-ssr
Advanced tools
Comparing version
@@ -22,2 +22,3 @@ 'use strict'; | ||
const SSR_RENDER_PORTAL = Symbol(`ssrRenderPortal`); | ||
const SSR_RENDER_SUSPENSE = Symbol(`ssrRenderSuspense`); | ||
const ssrHelpers = { | ||
@@ -37,3 +38,4 @@ [SSR_INTERPOLATE]: `ssrInterpolate`, | ||
[SSR_GET_DYNAMIC_MODEL_PROPS]: `ssrGetDynamicModelProps`, | ||
[SSR_RENDER_PORTAL]: `ssrRenderPortal` | ||
[SSR_RENDER_PORTAL]: `ssrRenderPortal`, | ||
[SSR_RENDER_SUSPENSE]: `ssrRenderSuspense` | ||
}; | ||
@@ -50,3 +52,3 @@ // Note: these are helpers imported from @vue/server-renderer | ||
const [rootBranch] = node.branches; | ||
const ifStatement = compilerDom.createIfStatement(rootBranch.condition, processChildrenAsStatement(rootBranch.children, context)); | ||
const ifStatement = compilerDom.createIfStatement(rootBranch.condition, processIfBranch(rootBranch, context)); | ||
context.pushStatement(ifStatement); | ||
@@ -56,3 +58,3 @@ let currentIf = ifStatement; | ||
const branch = node.branches[i]; | ||
const branchBlockStatement = processChildrenAsStatement(branch.children, context); | ||
const branchBlockStatement = processIfBranch(branch, context); | ||
if (branch.condition) { | ||
@@ -73,2 +75,9 @@ // else-if | ||
} | ||
function processIfBranch(branch, context) { | ||
const { children } = branch; | ||
const needFragmentWrapper = (children.length !== 1 || children[0].type !== 1 /* ELEMENT */) && | ||
// optimize away nested fragments when the only child is a ForNode | ||
!(children.length === 1 && children[0].type === 11 /* FOR */); | ||
return processChildrenAsStatement(children, context, needFragmentWrapper); | ||
} | ||
@@ -80,4 +89,7 @@ // Plugin for the first transform pass, which simply constructs the AST node | ||
function ssrProcessFor(node, context) { | ||
const needFragmentWrapper = node.children.length !== 1 || node.children[0].type !== 1 /* ELEMENT */; | ||
const renderLoop = compilerDom.createFunctionExpression(compilerDom.createForLoopParams(node.parseResult)); | ||
renderLoop.body = processChildrenAsStatement(node.children, context); | ||
renderLoop.body = processChildrenAsStatement(node.children, context, needFragmentWrapper); | ||
// v-for always renders a fragment | ||
context.pushStringPart(`<!--[-->`); | ||
context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_LIST), [ | ||
@@ -87,2 +99,3 @@ node.source, | ||
])); | ||
context.pushStringPart(`<!--]-->`); | ||
} | ||
@@ -124,2 +137,75 @@ | ||
// Note: this is a 2nd-pass codegen transform. | ||
function ssrProcessPortal(node, context) { | ||
const targetProp = compilerDom.findProp(node, 'target'); | ||
if (!targetProp) { | ||
context.onError(createSSRCompilerError(67 /* X_SSR_NO_PORTAL_TARGET */, node.loc)); | ||
return; | ||
} | ||
let target; | ||
if (targetProp.type === 6 /* ATTRIBUTE */ && targetProp.value) { | ||
target = compilerDom.createSimpleExpression(targetProp.value.content, true); | ||
} | ||
else if (targetProp.type === 7 /* DIRECTIVE */ && targetProp.exp) { | ||
target = targetProp.exp; | ||
} | ||
else { | ||
context.onError(createSSRCompilerError(67 /* X_SSR_NO_PORTAL_TARGET */, targetProp.loc)); | ||
return; | ||
} | ||
const contentRenderFn = compilerDom.createFunctionExpression([`_push`], undefined, // Body is added later | ||
true, // newline | ||
false, // isSlot | ||
node.loc); | ||
contentRenderFn.body = processChildrenAsStatement(node.children, context); | ||
context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_PORTAL), [ | ||
contentRenderFn, | ||
target, | ||
`_parent` | ||
])); | ||
} | ||
const wipMap = new WeakMap(); | ||
// phase 1 | ||
function ssrTransformSuspense(node, context) { | ||
return () => { | ||
if (node.children.length) { | ||
const wipEntry = { | ||
slotsExp: null, | ||
wipSlots: [] | ||
}; | ||
wipMap.set(node, wipEntry); | ||
wipEntry.slotsExp = compilerDom.buildSlots(node, context, (_props, children, loc) => { | ||
const fn = compilerDom.createFunctionExpression([], undefined, // no return, assign body later | ||
true, // newline | ||
false, // suspense slots are not treated as normal slots | ||
loc); | ||
wipEntry.wipSlots.push({ | ||
fn, | ||
children | ||
}); | ||
return fn; | ||
}).slots; | ||
} | ||
}; | ||
} | ||
// phase 2 | ||
function ssrProcessSuspense(node, context) { | ||
// complete wip slots with ssr code | ||
const wipEntry = wipMap.get(node); | ||
if (!wipEntry) { | ||
return; | ||
} | ||
const { slotsExp, wipSlots } = wipEntry; | ||
for (let i = 0; i < wipSlots.length; i++) { | ||
const { fn, children } = wipSlots[i]; | ||
fn.body = processChildrenAsStatement(children, context); | ||
} | ||
// _push(ssrRenderSuspense(slots)) | ||
context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_SUSPENSE), [ | ||
`_push`, | ||
slotsExp | ||
])); | ||
} | ||
// We need to construct the slot functions in the 1st pass to ensure proper | ||
@@ -129,4 +215,10 @@ // scope tracking, but the children of each slot cannot be processed until | ||
// pass and complete them in the 2nd pass. | ||
const wipMap = new WeakMap(); | ||
const wipMap$1 = new WeakMap(); | ||
const componentTypeMap = new WeakMap(); | ||
// ssr component transform is done in two phases: | ||
// In phase 1. we use `buildSlot` to analyze the children of the component into | ||
// WIP slot functions (it must be done in phase 1 because `buildSlot` relies on | ||
// the core transform context). | ||
// In phase 2. we convert the WIP slots from phase 1 into ssr-specific codegen | ||
// nodes. | ||
const ssrTransformComponent = (node, context) => { | ||
@@ -140,2 +232,5 @@ if (node.type !== 1 /* ELEMENT */ || | ||
componentTypeMap.set(node, component); | ||
if (component === compilerDom.SUSPENSE) { | ||
return ssrTransformSuspense(node, context); | ||
} | ||
return; // built-in component: fallthrough | ||
@@ -165,3 +260,3 @@ } | ||
const wipEntries = []; | ||
wipMap.set(node, wipEntries); | ||
wipMap$1.set(node, wipEntries); | ||
const buildSSRSlotFn = (props, children, loc) => { | ||
@@ -189,3 +284,2 @@ const fn = compilerDom.createFunctionExpression([props || `_`, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later | ||
// this is a built-in component that fell-through. | ||
// just render its children. | ||
const component = componentTypeMap.get(node); | ||
@@ -195,7 +289,13 @@ if (component === compilerDom.PORTAL) { | ||
} | ||
processChildren(node.children, context); | ||
else if (component === compilerDom.SUSPENSE) { | ||
return ssrProcessSuspense(node, context); | ||
} | ||
else { | ||
// real fall-through (e.g. KeepAlive): just render its children. | ||
processChildren(node.children, context, component === compilerDom.TRANSITION_GROUP); | ||
} | ||
} | ||
else { | ||
// finish up slot function expressions from the 1st pass. | ||
const wipEntries = wipMap.get(node) || []; | ||
const wipEntries = wipMap$1.get(node) || []; | ||
for (let i = 0; i < wipEntries.length; i++) { | ||
@@ -207,3 +307,3 @@ const { fn, children, vnodeBranch } = wipEntries[i]; | ||
// is called by `_ssrRenderSlot`. | ||
fn.body = compilerDom.createIfStatement(compilerDom.createSimpleExpression(`_push`, false), processChildrenAsStatement(children, context, true /* withSlotScopeId */), vnodeBranch); | ||
fn.body = compilerDom.createIfStatement(compilerDom.createSimpleExpression(`_push`, false), processChildrenAsStatement(children, context, false, true /* withSlotScopeId */), vnodeBranch); | ||
} | ||
@@ -213,30 +313,2 @@ context.pushStatement(compilerDom.createCallExpression(`_push`, [node.ssrCodegenNode])); | ||
} | ||
function ssrProcessPortal(node, context) { | ||
const targetProp = compilerDom.findProp(node, 'target'); | ||
if (!targetProp) { | ||
context.onError(createSSRCompilerError(67 /* X_SSR_NO_PORTAL_TARGET */, node.loc)); | ||
return; | ||
} | ||
let target; | ||
if (targetProp.type === 6 /* ATTRIBUTE */ && targetProp.value) { | ||
target = compilerDom.createSimpleExpression(targetProp.value.content, true); | ||
} | ||
else if (targetProp.type === 7 /* DIRECTIVE */ && targetProp.exp) { | ||
target = targetProp.exp; | ||
} | ||
else { | ||
context.onError(createSSRCompilerError(67 /* X_SSR_NO_PORTAL_TARGET */, targetProp.loc)); | ||
return; | ||
} | ||
const contentRenderFn = compilerDom.createFunctionExpression([`_push`], undefined, // Body is added later | ||
true, // newline | ||
false, // isSlot | ||
node.loc); | ||
contentRenderFn.body = processChildrenAsStatement(node.children, context); | ||
context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_PORTAL), [ | ||
contentRenderFn, | ||
target, | ||
`_parent` | ||
])); | ||
} | ||
const rawOptionsMap = new WeakMap(); | ||
@@ -333,2 +405,4 @@ const [baseNodeTransforms, baseDirectiveTransforms] = compilerDom.getBaseTransformPreset(true); | ||
const openTag = [`<${node.tag}`]; | ||
// some tags need to be pasesd to runtime for special checks | ||
const needTagForRuntime = node.tag === 'textarea' || node.tag.indexOf('-') > 0; | ||
// v-bind="obj" or v-bind:[key] can potentially overwrite other static | ||
@@ -348,3 +422,5 @@ // attrs and can affect final rendering result, so when they are present | ||
const tempId = `_temp${context.temps++}`; | ||
propsExp.arguments[0] = compilerDom.createAssignmentExpression(compilerDom.createSimpleExpression(tempId, false), props); | ||
propsExp.arguments = [ | ||
compilerDom.createAssignmentExpression(compilerDom.createSimpleExpression(tempId, false), props) | ||
]; | ||
const existingText = node.children[0]; | ||
@@ -365,8 +441,10 @@ rawChildrenMap.set(node, compilerDom.createCallExpression(context.helper(SSR_INTERPOLATE), [ | ||
propsExp.arguments = [ | ||
compilerDom.createAssignmentExpression(tempExp, props), | ||
compilerDom.createCallExpression(context.helper(compilerDom.MERGE_PROPS), [ | ||
tempExp, | ||
compilerDom.createCallExpression(context.helper(SSR_GET_DYNAMIC_MODEL_PROPS), [ | ||
compilerDom.createSequenceExpression([ | ||
compilerDom.createAssignmentExpression(tempExp, props), | ||
compilerDom.createCallExpression(context.helper(compilerDom.MERGE_PROPS), [ | ||
tempExp, | ||
vModel.exp // model | ||
compilerDom.createCallExpression(context.helper(SSR_GET_DYNAMIC_MODEL_PROPS), [ | ||
tempExp, | ||
vModel.exp // model | ||
]) | ||
]) | ||
@@ -377,2 +455,5 @@ ]) | ||
} | ||
if (needTagForRuntime) { | ||
propsExp.arguments.push(`"${node.tag}"`); | ||
} | ||
openTag.push(propsExp); | ||
@@ -457,3 +538,7 @@ } | ||
// transforms that returns properties with dynamic keys | ||
openTag.push(compilerDom.createCallExpression(context.helper(SSR_RENDER_DYNAMIC_ATTR), [key, value])); | ||
const args = [key, value]; | ||
if (needTagForRuntime) { | ||
args.push(`"${node.tag}"`); | ||
} | ||
openTag.push(compilerDom.createCallExpression(context.helper(SSR_RENDER_DYNAMIC_ATTR), args)); | ||
} | ||
@@ -546,3 +631,4 @@ } | ||
const context = createSSRTransformContext(ast, options); | ||
processChildren(ast.children, context); | ||
const isFragment = ast.children.length > 1 && ast.children.some(c => !compilerDom.isText(c)); | ||
processChildren(ast.children, context, isFragment); | ||
ast.codegenNode = compilerDom.createBlockStatement(context.body); | ||
@@ -601,3 +687,6 @@ // Finalize helpers. | ||
} | ||
function processChildren(children, context) { | ||
function processChildren(children, context, asFragment = false) { | ||
if (asFragment) { | ||
context.pushStringPart(`<!--[-->`); | ||
} | ||
for (let i = 0; i < children.length; i++) { | ||
@@ -629,6 +718,9 @@ const child = children[i]; | ||
} | ||
if (asFragment) { | ||
context.pushStringPart(`<!--]-->`); | ||
} | ||
} | ||
function processChildrenAsStatement(children, parentContext, withSlotScopeId = parentContext.withSlotScopeId) { | ||
function processChildrenAsStatement(children, parentContext, asFragment = false, withSlotScopeId = parentContext.withSlotScopeId) { | ||
const childContext = createChildContext(parentContext, withSlotScopeId); | ||
processChildren(children, childContext); | ||
processChildren(children, childContext, asFragment); | ||
return compilerDom.createBlockStatement(childContext.body); | ||
@@ -635,0 +727,0 @@ } |
{ | ||
"name": "@vue/compiler-ssr", | ||
"version": "3.0.0-alpha.8", | ||
"version": "3.0.0-alpha.9", | ||
"description": "@vue/compiler-ssr", | ||
@@ -30,5 +30,5 @@ "main": "dist/compiler-ssr.cjs.js", | ||
"dependencies": { | ||
"@vue/shared": "3.0.0-alpha.8", | ||
"@vue/compiler-dom": "3.0.0-alpha.8" | ||
"@vue/shared": "3.0.0-alpha.9", | ||
"@vue/compiler-dom": "3.0.0-alpha.9" | ||
} | ||
} |
40176
10.87%845
11.92%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
Updated