Skip to the solution
EasyFrontend10 min read

What is a Single File Component (SFC)?

571 views

The problem

Question presented to candidate: "Most Vue projects are full of .vue files. What is a Single File Component, what goes in it, and what actually happens to that file before it reaches the browser?"

What a strong answer should cover:

  • An SFC is a .vue file containing one component: a <template> block, a <script> and/or <script setup> block, and zero or more <style> blocks; custom blocks are allowed too.
  • It colocates markup, logic and styles by component (separation of concerns by feature, not by file type).
  • Browsers cannot run .vue files: a build plugin (@vitejs/plugin-vue, using @vue/compiler-sfc) compiles it into a JavaScript module whose template is a render function, and CSS.
  • <script setup> is compile-time sugar: top-level bindings are exposed to the template, and defineProps / defineEmits are compiler macros.
  • <style scoped> works by adding a data-v-xxxx attribute to elements and rewriting selectors; <style module> gives CSS modules; lang="ts" and lang="scss" select preprocessors.
  • Benefits: syntax highlighting and type checking in the editor, hot module replacement, scoped CSS, and compile-time template optimisations.

Clarifying questions expected:

  • "Do you want the file format, or what the compiler turns it into?"

Code / implementation expected: Yes. A small SFC, ideally with what it compiles into.

  • sfc
  • components

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

10 min read Β· 11 sections

Read the code

VueA complete SFC: script setup, template and scoped style
<!-- One file, three blocks: logic, markup and styles for one component. -->
<script setup>
import { ref, computed } from "vue";

// Top-level bindings in <script setup> are usable directly in the template.
const name = ref("Ada");
const count = ref(0);
const label = computed(() => (count.value === 1 ? "time" : "times"));
</script>

<template>
  <h2 class="title">Hello, {{ name }}</h2>
  <input v-model="name" />
  <button @click="count++">Clicked {{ count }} {{ label }}</button>
</template>

<style scoped>
/* scoped: compiled to .title[data-v-xxxx], so it cannot leak to other components */
.title {
  color: seagreen;
}
</style>
Compile an SFC yourself with @vue/compiler-sfc (what @vitejs/plugin-vue does)
import { readFileSync } from "fs";
import { parse, compileScript, compileStyle } from "@vue/compiler-sfc";
const src = readFileSync("Greeting.vue", "utf8");
const { descriptor } = parse(src, { filename: "Greeting.vue" });
console.log("BLOCKS:", Object.entries({ template: descriptor.template, scriptSetup: descriptor.scriptSetup, script: descriptor.script }).map(([k, v]) => `${k}=${v ? "yes" : "no"}`).join(" "), "styles=" + descriptor.styles.length, "scoped=" + descriptor.styles[0].scoped);
const id = "7a7a37b1";
const script = compileScript(descriptor, { id, inlineTemplate: true, isProd: true });
console.log("----- SCRIPT (production, template inlined) -----\n" + script.content);
const style = compileStyle({ source: descriptor.styles[0].content, filename: "Greeting.vue", id: "data-v-" + id, scoped: true });
console.log("----- STYLE -----\n" + style.code.trim());

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 14 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 nextEasy

What is Vue template syntax (interpolation, directives, expressions)?

Continue