Open-Source Wikis

/

Vue.js

/

Packages

/

compiler-ssr

vuejs/core

compiler-ssr

Active contributors: Evan You, daiwei, edison

Purpose

@vue/compiler-ssr produces server-side render functions: instead of returning a vnode tree, the generated code pushes string fragments into a buffer that @vue/server-renderer consumes. It reuses @vue/compiler-dom's parser and most of @vue/compiler-core's transforms, then swaps element/component/slot transforms for SSR-aware versions and substitutes a different codegen pass (ssrCodegenTransform).

The package is small (~2,250 lines, 17 files) but tightly coupled to compiler-dom and server-renderer: the helpers it emits must exist in server-renderer/src/helpers/.

Directory layout

packages/compiler-ssr/src/
├── index.ts                     # compile()
├── runtimeHelpers.ts            # SSR helper symbol IDs (ssrRenderAttrs, ssrInterpolate, …)
├── errors.ts                    # SSR-specific compiler errors
├── ssrCodegenTransform.ts       # walks AST, rewrites vnode-style codegenNode → string-builder code
└── transforms/
    ├── ssrTransformElement.ts            # element → ssrRenderAttr / template literal pieces
    ├── ssrTransformComponent.ts          # component → ssrRenderComponent (with slot encoding)
    ├── ssrTransformSlotOutlet.ts         # <slot> → ssrRenderSlot
    ├── ssrTransformSuspense.ts           # render fallback synchronously
    ├── ssrTransformTeleport.ts           # emit teleport markers
    ├── ssrTransformTransition.ts         # render the BaseTransition default branch
    ├── ssrTransformTransitionGroup.ts    # render the wrapper element + children
    ├── ssrVIf.ts / ssrVFor.ts            # control-flow rewrites that emit string code
    ├── ssrVModel.ts / ssrVShow.ts        # SSR-side directive renderers
    ├── ssrInjectFallthroughAttrs.ts      # inject parent attrs into root element string
    └── ssrInjectCssVars.ts               # write style="--cssvar-foo: …" into root attrs

The pipeline

graph LR
    src["template source"]
    parse["compiler-dom baseParse"]
    ast["AST"]
    xform["transform with ssr* plugins"]
    cgxform["ssrCodegenTransform<br/>(rewrites codegenNode)"]
    gen["compiler-core generate()"]
    out["ssrRender(_ctx, _push, _parent, …) {…}"]
    src --> parse --> ast --> xform --> cgxform --> gen --> out

Two things happen in the transform:

  1. Standard transforms like transformExpression, trackVForSlotScopes, trackSlotScopes, transformStyle, transformVBindShorthand — same logic as the client-side pipeline.
  2. SSR-specific transforms like ssrTransformElement, ssrTransformComponent, ssrInjectFallthroughAttrs, ssrInjectCssVars — replace or augment the AST nodes with SSR-flavored variants.

After both phases, ssrCodegenTransform walks the AST and rewrites each node's codegenNode into a sequence of _push("…literal…") and _push(_ssrRenderAttr(…)) calls that, in aggregate, emit the entire HTML string. Then the same compiler-core/codegen.ts writes the JavaScript.

ssrTransformElement

The biggest of the SSR transforms. For each element it:

  1. Resolves the runtime element name (handling <component is="…">).
  2. Iterates props, deciding for each one:
    • Is it a static attribute? Append it to a literal template piece.
    • Is it a dynamic class/style? Generate a call to _ssrRenderClass/_ssrRenderStyle.
    • Is it v-bind="object"? Generate _ssrRenderAttrs(_mergeProps(…)).
    • Is it an event handler or a DOM-only prop? Skip it (event handlers don't run on the server).
  3. Decides between writing the attributes into a static <tag attr="…">…</tag> template, or splitting into _push(<tag) then a dynamic attributes call then _push(>) and so on.
  4. Recurses into children, with text and interpolations becoming _ssrInterpolate calls.

ssrTransformComponent

Component vnodes can't simply be string-emitted because the server needs to await their setup, run their render function, etc. This transform:

  1. Computes the props arguments.
  2. Encodes scoped slots as functions that themselves push to an inner buffer.
  3. Emits a call to _ssrRenderComponent(_component_X, props, slots, _parent, _scopeId) that the runtime will resolve and recurse into.
  4. Tracks per-slot opt-in to async rendering.

The non-trivial bit is the slot encoding: a scoped slot in SSR context must produce its own string buffer, which then gets stitched into the parent's buffer at the right place. The transform builds a function whose body is a mini ssrRender pipeline.

Mode selection

The compiler is configured per-call via options.ssr. compileTemplate in @vue/compiler-sfc decides this and forwards to @vue/compiler-ssr when SSR is requested; otherwise to @vue/compiler-dom. The same .vue file therefore generates both a client render function and a ssrRender function in SSR-enabled builds; vite-plugin-vue's SSR mode flips the flag.

Differences from compiler-dom

Concern compiler-dom compiler-ssr
Entry symbol render(_ctx, _cache) ssrRender(_ctx, _push, _parent, _attrs)
Output vnode tree via _createElementVNode string fragments via _push
Static hoisting yes (cacheStatic) disabled (hoistStatic: false in options)
prefixIdentifiers conditional always true
Cache handlers enabled in client disabled (no event handlers in SSR)

Integration points

  • Imports parserOptions, transformBind, transformOn, transformStyle, transformVBindShorthand, trackSlotScopes, trackVForSlotScopes, transformExpression from @vue/compiler-dom and @vue/compiler-core.
  • Emits calls to helpers defined in @vue/server-renderer/src/helpers/. The names in runtimeHelpers.ts here must match those exports.
  • Invoked by @vue/compiler-sfc.compileTemplate when options.ssr is set.
  • The generated ssrRender is consumed by the renderer logic in packages/server-renderer/src/render.ts.

Entry points for modification

  • New SSR helper? Add it in runtimeHelpers.ts here, implement it in server-renderer/src/helpers/, export it from server-renderer/src/internal.ts.
  • Built-in component change? Update the matching ssrTransformX and the helpers/ssrRenderX in server-renderer in lockstep.
  • Hydration-mismatch bug from a particular template? Reproduce it in the Template Explorer with ssr: true to see what code SSR generates, then in client mode to compare.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

compiler-ssr – Vue.js wiki | Factory