The Scaling Up chapters are the guide's opinions: which tools to use, how to think about state, how to test, and the rules for code that must run on a server. Nuxt implements most of this for you, which is exactly why interviewers ask about it: they want to know whether you understand what the framework is doing on your behalf.
.vue file holds <template>, <script> (or <script setup>), <style> and optional custom blocks; @vue/compiler-sfc compiles it to standard JavaScript and CSS at build time.<style scoped> adds a data-v-hash attribute selector; a child component's root receives the parent's scoped styles; :deep() targets child internals, :slotted() slot content, :global() escapes scoping; mixing scoped and global blocks is allowed. CSS modules via <style module> expose $style (or a custom name), and useCssModule() in setup. v-bind() in CSS binds a reactive value to a custom property (color: v-bind('theme.color')).lang="ts", lang="scss", lang="pug"; src imports for any block; automatic component name inference from the filename.npm create vue@latest (create-vue, Vite-based); Vue CLI is in maintenance mode. Online: SFC Playground, StackBlitz.vue-tsc. Browser devtools: the Vue Devtools extension (also standalone and as a Vite plugin).@vitejs/plugin-vue for Vite, vue-loader for webpack. Linting with eslint-plugin-vue, formatting with Prettier (or the extension's formatter). Lower-level: @vue/compiler-sfc for custom tooling.<component :is> and hashchange.reactive() object exported from a module, but that is a singleton shared across requests in SSR, which is the "cross-request state pollution" the SSR chapter warns about.vitest with environment: 'happy-dom' or 'jsdom'; testing composables with a withSetup helper that mounts a throwaway app so lifecycle hooks and inject work, then unmounts it.createSSRApp on both sides, renderToString from vue/server-renderer on the server, .mount() on the client to hydrate; import.meta.env.SSR in Vite tells you where code runs. The guide points to Nuxt, Quasar and Vite SSR as higher-level solutions.beforeCreate/created (or setup) run, no mounted; guard platform APIs; avoid module singletons for state (create the app, router and store per request); use app.runWithContext when calling composables outside setup.<div> inside <p>), non-deterministic values (Date.now(), Math.random()), timezone differences, and browser extensions editing the DOM; data-allow-mismatch (3.5) suppresses an expected one. Custom directives need getSSRProps; Teleports render into ctx.teleports and must be injected into the HTML manually.The guide's withSetup helper, which is the standard way to unit-test a composable:
import { createApp } from 'vue'
export function withSetup<T>(composable: () => T): [T, ReturnType<typeof createApp>] {
let result!: T
const app = createApp({
setup() {
result = composable()
return () => {} // render nothing
},
})
app.mount(document.createElement('div'))
return [result, app] // call app.unmount() at the end of the test
}
The SFC CSS features in one block:
<script setup lang="ts">
const theme = reactive({ color: 'tomato' })
const styles = useCssModule() // $style in the template
</script>
<template>
<section :class="styles.panel">
<ChildCard class="card"><slot /></ChildCard>
</section>
</template>
<style scoped>
.card :deep(.title) { font-weight: 600; } /* child internals */
:slotted(p) { margin: 0; } /* slot content */
:global(body) { margin: 0; } /* escape scoping */
section { color: v-bind('theme.color'); } /* reactive custom property */
</style>
<style module>
.panel { padding: 1rem; }
</style>
Cross-request state pollution, the SSR chapter's central warning:
// ✗ store.ts — one object for the whole server process
export const store = reactive({ user: null })
// ✓ create per request and provide it
export function createStore() { return reactive({ user: null }) }
// server entry: const app = createSSRApp(App); app.provide('store', createStore())
<style scoped> in a parent still applies to the root element of a child component, by design, so layout styling works. Everything below the child's root needs :deep(). People discover this when a child's root class collides with a parent's scoped selector.
withSetup to test a composable that registers onMounted and injects a value; confirm both work and the test fails without the helper.:deep() rule for its internals; inspect the generated selectors in devtools.renderToString in a Node script twice with different data using a module-level store, and observe the pollution."Three rules. Only setup, beforeCreate and created run on the server, so anything touching the DOM or browser APIs goes into mounted or behind an environment check. Never keep state in module-level singletons, because one Node process serves many requests and a shared reactive() leaks one user's data into another's render; create the app, router and store per request, which is what Pinia and Nuxt do for you. And produce deterministic markup: valid HTML nesting, no Date.now() or Math.random() in the render, or a data-allow-mismatch when a difference is intended. Everything else, hydration and renderToString, the framework handles."
Nothing yet. Notes filed here show up on this page and the section overview.
Built-in components
Transition and TransitionGroup classes, hooks and modes, KeepAlive include/exclude/max, Teleport targets and defer, and Suspense's async dependencies, events and nesting rules.
Best practices — deployment, performance, accessibility, security
The four Best Practices chapters as checklists — production builds and error tracking, the guide's page-load and update-performance techniques, its accessibility guidance, and the security rules Vue enforces and the ones it cannot.