Zum Inhalt springen

Das erste Projekt

Auftrag

Der Freiwillige, der als Informatiker arbeitet, hat einen Rat für den Anfang. «Legt das Projekt mit dem offiziellen Werkzeug an, dann sieht es aus wie jedes andere Vue-Projekt.» Martina Hostettler hat daraus einen Auftrag gemacht.

Als Freiwilliger möchte ich, dass die App wie ein übliches Vue-Projekt aufgebaut ist, damit sich neue Freiwillige sofort zurechtfinden.

Die Akzeptanzkriterien lauten:

  1. Das Projekt entsteht mit create-vue und verwendet JavaScript.
  2. Die Startseite zeigt den Namen des Vereins und die Zahl der Dinge im Katalog.
  3. Die Öffnungszeiten stehen in einer eigenen Komponente.
  4. Während der Entwicklung stehen die Vue DevTools bereit.

Lernziele

Nach diesem Kapitel kannst du

  • ein Projekt mit create-vue in JavaScript anlegen und starten,
  • die Dateien eines Vue-Projekts und ihre Aufgaben erklären,
  • eine Single File Component mit Template, Script und Style schreiben,
  • erklären, wie createApp und mount die App in die Seite bringen,
  • die Vue DevTools und den Linter einsetzen.

Du brauchst dafür Node und npm aus dem Kapitel «Einrichten» und Vite aus dem Kapitel «Werkzeuge».

create-vue ist das offizielle Werkzeug für neue Vue-Projekte. Es legt ein Projekt mit Vite an, wie du es aus Teil 2 kennst, und fragt dich vorher, was hineingehört. Du startest es im Ordner, in dem das Projekt entstehen soll.

Terminal-Fenster
npm create vue@latest

Das Werkzeug stellt einige Fragen. Für den Kurs beantwortest du sie so, der Stand ist create-vue 3.24.0 vom Herbst 2026.

  1. Als Namen gibst du lorraine-commons ein.
  2. Bei «Use TypeScript?» wählst du No. Der Kurs verwendet JavaScript, und create-vue schlägt TypeScript als Standard vor.
  3. Bei den Funktionen wählst du nur «Linter». Router, Pinia und Vitest kommen in den Teilen 6 bis 8 dazu.
  4. Die experimentellen Funktionen lässt du leer.
  5. Bei der Frage, ob das Beispiel weggelassen werden soll, wählst du No, damit du es dir ansehen kannst.

Danach installierst du die Pakete und startest den Server für die Entwicklung.

Terminal-Fenster
cd lorraine-commons
npm install
npm run dev

Unter http://localhost:5173/ erscheint eine Seite mit dem Logo von Vue und dem Satz «You did it!».

create-vue legt mehr Dateien an als die SPA aus Teil 2. Die meisten kennst du aber schon.

Datei oder OrdnerAufgabe
index.htmldie einzige HTML-Seite, mit dem Element <div id="app"> und dem Skript src/main.js
src/main.jsstartet die App mit createApp und mount
src/App.vuedie oberste Komponente, von ihr aus wächst die App
src/components/weitere Komponenten, im Beispiel HelloWorld.vue und TheWelcome.vue
src/assets/CSS und Bilder, die Vite beim Build verarbeitet
public/Dateien, die Vite unverändert ausliefert, etwa favicon.ico
vite.config.jsdie Konfiguration von Vite mit den Plugins für Vue und die DevTools
jsconfig.jsonsagt dem Editor, wo der Alias @ hinzeigt
package.jsondie Pakete und die Befehle dev, build, preview und lint
eslint.config.js, .oxlintrc.jsondie Regeln für den Linter
.vscode/extensions.jsonempfiehlt Erweiterungen für VS Code, darunter «Vue (Official)»

Die Konfiguration von Vite sieht in den Übungsprojekten dieses Teils so aus. Gegenüber dem Projekt von create-vue ist nur der Block test für die Tests dazugekommen.

vite.config.js
import { fileURLToPath, URL } from 'node:url'
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import vueDevTools from 'vite-plugin-vue-devtools'
export default defineConfig({
plugins: [vue(), vueDevTools()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
test: {
environment: 'happy-dom',
},
})

vue() bringt Vite bei, Dateien mit der Endung .vue zu übersetzen. vueDevTools() fügt während der Entwicklung die DevTools in die Seite ein. Unter resolve.alias steht, dass @ für den Ordner src steht.

Eine Komponente in Vue ist meist eine Single File Component, kurz SFC. Eine Datei mit der Endung .vue fasst bis zu drei Blöcke zusammen, das Template mit dem Aufbau, das Script mit Zustand und Logik und den Style mit dem Aussehen. Schau dir die kleinste sinnvolle Komponente an und überleg dir, was sie anzeigt, bevor du sie im Playground öffnest.

App.vue
<script setup>
// Everything declared here can be used in the template
const association = 'Lorraine Commons'
const itemCount = 60
</script>
<template>
<h1>{{ association }}</h1>
<p>Bibliothek der Dinge im Lorraine-Quartier mit {{ itemCount }} Dingen im Katalog</p>
</template>
<style scoped>
h1 {
color: #1f6b4f;
}
</style>

Der Block <script setup> läuft einmal, wenn Vue die Komponente erzeugt. Alles, was darin auf oberster Ebene steht, Konstanten, Funktionen und Imports, kann das Template verwenden. {{ association }} setzt den Wert der Konstante als Text ein, das nächste Kapitel zeigt diese Schreibweise genauer. Der Block <style scoped> gilt nur für diese Komponente.

Ein Browser kann mit einer Datei .vue nichts anfangen. Vite übersetzt sie mit dem Plugin für Vue in ein JavaScript-Modul. Aus dem Template wird dabei eine Render-Funktion, die die Elemente der Seite erzeugt, und aus dem Style gewöhnliches CSS.

Der Weg einer Single File Component bis auf die Seite Links die Datei App.vue mit drei Blöcken, template für den Aufbau, script setup für Zustand und Logik, style scoped für das Aussehen. Vite übersetzt die Datei mit dem Plugin für Vue in CSS und ein JavaScript-Modul mit einer Render-Funktion. main.js importiert das Modul, erzeugt mit createApp die App und hängt sie mit mount in das Element mit der id app in index.html ein. App.vue <template> Aufbau mit HTML und Vue-Syntax <script setup> Zustand und Logik in JavaScript <style scoped> CSS nur für diese Komponente Vite mit @vitejs/plugin-vue CSS Regeln mit data-v-Attribut JavaScript-Modul mit Render-Funktion main.js createApp(App).mount('#app') importiert index.html <div id="app"></div> hängt ein

Das Modul src/main.js verbindet die Komponente mit der Seite.

main.js
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

createApp(App) erzeugt eine App, deren oberste Komponente App ist. mount('#app') sucht in der Seite das Element mit der id app und ersetzt seinen Inhalt durch das, was die Komponente rendert. Ab dann hält Vue die Seite im Takt mit dem Zustand. Zwischen den beiden Aufrufen lässt sich die App einrichten, in Teil 6 kommt dort zum Beispiel der Router dazu. Der Import der CSS-Datei bringt die Regeln, die für die ganze App gelten.

Eine Komponente kann andere Komponenten verwenden. Dafür importierst du die Datei im Script, und schon steht die Komponente im Template als eigenes Tag bereit. Im Beispiel bindet App.vue die Öffnungszeiten ein.

App.vue

App.vue
<script setup>
import OpeningHours from './OpeningHours.vue'
</script>
<template>
<h1>Lorraine Commons</h1>
<OpeningHours />
<h2>Katalog</h2>
</template>

OpeningHours.vue

OpeningHours.vue
<template>
<section>
<h2>Öffnungszeiten</h2>
<p>Mittwoch 17 bis 19 Uhr</p>
<p>Samstag 10 bis 13 Uhr</p>
</section>
</template>
<style scoped>
/* Applies only to the h2 of this component, the h2 in App.vue keeps its colour */
h2 {
color: #a3341f;
}
</style>

Beide Komponenten haben eine Überschrift h2, aber nur die in OpeningHours.vue ist rot. Bei scoped hängt Vue beim Übersetzen an jedes Element der Komponente ein Attribut wie data-v-7ba5bd90 und an jeden Selektor im Style dasselbe Attribut. Die Regel trifft darum nur Elemente dieser Komponente.

In einem Projekt von create-vue liegen Komponenten im Ordner src/components. Statt eines Pfads wie ../components/OpeningHours.vue schreibst du @/components/OpeningHours.vue. Der Alias @ zeigt immer auf src, egal in welchem Ordner die Datei liegt, die importiert. Komponenten bekommen Namen aus mehreren Wörtern in PascalCase, etwa OpeningHours, damit sie sich nicht mit HTML-Elementen verwechseln lassen. Einzig App ist davon ausgenommen.

Solange npm run dev läuft, zeigt die Seite unten in der Mitte eine kleine Leiste mit zwei Knöpfen. Der Knopf mit dem Logo von Vue öffnet die DevTools direkt in der Seite. Unter «Components» siehst du den Baum der Komponenten, und rechts daneben den Zustand der gewählten Komponente. Werte lassen sich dort auch ändern, die Seite folgt sofort. Der Knopf mit dem Fadenkreuz schaltet den Inspektor ein. Fährst du dann über die Seite, zeigt er, zu welcher Komponente ein Element gehört, und ein Klick öffnet die Datei im Editor.

Die DevTools stammen aus dem Plugin vite-plugin-vue-devtools und laufen nur während der Entwicklung, im Build fehlen sie. Für Chromium-Browser und Firefox gibt es zusätzlich die Erweiterung «Vue.js devtools», die in den Entwicklertools des Browsers einen eigenen Reiter für Vue einrichtet.

Ein Linter liest den Code, ohne ihn auszuführen, und meldet Fehler und verdächtige Stellen. Mit der Antwort «Linter» richtet create-vue zwei davon ein, das schnelle Oxlint und ESLint mit den Regeln für Vue. npm run lint startet beide nacheinander und lässt sie alles korrigieren, was sich automatisch korrigieren lässt. Was übrig bleibt, melden sie mit Datei, Zeile und Regel, etwa so.

src/components/ItemList.vue
5:7 error 'unused' is assigned a value but never used no-unused-vars
10:5 error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key

Die erste Meldung betrifft eine Variable, die nirgends verwendet wird. Die zweite gilt einer Liste ohne key, das nächste Kapitel erklärt, warum Vue diesen Schlüssel braucht. In VS Code zeigt die Erweiterung «ESLint» dieselben Meldungen schon beim Tippen, und «Vue (Official)» bringt Farben, Vervollständigung und Prüfungen für Dateien .vue. Die Übungsprojekte des Kurses verzichten auf den Linter, damit die Installation klein bleibt. In deinem eigenen Projekt lohnt er sich.

Arbeite mit dem Beispiel zu den Öffnungszeiten. Lies zuerst beide Dateien und schreib auf, was du erwartest, bevor du etwas änderst.

  1. Welche Farbe hat die Überschrift «Katalog», wenn du in OpeningHours.vue das Wort scoped entfernst? Probiere es im Playground aus.
  2. Setz scoped wieder ein und ergänze in App.vue einen Block <style> mit h2 { color: blue; }. Welche Überschrift wird blau, und warum bleibt die andere rot?
  3. Ändere den Import in App.vue auf import Hours from './OpeningHours.vue'. Was musst du im Template anpassen?
  4. Öffne in deinem Projekt von create-vue die DevTools und klappe im Baum die Komponente TheWelcome auf. Wie viele Komponenten WelcomeItem stecken darin, und wo stehen sie in TheWelcome.vue?

Die Aufgaben bauen auf einem Projekt mit der Struktur von create-vue auf. Du findest es im Kursrepo im Ordner exercises/03-vue/first-project/de. Kopiere den Ordner, installiere die Pakete und starte die Tests.

Terminal-Fenster
npm install
npm test

Mit npm run dev siehst du die App und die DevTools im Browser. Zu Beginn schlagen alle Tests fehl, das ist so gewollt.

Aufgabe Einstieg

Ersetze in src/App.vue das Beispiel durch den Anfang des Katalogs. Die Hauptüberschrift lautet «Lorraine Commons», darunter steht die Zahl der Dinge, etwa «60 Dinge im Katalog». Die Daten importierst du mit import items from '../data/items.json', die Zahl ist items.length.

Lösung

Im Script steht der Import der Daten, import items from '../data/items.json'. Das Template zeigt Name und Zahl.

App.vue
<template>
<header>
<h1>Lorraine Commons</h1>
<p>{{ items.length }} Dinge im Katalog</p>
</header>

Vite lädt die Datei JSON beim Import als gewöhnliches Array. {{ items.length }} steht im Template, damit die Zahl aus den Daten kommt und sich anpasst, wenn der Katalog wächst.

Aufgabe Vertiefung

Lege die Komponente src/components/OpeningHours.vue an. Sie zeigt die Überschrift «Öffnungszeiten» und darunter «Mittwoch 17 bis 19 Uhr» und «Samstag 10 bis 13 Uhr». Gib ihr einen eigenen Style mit scoped und binde sie in App.vue mit dem Alias @ ein.

Lösung
OpeningHours.vue
<template>
<section class="opening-hours">
<h2>Öffnungszeiten</h2>
<p>Mittwoch 17 bis 19 Uhr</p>
<p>Samstag 10 bis 13 Uhr</p>
</section>
</template>
<style scoped>
.opening-hours {
padding: 0.5rem 1rem;
border-left: 4px solid #1f6b4f;
background: #eef6f2;
}
h2 {
margin: 0;
font-size: 1.1rem;
}
p {
margin: 0.25rem 0 0;
}
</style>

In App.vue kommt der Import import OpeningHours from '@/components/OpeningHours.vue' dazu, und im Template steht <OpeningHours />. Eine Komponente ohne Script braucht keinen Block <script setup>.

Aufgabe Knobelei

Ergänze in App.vue eine Fusszeile footer, die die Version von Vue und den Modus nennt, in dem Vite die App ausführt, etwa «Gebaut mit Vue 3.5.44, Modus development». Die Version liefert import { version } from 'vue', den Modus kennst du aus dem Kapitel «Werkzeuge». Was zeigt die Fusszeile nach npm run build und npm run preview, und was in den Tests?

Lösung
App.vue
<script setup>
import { version } from 'vue'
import OpeningHours from '@/components/OpeningHours.vue'
import items from '../data/items.json'
// Vite sets the mode, development for npm run dev and production for the build
const mode = import.meta.env.MODE
</script>
<template>
<header>
<h1>Lorraine Commons</h1>
<p>{{ items.length }} Dinge im Katalog</p>
</header>
<main>
<OpeningHours />
</main>
<footer>Gebaut mit Vue {{ version }}, Modus {{ mode }}</footer>
</template>

import.meta.env.MODE ist bei npm run dev gleich development, im Build production und in den Tests test. Vite setzt den Wert beim Übersetzen ein, darum steht im Build fest «production». Die Konstante mode braucht es, weil das Template nur auf Werte aus dem Script zugreift und import.meta dort nicht kennt.

FehlerWas hilft
Bei «Use TypeScript?» aus Versehen Yes gewählt, im Projekt stehen Dateien .ts.Das Projekt neu anlegen und No wählen. Der Kurs verwendet JavaScript.
index.html direkt im Browser geöffnet, die Seite bleibt leer.Die App mit npm run dev starten. Der Browser kann Dateien .vue nicht selbst lesen.
In der Konsole steht Failed to resolve component: OpeningHours.Die Komponente im Script importieren und den Namen im Template genau gleich schreiben.
VS Code färbt Dateien .vue falsch ein oder meldet seltsame Fehler.Die Erweiterung «Vue (Official)» installieren und die ältere Erweiterung Vetur deaktivieren.
Ein KI-Assistent schlägt vue create vor.Das ist die Vue CLI, die nur noch gewartet wird. Neue Projekte entstehen mit npm create vue@latest.
Ein KI-Assistent schreibt new Vue({ el: '#app' }) oder verlangt ein einziges Wurzelelement im Template.Beides stammt aus Vue 2. In Vue 3 startet createApp(App).mount('#app') die App, und ein Template darf mehrere Wurzeln haben.

Selbstcheck

  1. Wie beantwortest du die Fragen von create-vue für den Kurs, und warum?
  2. Welche Aufgabe haben index.html, src/main.js und src/App.vue?
  3. Was bewirkt scoped im Block <style>?
  4. Wofür steht @ in einem Import, und wo ist das festgelegt?
Antworten
  1. TypeScript No, weil der Kurs JavaScript verwendet, bei den Funktionen nur «Linter», weil Router, Pinia und Vitest später kommen, und das Beispiel behalten, um es anzusehen.
  2. index.html ist die einzige Seite und enthält das Element #app. main.js erzeugt die App mit createApp und hängt sie mit mount dort ein. App.vue ist die oberste Komponente.
  3. Die Regeln gelten nur für die Elemente dieser Komponente. Vue hängt dafür beim Übersetzen ein Attribut data-v- an die Elemente und an die Selektoren.
  4. Für den Ordner src. Festgelegt ist das in vite.config.js unter resolve.alias, und jsconfig.json sagt es dem Editor.

Auftrag

Die Startseite steht, und der Freiwillige findet sich in den Dateien zurecht. Martina Hostettler möchte als Nächstes den Katalog sehen. «Zeig alle Dinge mit Kategorie und ob sie gerade da sind, wie in der Version von Hand.» Darum geht es im nächsten Kapitel.

Texte und Abbildungen stehen unter CC BY-SA 4.0, der Code unter der MIT-Lizenz und die Daten des Kurses unter CC0 1.0. Die Schrift der Abbildungen ist Kalam unter der SIL Open Font License 1.1. Die Quellen liegen auf GitHub.