660 lines
11 KiB
Markdown
660 lines
11 KiB
Markdown
---
|
||
marp: true
|
||
theme: gaia
|
||
paginate: true
|
||
backgroundColor: #fff
|
||
header: "Web Engineering – DHBW Stuttgart"
|
||
footer: "Michael Czechowski – SoSe 2026"
|
||
title: Web Engineering
|
||
---
|
||
|
||
<style>
|
||
:root {
|
||
--color-foreground: #1a1a2e;
|
||
--color-highlight: #005f8a;
|
||
--color-dimmed: #4a6a7a;
|
||
}
|
||
section.invert {
|
||
--color-foreground: #fff;
|
||
}
|
||
section {
|
||
font-size: 1.7rem;
|
||
}
|
||
h1 {
|
||
color: #005f8a;
|
||
}
|
||
section.invert h1 {
|
||
color: #fff;
|
||
}
|
||
pre {
|
||
background: #0f1e2e;
|
||
color: #00b4d8;
|
||
border-radius: 8px;
|
||
border-left: 3px solid #005f8a;
|
||
}
|
||
pre code {
|
||
background: transparent;
|
||
color: inherit;
|
||
}
|
||
code {
|
||
background: #1a3a4a;
|
||
color: #00b4d8;
|
||
padding: 0.15em 0.4em;
|
||
border-radius: 4px;
|
||
}
|
||
a {
|
||
color: var(--color-highlight);
|
||
}
|
||
</style>
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _header: '' -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||

|
||
|
||
# Web Engineering
|
||
|
||
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
|
||
|
||
**Sommersemester 2026**
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Willkommen!
|
||
|
||
## 1. Sitzung: 08.05.2026
|
||
|
||
---
|
||
|
||
# Wer bist du, was machst du?
|
||
|
||
- B.Sc. Wirtschaftsinformatik (Leibniz-FH Hannover)
|
||
- Angestellt bei PONS Langenscheidt GmbH in Stuttgart
|
||
- Honorardozent und Berater
|
||
|
||
**Bisherige Vorlesungen (DHBW/Leibniz-FH):**
|
||
- Social Engineering, Mobile Medien, Web Eng
|
||
|
||
---
|
||
|
||
# Vorlesungsplan
|
||
|
||
| Sitzung | Datum | Thema |
|
||
|---------|-------|-------|
|
||
| 1 | 08.05. | Intro, Projektgruppen, Internet 101 |
|
||
| 2 | 15.05. | HTML und CSS (Frameworks) |
|
||
| 3 | 22.05. | JS (Frameworks) und npm |
|
||
| 4 | 29.05. | nodeJS: Scripting, Running and Building |
|
||
| 5 | 05.06. | Express API, CRUD und "Middlewares" |
|
||
| 6 | 12.06. | **Projektwerkstattbericht** |
|
||
| 7 | 19.06. | Testing (unit, integration, end-to-end) |
|
||
| 8 | 26.06. | TypeScript |
|
||
| 9 | 03.07. | Docker, Proxies and DBs |
|
||
| 10 | 10.07. | Wrap-up & Projektsupport |
|
||
| 11 | 17.07. | **Präsentation** |
|
||
|
||
---
|
||
|
||
# Prüfungsleistung – Übersicht
|
||
|
||
Eigenes Projekt: **Web-App** ODER **Backend** (API/CLI).
|
||
|
||
**Grundanforderungen (75 Punkte)**
|
||
|
||
| Punkte | Bereich |
|
||
|--------|---------|
|
||
| 20 | Idee, Konzeption, Planung |
|
||
| 5 | Plattformunabhängigkeit |
|
||
| 25 | Clean Code (KISS, SOLID, DI, Testing, Error Handling) |
|
||
| 15 | Präsentation |
|
||
| 10 | Dokumentation |
|
||
|
||
**Zusatzpunkte (max. 10):** TypeScript, Docker, Dev/Prod-Parity, `.env`, npm-Publish, Domain, HTTPS, Responsive Design.
|
||
|
||
---
|
||
|
||
# Prüfungsleistung – Idee & Konzeption (20 P.)
|
||
|
||
- "Powerpoint"-Präsentation + Folien
|
||
- Aussagekräftiger Arbeitstitel + Beschreibung
|
||
- **Elevator Pitch** (max. 1 Min.)
|
||
- Repo-Link (GitHub / GitLab / Codeberg / BitBucket)
|
||
- **Logs**: wie hat sich Projekt verändert vs. Ursprungsidee?
|
||
- Schematischer Projektaufbau (UML o. Ä.)
|
||
|
||
---
|
||
|
||
# Prüfungsleistung – Clean Code (25 P.)
|
||
|
||
- `README.md` (clone, start, contribute)
|
||
- **KISS** – Keep It Simple, Stupid
|
||
- **SOLID** – SRP, OCP, LSP, ISP, DIP
|
||
- **DI** – Dependency Injection
|
||
- **Testing-Pyramide** (Unit, Integration, E2E)
|
||
- **Exception/Error Handling**
|
||
|
||
---
|
||
|
||
# Prüfungsleistung – Abgabe & Termine
|
||
|
||
- **Code-Upload:** bis 27.07.
|
||
- **Präsentation:** 17.07. (Gruppen, ~10 Min.)
|
||
|
||
Details: https://git.dailysh.it/DHBW/pruefungsleistung
|
||
|
||
---
|
||
|
||
# Internet 101 – Zeitleiste
|
||
|
||
| Jahr | Ereignis |
|
||
|------|----------|
|
||
| 1966 | ARPANET |
|
||
| 1969 | RFCs |
|
||
| 1986 | IETF |
|
||
| 1992 | Internet Society |
|
||
| 1974 | TCP/IP und HTTP(S) |
|
||
| 1987 | Domain Names und DNS |
|
||
| 1993 | "Erster" Browser: Mosaic |
|
||
| 1994 | W3C (HTML, XML, CSS, SVG, WCAG etc.) |
|
||
| 1995 | ECMAScript (JS) |
|
||
| 2006/08 | V8 JS Runtime Engine |
|
||
| Heute | 1,3 Mio. km Unterseekabel |
|
||
|
||
---
|
||
|
||
# Internet 101 – Browser Request
|
||
|
||
```
|
||
Browser Server
|
||
| |
|
||
|-- GET /products -------------->|
|
||
|<-- HTTP 200 + index.html -----|
|
||
| |
|
||
|-- GET /script.js ------------->|
|
||
|<-- HTTP 200 + script.js ------|
|
||
| |
|
||
|-- GET /api/products ---------->|
|
||
|<-- HTTP 200 + JSON ------------|
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _header: '' -->
|
||
<!-- _footer: '' -->
|
||
|
||

|
||
|
||
|
||
---
|
||
|
||
# HTTP – Hypertext Transfer Protocol
|
||
|
||
**Request:**
|
||
```
|
||
GET /products HTTP/1.1
|
||
Host: localhost:8080
|
||
```
|
||
|
||
**Response:**
|
||
```
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
|
||
[{"id": 1, "name": "Produkt A"}, ...]
|
||
```
|
||
|
||
---
|
||
|
||
# Entwicklungsumgebung
|
||
|
||
**Tools:**
|
||
- Node.js + npm
|
||
- VS Code
|
||
- Git
|
||
- Chrome DevTools
|
||
|
||
---
|
||
|
||
# Versionierung mit Git
|
||
|
||
```bash
|
||
git init
|
||
git add .
|
||
git commit -m "initial commit"
|
||
git remote add origin https://github.com/user/repo.git
|
||
git push -u origin main
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _header: '' -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||
# HTML – Hypertext Markup Language
|
||
|
||
---
|
||
|
||
# HTML Grundlagen
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Meine Seite</title>
|
||
</head>
|
||
<body>
|
||
<h1>Überschrift</h1>
|
||
<p>Textabsatz</p>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
---
|
||
|
||
# Semantisches HTML
|
||
|
||
```html
|
||
<header>Navigationsbereich</header>
|
||
<nav>Navigation</nav>
|
||
<main>
|
||
<article>
|
||
<section>
|
||
<h2>Überschrift</h2>
|
||
<p>Inhalt</p>
|
||
</section>
|
||
</article>
|
||
</main>
|
||
<footer>Fußzeile</footer>
|
||
```
|
||
|
||
---
|
||
|
||
# HTML – Tabellen und Listen
|
||
|
||
```html
|
||
<table>
|
||
<thead>
|
||
<tr><th>Name</th><th>Preis</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Produkt A</td><td>29,99 €</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<ul>
|
||
<li>Punkt 1</li>
|
||
<li>Punkt 2</li>
|
||
</ul>
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _header: '' -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||
# CSS – Cascading Style Sheets
|
||
|
||
---
|
||
|
||
# CSS Grundlagen
|
||
|
||
```css
|
||
/* Element */
|
||
body { font-family: Arial, sans-serif; }
|
||
|
||
/* Klasse */
|
||
.button { background: #005f8a; color: white; }
|
||
|
||
/* ID */
|
||
#header { height: 60px; }
|
||
|
||
/* Attribut */
|
||
input[type="text"] { border: 1px solid #ccc; }
|
||
```
|
||
|
||
---
|
||
|
||
# CSS Layout – Flexbox
|
||
|
||
```css
|
||
.container {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# CSS Layout – Grid
|
||
|
||
```css
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 20px;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# CSS – Box Model
|
||
|
||
```css
|
||
.box {
|
||
margin: 10px;
|
||
border: 2px solid #005f8a;
|
||
padding: 20px;
|
||
width: 200px;
|
||
box-sizing: border-box;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# CSS Responsive
|
||
|
||
```css
|
||
@media (max-width: 768px) {
|
||
.grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _header: '' -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||
# JavaScript
|
||
|
||
---
|
||
|
||
# JavaScript Grundlagen
|
||
|
||
```javascript
|
||
// Variablen
|
||
let name = "Welt";
|
||
const alter = 25;
|
||
|
||
// Funktionen
|
||
function gruss(name) {
|
||
return `Hallo, ${name}!`;
|
||
}
|
||
|
||
// Arrow Functions
|
||
const add = (a, b) => a + b;
|
||
|
||
// Arrays
|
||
const arr = [1, 2, 3];
|
||
arr.map(x => x * 2); // [2, 4, 6]
|
||
```
|
||
|
||
---
|
||
|
||
# DOM Manipulation
|
||
|
||
```javascript
|
||
// Element auswählen
|
||
const title = document.querySelector('h1');
|
||
|
||
// Inhalt ändern
|
||
title.textContent = 'Neue Überschrift';
|
||
|
||
// Event Listener
|
||
button.addEventListener('click', () => {
|
||
alert('Geklickt!');
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
# Fetch API
|
||
|
||
```javascript
|
||
fetch('/api/products')
|
||
.then(res => res.json())
|
||
.then(data => {
|
||
console.log(data);
|
||
})
|
||
.catch(err => console.error(err));
|
||
```
|
||
|
||
---
|
||
|
||
# Async/Await
|
||
|
||
```javascript
|
||
async function loadProducts() {
|
||
try {
|
||
const res = await fetch('/api/products');
|
||
const data = await res.json();
|
||
return data;
|
||
} catch (err) {
|
||
console.error('Fehler:', err);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _header: '' -->
|
||
<!-- _footer: '' -->
|
||
|
||

|
||
|
||
|
||
---
|
||
|
||
# JavaScript Frameworks – Kategorien
|
||
|
||
| Kategorie | Beispiele |
|
||
|-----------|-----------|
|
||
| **CSS Frameworks** | Tailwind, Bootstrap, shadcn/ui |
|
||
| **Frontend Frameworks** | React, Vue, Svelte, Astro |
|
||
| **Rendering / Meta** | Next.js, Nuxt, Gatsby |
|
||
| **Build Tools / Bundler** | Webpack, Vite, Parcel, esbuild |
|
||
| **Backend Frameworks** | Express, Fastify, NestJS |
|
||
|
||
---
|
||
|
||
# Vanilla JS – Counter
|
||
|
||
```html
|
||
<button id="myButton">Clicked 0 times</button>
|
||
<script>
|
||
let count = 0;
|
||
const btn = document.getElementById('myButton');
|
||
btn.addEventListener('click', () => {
|
||
count++;
|
||
btn.textContent = `Clicked ${count} times`;
|
||
});
|
||
</script>
|
||
```
|
||
|
||
✓ hohe Kompatibilität, simpel
|
||
✗ State-Management, Re-Rendering wird schnell komplex
|
||
|
||
---
|
||
|
||
# React – Counter (JSX)
|
||
|
||
```jsx
|
||
import { useState } from "react";
|
||
|
||
function MyButton() {
|
||
const [count, setCount] = useState(0);
|
||
|
||
return (
|
||
<button onClick={() => setCount(count + 1)}>
|
||
Clicked {count} times
|
||
</button>
|
||
);
|
||
}
|
||
```
|
||
|
||
- **Component-Based** Architecture
|
||
- **Virtual DOM**, JSX, Hooks
|
||
- großes Ökosystem (React Router, Redux, …)
|
||
|
||
---
|
||
|
||
# Vue 3 – Composition API
|
||
|
||
```vue
|
||
<script setup>
|
||
import { ref } from "vue";
|
||
const count = ref(0);
|
||
</script>
|
||
|
||
<template>
|
||
<button @click="count++">
|
||
Clicked {{ count }} times
|
||
</button>
|
||
</template>
|
||
```
|
||
|
||
- **Reactive Data Binding**
|
||
- Single-File Components (`.vue`)
|
||
- Vue Router, Pinia (State)
|
||
|
||
---
|
||
|
||
# Svelte – Counter
|
||
|
||
```svelte
|
||
<script>
|
||
let count = 0;
|
||
</script>
|
||
|
||
<button on:click={() => count += 1}>
|
||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||
</button>
|
||
```
|
||
|
||
- **Compiler-Based** – kein Runtime-Framework
|
||
- Reactive Assignments (`let`)
|
||
- Built-in Animationen
|
||
|
||
---
|
||
|
||
# Astro – Content-First
|
||
|
||
```astro
|
||
---
|
||
const greeting = "Hallo DHBW";
|
||
---
|
||
<html>
|
||
<body>
|
||
<h1>{greeting}</h1>
|
||
<MyReactButton client:load />
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
- **Islands Architecture** – Komponenten mehrerer Frameworks mischen
|
||
- Default: kein JS im Output (nur statisches HTML)
|
||
- Hydration on demand (`client:load`, `client:visible`)
|
||
|
||
---
|
||
|
||
# Rendering Frameworks
|
||
|
||
| | Frontend | SSR | SSG | File Routing |
|
||
|---|---|---|---|---|
|
||
| **Next.js** | React | ✓ | ✓ | `/app/page.js` |
|
||
| **Nuxt** | Vue | ✓ | ✓ | `/pages/*.vue` |
|
||
| **Gatsby** | React | – | ✓ | GraphQL Data |
|
||
|
||
---
|
||
|
||
# Build Tools – Webpack
|
||
|
||
- Module Bundling (JS, CSS, Images)
|
||
- Module Loaders + großes Plugin-Ökosystem
|
||
- Code Splitting, HMR
|
||
- Konfigurations-lastig (`webpack.config.js`)
|
||
|
||
→ Standard für viele Legacy-Setups.
|
||
|
||
---
|
||
|
||
# Build Tools – Vite
|
||
|
||
```javascript
|
||
// vite.config.js
|
||
import { defineConfig } from "vite";
|
||
|
||
export default defineConfig({
|
||
server: {
|
||
proxy: {
|
||
"/api": {
|
||
target: "http://localhost:4567",
|
||
changeOrigin: true,
|
||
rewrite: (path) => path.replace(/^\/api/, "")
|
||
}
|
||
}
|
||
}
|
||
});
|
||
```
|
||
|
||
- Rollup unter der Haube, **HMR mit ES Modules**
|
||
- Tree-Shaking, Code Splitting, React + Vue out-of-the-box
|
||
|
||
---
|
||
|
||
# Build Tools – Parcel
|
||
|
||
```json
|
||
{
|
||
"name": "my-project",
|
||
"source": "src/index.html",
|
||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||
"scripts": {
|
||
"start": "parcel",
|
||
"build": "parcel build"
|
||
},
|
||
"devDependencies": {
|
||
"parcel": "latest"
|
||
}
|
||
}
|
||
```
|
||
|
||
- **Zero-Config**
|
||
- Auto-Resolution, HMR
|
||
- TS / CSS-Preprocessors out-of-the-box
|
||
|
||
---
|
||
|
||
# Microfrontends
|
||
|
||
- Mehrere unabhängige Frontend-Apps in einer Page
|
||
- Module Federation (Webpack 5)
|
||
- **single-spa**: https://single-spa.js.org/
|
||
- Anwendungsfall: große Teams, unterschiedliche Tech-Stacks pro Domain
|
||
|
||
→ Komplexitätskosten: nur bei echtem Skalierungsbedarf.
|
||
|
||
---
|
||
|
||
# Demo-Repos
|
||
|
||
- https://github.com/nextlevelshit/dhbw-client-js (Counter, multi-framework)
|
||
- https://github.com/nextlevelshit/node-cache-api (API + Tests)
|
||
- https://github.com/nextlevelshit/dhbw-docker (Compose-Stack)
|
||
|
||
---
|
||
|
||
# Fragen?
|
||
|
||
1. Was ist der Unterschied zwischen `let` und `const`?
|
||
2. Wie funktioniert `fetch()`?
|
||
3. Was macht `box-sizing: border-box`? |