Projekt-Struktur
Jeder Ordner und jede Datei in einem Vue-Projekt erklärt — mit direktem Vergleich zur Rails-Konvention.
Überblick
Nach npm create vue@latest mit allen Optionen entsteht eine klar strukturierte Projektstruktur. Anders als Rails setzt Vue auf eine flache Hierarchie mit wenigen, aber wichtigen Ordnern.
Convention over Configuration
Rails lebt von Konventionen: Alles hat seinen festen Platz. Vue ist flexibler — es gibt Empfehlungen, aber keine harten Regeln. Die von create-vue erzeugte Struktur ist jedoch der de-facto-Standard und sollte beibehalten werden.
Der Verzeichnisbaum
So sieht ein frisch erstelltes Vue-Projekt aus:
mein-vue-projekt/
├── public/ # Statische Dateien (Favicon, robots.txt)
│ └── favicon.ico
├── src/ # Quellcode der Anwendung
│ ├── assets/ # CSS, Bilder, Schriften
│ │ ├── base.css
│ │ ├── logo.svg
│ │ └── main.css
│ ├── components/ # Wiederverwendbare Komponenten
│ │ ├── HelloWorld.vue
│ │ ├── TheWelcome.vue
│ │ ├── WelcomeItem.vue
│ │ └── icons/
│ │ └── ...
│ ├── composables/ # Wiederverwendbare Logik (Hooks)
│ ├── router/ # Vue Router Konfiguration
│ │ └── index.ts
│ ├── stores/ # Pinia Stores (State Management)
│ │ └── counter.ts
│ ├── views/ # Seiten-Komponenten (Routen-Ziele)
│ │ ├── HomeView.vue
│ │ └── AboutView.vue
│ ├── App.vue # Wurzel-Komponente
│ └── main.ts # Einstiegspunkt der Anwendung
├── .eslintrc.cjs # ESLint-Konfiguration
├── .prettierrc.json # Prettier-Konfiguration
├── env.d.ts # TypeScript-Umgebungstypen
├── index.html # HTML-Einstiegspunkt
├── package.json # Abhängigkeiten und Skripte
├── tsconfig.json # TypeScript-Konfiguration
├── tsconfig.app.json # TypeScript-Config für die App
├── tsconfig.node.json # TypeScript-Config für Node (Vite)
└── vite.config.ts # Vite Build-Konfigurationpublic/ — Statische Dateien
Dateien in public/ werden unverändert in den Build kopiert. Hier gehören Dateien hin, die nicht durch das Build-System verarbeitet werden sollen: Favicons, robots.txt, Open-Graph-Bilder.
public/ in Rails vs. Vue
In Rails ist public/ ebenfalls für statische Dateien. Der Unterschied: Rails hat zusätzlich die Asset Pipeline (app/assets/). In Vue übernimmt src/assets/ die Rolle der Asset Pipeline, während public/ identisch funktioniert.
src/ — Der Quellcode
Alles, was du entwickelst, lebt in src/. Das ist das Äquivalent zu app/ in Rails. Schauen wir uns jeden Unterordner an:
src/assets/ — Styles und Medien
CSS-Dateien, Bilder und Schriften, die vom Build-System verarbeitet werden. Vite optimiert diese Dateien automatisch: CSS wird minimiert, Bilder werden mit Hashes versehen (Cache-Busting), und ungenutzte Assets werden entfernt.
/* Globale Styles */
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}src/components/ — Wiederverwendbare Bausteine
Hier leben alle wiederverwendbaren UI-Komponenten. Eine Komponente ist eine in sich geschlossene Einheit aus Template, Logik und optionalem Styling.
Gängige Namenskonventionen:
Base*— Basis-Komponenten (BaseButton, BaseInput)The*— Singleton-Komponenten (TheHeader, TheSidebar)App*— App-spezifische Komponenten (AppNotification)
Partials vs. Komponenten
Rails-Partials (_header.html.erb) sind reines HTML mit eingebettetem Ruby. Vue-Komponenten enthalten zusätzlich ihre eigene Logik und ihr eigenes Styling. Eine Vue-Komponente ist eher vergleichbar mit einem ViewComponent oder einem Stimulus-Controller mit zugehörigem Partial.
src/composables/ — Wiederverwendbare Logik
Composables sind Funktionen, die reaktive Logik kapseln und in mehreren Komponenten wiederverwendet werden können. Sie beginnen konventionell mit use — z.B. useAuth, useFetch, useLocalStorage.
import { ref, computed } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
function reset() {
count.value = initial
}
return { count, doubled, increment, reset }
}Concerns und Service Objects
Composables erfüllen eine ähnliche Rolle wie Rails Concerns oder Service Objects: Sie extrahieren wiederverwendbare Logik aus der Hauptklasse (bzw. Komponente). Der Unterschied: Composables sind reaktiv — sie aktualisieren automatisch alle Komponenten, die sie nutzen.
src/router/ — Routing
Die Router-Konfiguration ordnet URLs zu Vue-Komponenten zu. Jede Route definiert einen Pfad und die zugehörige Komponente:
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(globalThis._importMeta_.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// Lazy Loading: wird erst bei Bedarf geladen
component: () => import('../views/AboutView.vue')
}
]
})
export default routerroutes.rb vs. router/index.ts
In Rails definierst du Routen in config/routes.rb und sie verweisen auf Controller-Actions. In Vue definierst du Routen in router/index.ts und sie verweisen auf Komponenten. Rails-Routen laufen auf dem Server, Vue-Routen im Browser. Lazy Loading (() => import(...)) ist wie Rails' autoloading — Dateien werden erst geladen, wenn sie gebraucht werden.
src/stores/ — State Management mit Pinia
Pinia Stores verwalten den Anwendungszustand, der über mehrere Komponenten geteilt wird. Jeder Store ist eine eigene Datei mit State, Getters und Actions:
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})src/views/ — Seiten-Komponenten
Views sind die „Seiten" der Anwendung — die Komponenten, auf die der Router verweist. Sie werden oft als Container verwendet, die kleinere Komponenten zusammensetzen.
Views vs. Components
Die Trennung ist rein organisatorisch: Views sind Seiten (werden vom Router angezeigt), Components sind wiederverwendbare Bausteine. Technisch sind beides Vue-Komponenten. In Rails wäre das vergleichbar mit dem Unterschied zwischen einer View (index.html.erb) und einem Partial (_form.html.erb).
Die Wurzel-Dateien
index.html — Der Einstiegspunkt
Die einzige HTML-Datei der gesamten Anwendung. Vue mountet sich in das <div id="app">-Element:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mein Vue Projekt</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>application.html.erb vs. index.html
In Rails ist application.html.erb das Layout, in das Views eingebettet werden. In Vue ist index.html die Shell — aber der gesamte sichtbare Inhalt wird von Vue im <div id="app"> gerendert. Es gibt keine ERB-Tags, kein yield.
main.ts — App-Bootstrap
Hier wird die Vue-App erstellt, Plugins registriert und in das DOM gemountet:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './assets/main.css'
const app = createApp(App)
app.use(createPinia()) // State Management
app.use(router) // Routing
app.mount('#app') // In <div id="app"> rendernApp.vue — Die Wurzel-Komponente
Die oberste Komponente der Anwendung. Sie enthält typischerweise das Layout (Header, Navigation, Footer) und einen <RouterView /> für den seitenspezifischen Inhalt:
<template>
<header>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">Über uns</RouterLink>
</nav>
</header>
<RouterView />
</template>vite.config.ts — Build-Konfiguration
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', globalThis._importMeta_.url))
}
}
})Das @-Alias
@ ist ein Alias für src/. Statt ../../components/MyButton.vue schreibst du @/components/MyButton.vue. Das macht Imports übersichtlicher und unabhängig von der Verzeichnistiefe.
Struktur-Vergleich: Rails vs. Vue
| Rails | Vue / Vite | Funktion |
|---|---|---|
| app/ | src/ | Quellcode-Verzeichnis |
| app/views/ | src/views/ | Seiten / Templates |
| app/views/ (Partials) | src/components/ | Wiederverwendbare Teile |
| app/controllers/ | src/composables/ | Logik und Steuerung |
| app/models/ | src/stores/ | Datenhaltung |
| app/assets/ | src/assets/ | CSS, Bilder, Fonts |
| config/routes.rb | src/router/index.ts | Routing-Definition |
| config/ | vite.config.ts | Build-Konfiguration |
| public/ | public/ | Statische Dateien (identisch!) |
| Gemfile | package.json | Abhängigkeiten |
| Gemfile.lock | package-lock.json | Lock-Datei für exakte Versionen |
Was fehlt im Vergleich zu Rails?
Ein Vue-Projekt ist absichtlich schlank. Vieles, was Rails mitbringt, fehlt:
| Rails hat… | Vue braucht… |
|---|---|
| Datenbank (ActiveRecord) | Externe API (z.B. Rails-API) |
| Mailer (ActionMailer) | Backend-Service für E-Mails |
| Background Jobs (ActiveJob) | Backend-Service für Jobs |
| Authentifizierung (Devise) | Token-basierte Auth über API |
Generatoren (rails generate) | Manuelles Erstellen von Dateien |
Vue ist ein Frontend-Framework
Vue ist bewusst auf das Frontend beschränkt. Für alles, was einen Server braucht (Datenbank, Auth, E-Mails, Jobs), brauchst du ein Backend — und dafür ist Rails perfekt geeignet. Vue + Rails API ist eine sehr beliebte Kombination.
Zusammenfassung
src/ist das Äquivalent zuapp/in Rails- Komponenten (
components/) ersetzen Partials - Composables (
composables/) ersetzen Concerns und Service Objects - Stores (
stores/) ersetzen Models für clientseitigen Zustand - Views (
views/) sind die Seiten, auf die der Router verweist vite.config.tsentspricht der Build-Konfiguration- Vue hat keine Datenbank, keinen Mailer, keine Jobs — dafür brauchst du ein Backend