Aufwärmen · Aufgabe 1 von 7
// B4.2 · ca. 30 Min. · Einstieg
Ein Vite-Projekt von Grund auf
Nach dieser Lektion legen Sie auf Ihrem Computer ein React-Projekt mit Vite an, erklären, welche Datei Ihre App auf die Seite bringt, und starten, bauen und prüfen es.
Danach können Sie
- Ein Vite-React-Projekt mit npm create vite anlegen, auf einer Node.js-Version, die Vite unterstützt
- Erklären, wie index.html, src/main.jsx und src/App.jsx die App auf die Seite bringen
- Den Entwicklungsserver starten, nach dist bauen und den Build mit den npm-Skripten ansehen
Vorhersagen · Aufgabe 2 von 7
Sagen Sie voraus, bevor Sie weiterlesen. npm run dev läuft, und Sie öffnen http://localhost:5173/. Welche Datei des Projekts lädt der Browser zuerst?
Üben · Aufgabe 3 von 7
node --version gibt v18.20.4 aus. Was brauchen Sie laut vite.dev für Vite?
Üben · Aufgabe 4 von 7
Ordnen Sie jeder Datei und jedem Ordner des neuen Projekts die Aufgabe zu.
Üben · Aufgabe 5 von 7
Ergänzen Sie das Modul, aus dem src/main.jsx createRoot importiert.
import { StrictMode } from 'react' import { createRoot } from '____' import './index.css' import App from './App.jsx'import { createRoot } from ''Denksport · Aufgabe 6 von 7
Knobelaufgabe. Die src/main.jsx Ihres Projekts legt wie die Vorlage <StrictMode> um App, und npm run dev liefert die Seite aus. App rendert zwei Counter-Komponenten, und Counter gibt in seinem Rumpf etwas aus. Wie viele Zeilen zeigt die Konsole, wenn die Seite lädt?
function Counter() { console.log('Counter'); const [count, setCount] = useState(0); return <button onClick={() => setCount((count) => count + 1)}>count is {count}</button>; } function App() { return ( <> <Counter /> <Counter /> </> ); }Anwenden · Aufgabe 7 von 7
Mini-Aufgabe: Ihr eigenes Vite-Projekt. Prüfen Sie in einem Terminal node --version, führen Sie dann npm create vite@latest my-app -- --template react, cd my-app, npm install und npm run dev aus und öffnen Sie die ausgegebene Local-Adresse. Während der Server läuft, ersetzen Sie src/App.jsx durch Ihre eigene Komponente und speichern: Die Seite aktualisiert sich. Stoppen Sie den Server mit Strg+C, führen Sie npm run build aus und sehen Sie in dist nach, dann führen Sie npm run preview aus und öffnen dessen Adresse.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Die Form der Vorlage, im Runner
Ein Vite-Projekt hat seinen Einstieg in src/main.jsx und seine erste Komponente in src/App.jsx und importiert ./index.css. Hier exportiert main.jsx nur App: Der Runner tut, was src/main.jsx tut, und rendert sie in #root in <StrictMode>, und er wendet index.css selbst an. App.jsx importiert einen Counter aus dessen eigener Datei, mit Endung, wie Dateien in src/ es tun. Counter nutzt die Updater-Form der Vorlage, setCount((count) => count + 1). Der Schnappschuss zeigt #root nach dem ersten Rendern.
main.jsx
import App from './App.jsx';
// In a Vite project, src/main.jsx ends with
// createRoot(document.getElementById('root')).render(
// <StrictMode>
// <App />
// </StrictMode>,
// )
// and imports './index.css'. The runner does both for this default export.
export default App;
App.jsx
import Counter from './Counter.jsx';
export default function App() {
return (
<>
<h1>my-app</h1>
<Counter />
<p>
Edit <code>src/App.jsx</code> and save to test <code>HMR</code>
</p>
</>
);
}
Counter.jsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
);
}
index.css
body {
font-family: system-ui, sans-serif;
}
Ausführen mit
npm run devAusgabe
<!-- #root -->
<div id="root"><h1>my-app</h1><button>count is 0</button><p>Edit <code>src/App.jsx</code> and save to test <code>HMR</code></p></div>- <StrictMode> rendert kein eigenes Element: #root enthält nur, was App zurückgibt.
- Counter.jsx wird als ./Counter.jsx importiert, mit Endung, wie im Ordner src/ eines Vite-Projekts.
- index.css wendet der Runner an; in Vite braucht src/main.jsx dafür import './index.css'.
- Im Browser bleibt die Seite aktiv: Klicken Sie auf den Button. In einem Vite-Projekt würde das Speichern von App.jsx sie über HMR aktualisieren.
Ändern und ausführen
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Übungen
Übung 1 von 2
Den Einstieg schreiben, wie src/main.jsx es tut
src/main.jsx rendert App in das Element aus index.html. Schreiben Sie diesen Schritt als Funktion: start(rootElement) erzeugt eine Root für rootElement, rendert <App /> in <StrictMode> hinein und gibt die Root zurück. App.jsx ist vorgegeben. Ausführen zeigt App über den Default-Export; die Tests setzen ein leeres Element auf die Seite, rufen start damit auf und klicken auf den Zähler.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Hinweise
Hinweis 1
createRoot allein zeigt nichts an; die Root braucht einen Aufruf von render.
Hinweis 2
Übernehmen Sie die Form der Vorlage: root.render(<StrictMode><App /></StrictMode>);
Hinweis 3
Beenden Sie die Funktion mit return root;
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
// For the runner, which renders this into #root.
export default App;
// What src/main.jsx does, as a function that works for any element.
export function start(rootElement) {
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>,
);
return root;
}
Auf dem eigenen Computer ausführen
Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
// For the runner, which renders this into #root.
export default App;
// What src/main.jsx does, as a function that works for any element.
export function start(rootElement) {
const root = createRoot(rootElement);
// TODO: render <App /> inside <StrictMode>, then return the root.
}
main.test.jsx
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {act, within} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {start} from './main.jsx';
// The page of index.html: an empty element for the app.
function indexHtml() {
document.body.innerHTML = '<div id="app-root"></div>';
return document.getElementById('app-root');
}
async function mount(element) {
let root;
await act(async () => {
root = start(element);
});
return root;
}
async function unmount(root) {
if (root && typeof root.unmount === 'function') await act(async () => root.unmount());
}
test('start zeigt App im Element', async () => {
const element = indexHtml();
const root = await mount(element);
const heading = within(element).queryByRole('heading', {level: 1});
assert.equal(heading?.textContent, 'my-app', 'das Element enthält ' + JSON.stringify(element.innerHTML));
await unmount(root);
});
test('Der Zähler funktioniert nach start', async () => {
const user = userEvent.setup();
const element = indexHtml();
const root = await mount(element);
const button = within(element).queryByRole('button', {name: 'count is 0'});
assert.ok(button !== null, 'im Element gibt es keinen Button "count is 0"');
await user.click(button);
await user.click(button);
assert.equal(button.textContent, 'count is 2', 'nach zwei Klicks zeigt der Button ' + JSON.stringify(button.textContent));
await unmount(root);
});
test('start gibt die Root zurück', async () => {
const element = indexHtml();
const root = await mount(element);
assert.equal(typeof root?.render, 'function', 'start hat ' + String(root) + ' zurückgegeben, nicht die Root von createRoot');
await unmount(root);
});
App.jsx
import { useState } from 'react';
export default function App() {
const [count, setCount] = useState(0);
return (
<>
<h1>my-app</h1>
<button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
</>
);
}
Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.
Programm ausführen:
npm run devFür die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.
Übung 2 von 2
Eine Komponente pro Datei
Header und Counter des Teeladens liegen jetzt in Header.jsx und Counter.jsx, wie Dateien im Ordner src/ eines Vite-Projekts. main.jsx hat noch eine alte Kopie von Counter, die nicht zählen kann. Importieren Sie Counter aus ./Counter.jsx, löschen Sie die alte Kopie und rendern Sie unter der Überschrift einen Counter mit der Beschriftung Green und einen mit Black. Jeder muss seine eigenen Klicks zählen.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Hinweise
Hinweis 1
Counter.jsx hat einen Default-Export, also importieren Sie ohne geschweifte Klammern: import Counter from './Counter.jsx';
Hinweis 2
Zwei Komponenten können in einer Datei nicht denselben Namen tragen, also muss die alte function Counter weg.
Hinweis 3
Übergeben Sie die Beschriftung als Prop: <Counter label="Green" /> und <Counter label="Black" />.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import Header from './Header.jsx';
import Counter from './Counter.jsx';
export default function App() {
return (
<>
<Header title="Tea shop" />
<Counter label="Green" />
<Counter label="Black" />
</>
);
}
Auf dem eigenen Computer ausführen
Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.jsx
import Header from './Header.jsx';
// TODO: Counter now lives in Counter.jsx. Import it from there,
// delete this old copy, and render one Counter per tea.
function Counter() {
return <button>Tea: 0</button>;
}
export default function App() {
return (
<>
<Header title="Tea shop" />
<Counter />
</>
);
}
main.test.jsx
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './main.jsx';
test('Die Seite zeigt die Überschrift Tea shop und zwei Zähler', () => {
render(<App />);
assert.equal(screen.getByRole('heading', {level: 1}).textContent, 'Tea shop', 'die Überschrift');
const labels = screen.queryAllByRole('button').map((button) => button.textContent);
assert.deepEqual(labels, ['Green: 0', 'Black: 0'], 'die Buttons zeigen ' + JSON.stringify(labels));
});
test('Green zählt seine eigenen Klicks', async () => {
const user = userEvent.setup();
render(<App />);
const green = screen.queryByRole('button', {name: 'Green: 0'});
assert.ok(green !== null, 'es gibt keinen Button "Green: 0"');
await user.click(green);
await user.click(green);
assert.equal(green.textContent, 'Green: 2', 'nach zwei Klicks zeigt Green ' + JSON.stringify(green.textContent));
const black = screen.queryByRole('button', {name: /^Black/});
assert.equal(black?.textContent, 'Black: 0', 'der Klick auf Green hat Black geändert zu ' + JSON.stringify(black?.textContent));
});
Header.jsx
export default function Header({ title }) {
return <h1>{title}</h1>;
}
Counter.jsx
import { useState } from 'react';
export default function Counter({ label }) {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((count) => count + 1)}>
{label}: {count}
</button>
);
}
Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.
Programm ausführen:
npm run devFür die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.
Häufige Fehler
Den Import von StrictMode weglassen
import { createRoot } from 'react-dom/client';
export default function App() {
return <h1>my-app</h1>;
}
// Typed from src/main.jsx without its first line, for a second root
const preview = document.createElement('div');
document.body.append(preview);
createRoot(preview).render(
<StrictMode>
<App />
</StrictMode>,
);
Was React ausgibt
ReferenceError: StrictMode is not definedWarum, und die Lösung
<StrictMode> ist eine Komponente aus React und muss wie jede andere importiert werden: import { StrictMode } from 'react'. JSX importiert sie nicht für Sie. Der Lauf stoppt mit Uncaught ReferenceError, bevor in dieser Root etwas rendert.
Die Komponente umbenennen, aber nicht ihren Export
import App from './App.jsx';
export default App;
Was React ausgibt
ReferenceError: App is not definedWarum, und die Lösung
export default App; am Ende einer Datei exportiert einen Namen, der in dieser Datei existieren muss. Nach der Umbenennung gibt es in App.jsx kein App mehr, also scheitert das Modul beim Laden, und nichts rendert. Exportieren Sie den neuen Namen, export default Shop;, oder schreiben Sie in einem Zug export default function Shop(). Die importierende Datei darf ihren eigenen Namen behalten: import App from './App.jsx' funktioniert weiter.
JSX in einer .js-Datei
import Counter from './Counter.js';
export default function App() {
return <Counter />;
}
Was React ausgibt
SyntaxError: Unexpected token '<'Warum, und die Lösung
Der Runner dieses Kurses wandelt JSX nur in .jsx-Dateien in JavaScript um, also wird Counter.js als reines JavaScript geladen, wo < keinen Ausdruck beginnen kann, und nichts läuft. vite.dev nennt .jsx und .tsx als die Dateien mit JSX-Unterstützung, und die Komponenten der Vorlage sind .jsx-Dateien. Benennen Sie die Datei in Counter.jsx um und importieren Sie sie als ./Counter.jsx. Die Formulierung ist die von V8; Firefox und Safari formulieren es anders.
React im Browser: React 19.3.0 (Entwicklungs-Build) und React Testing Library, MIT, von dieser Website ausgeliefert; JSX übersetzt TypeScript 6.0.3, Apache-2.0. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.