Skip to the solution
HardFrontend11 min read

When should you choose Vapor over VDOM in a real app — trade-offs?

875 views

The problem

Question presented to candidate: "Assume Vue 3.6 ships Vapor mode as stable. We have a large Vue 3 dashboard and a separate lightweight marketing widget embedded on partner sites. Where, if anywhere, would you use Vapor, and what would you weigh before switching?"

What a strong answer should cover:

  • What Vapor changes: compiled DOM updates instead of VNode diffing, and an optional runtime without the virtual DOM.
  • Where the gains are real: update-heavy UI (large tables, frequent ticks) and bundle-sensitive standalone apps that can be all-Vapor.
  • Where they are not: pages dominated by layout and paint, or initial render, where Vapor and VDOM are close; and mixed apps, where interop adds both runtimes.
  • Compatibility limits: <script setup>/template-only SFCs only, no Options API, no v-memo, getCurrentInstance() returns null; libraries built on render functions stay VDOM.
  • Status: Vapor is in the 3.6 release candidates today, not stable 3.5.
  • Method: measure first, adopt per component or per island, keep boundaries coarse.

Clarifying questions expected:

  • "Is the bottleneck JavaScript work on updates, initial load, or layout and paint?"
  • "How much of the UI comes from third-party component libraries?"

Code / implementation expected: Optional — explain how you would benchmark one component both ways.

  • vapor
  • compiler
  • hydration

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

11 min read · 11 sections

Read the code

VueBenchmark component: 5,000 keyed rows (compiled once as Vapor, once as VDOM)
<script setup vapor>
import { shallowRef } from "vue";
const rows = shallowRef([]);
let id = 1;
const build = (n) => Array.from({ length: n }, () => ({ id: id++, label: "row " + id }));
const create = () => { rows.value = build(5000); };
const update = () => { rows.value = rows.value.map((r, i) => (i % 10 === 0 ? { ...r, label: r.label + " !!!" } : r)); };
const swap = () => { const a = rows.value.slice(); const t = a[1]; a[1] = a[998]; a[998] = t; rows.value = a; };
window.__bench = { create, update, swap };
</script>
<template>
  <table><tbody>
    <tr v-for="row in rows" :key="row.id"><td>{{ row.id }}</td><td>{{ row.label }}</td></tr>
  </tbody></table>
</template>
Playwright harness used for the timings (median of 7 page loads)
import { chromium } from "playwright-core";
import http from "http"; import { readFileSync } from "fs"; import { join } from "path";
const dirs = process.argv.slice(2);
const browser = await chromium.launch({ executablePath: "/opt/pw-browsers/chromium-1194/chrome-linux/chrome" });
for (const dir of dirs) {
  const srv = http.createServer((q, s) => { const p = q.url === "/" ? "/index.html" : q.url; try { s.setHeader("content-type", p.endsWith(".js") ? "text/javascript" : "text/html"); s.end(readFileSync(join(dir, "dist", p))); } catch { s.statusCode = 404; s.end(); } }).listen(5263);
  const res = { create: [], update: [], swap: [] };
  for (let run = 0; run < 7; run++) {
    const page = await browser.newPage();
    await page.goto("http://localhost:5263/");
    for (const op of ["create", "update", "swap"]) {
      const ms = await page.evaluate((op) => new Promise((r) => { const t = performance.now(); window.__bench[op](); requestAnimationFrame(() => setTimeout(() => r(performance.now() - t), 0)); }), op);
      res[op].push(ms);
    }
    await page.close();
  }
  const med = (a) => a.sort((x, y) => x - y)[a.length >> 1].toFixed(1);
  console.log(dir.split("/").pop(), "median ms over 7 runs:", Object.entries(res).map(([k, v]) => `${k}=${med(v)}`).join(" "));
  await new Promise((r) => srv.close(r));
}
await browser.close();

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 76 of 92 in Vue.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Vue.js questions
Up nextHard

What is async setup and top-level await?

Continue