Wie Markdown funktioniert
Das Verständnis, wie Markdown funktioniert, hilft Ihnen, dieses leistungsstarke Werkzeug besser zu nutzen. In diesem Kapitel erfahren Sie, wie aus einfachem Text ein schön formatiertes Dokument wird.
Grundlegender Ablauf
Der Markdown-Workflow lässt sich in folgende Schritte zusammenfassen:
Markdown-Quelldatei (.md) → Markdown-Parser → HTML-Dokument → Browser-Rendering
1. Schreiben der Markdown-Quelldatei
Sie verwenden einen beliebigen Texteditor, um eine .md
-Datei zu erstellen und Inhalte mit Markdown-Syntax zu schreiben:
# Mein Dokument
Dies ist ein **wichtiger** Absatz.
## Listenbeispiel
- Element 1
- Element 2
- Element 3
2. Verarbeitung durch den Markdown-Parser
Der Parser liest die Markdown-Datei, erkennt Syntaxelemente und wandelt sie in entsprechendes HTML um:
<h1>Mein Dokument</h1>
<p>Dies ist ein <strong>wichtiger</strong> Absatz.</p>
<h2>Listenbeispiel</h2>
<ul>
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
</ul>
3. Browser-Rendering
Das generierte HTML wird im Browser als formatiertes Dokument angezeigt.
Wie Parser arbeiten
Lexikalische Analyse
Der Parser führt zuerst eine lexikalische Analyse durch und zerlegt den Markdown-Text in Tokens:
#
wird als Überschriften-Token erkannt**text**
als Fett-Token- element
als Listenelement-Token
Syntaxanalyse
Anschließend erfolgt die Syntaxanalyse zur Erstellung eines abstrakten Syntaxbaums (AST):
Dokument
├── Überschrift (Ebene 1): "Mein Dokument"
├── Absatz
│ ├── Text: "Dies ist ein"
│ ├── Fett: "wichtiger"
│ └── Text: "Absatz."
├── Überschrift (Ebene 2): "Listenbeispiel"
└── Ungeordnete Liste
├── Listenelement: "Element 1"
├── Listenelement: "Element 2"
└── Listenelement: "Element 3"
HTML-Generierung
Abschließend wird der Syntaxbaum durchlaufen und das entsprechende HTML erzeugt.
Gängige Parser
CommonMark
- Standard-Spezifikation – Einheitlicher Parsing-Standard
- Streng definiert – Beseitigt Mehrdeutigkeiten zwischen Implementierungen
- Weit verbreitet – Von vielen Parsern übernommen
GitHub Flavored Markdown (GFM)
Basiert auf CommonMark, mit Erweiterungen:
- Tabellenunterstützung
- Durchgestrichen
- Aufgabenlisten
- Autolink-Erkennung
- Syntaxhervorhebung für Codeblöcke
Weitere Parser
Parser | Sprache | Merkmale |
---|---|---|
marked | JavaScript | Schnell, leichtgewichtig |
markdown-it | JavaScript | Erweiterbar, viele Plugins |
Python-Markdown | Python | Funktionsreich, Plugin-System |
kramdown | Ruby | Unterstützt mehrere Ausgabeformate |
Pandoc | Haskell | Universeller Dokumentenkonverter |
Rendering-Engines
Clientseitiges Rendering
Markdown wird im Browser in Echtzeit geparst:
// Mit marked.js
const html = marked.parse('# Hallo Welt');
document.body.innerHTML = html;
Vorteile:
- Keine Serververarbeitung nötig
- Echtzeit-Vorschau
- Entlastet den Server
Nachteile:
- Abhängig von JavaScript
- Nicht SEO-freundlich
- Langsamer initialer Ladevorgang
Serverseitiges Rendering
HTML wird auf dem Server vorab generiert:
// Node.js Beispiel
const fs = require('fs');
const marked = require('marked');
const markdown = fs.readFileSync('dokument.md', 'utf8');
const html = marked.parse(markdown);
Vorteile:
- SEO-freundlich
- Schnelles Laden
- Keine Abhängigkeit von clientseitigem JavaScript
Nachteile:
- Serverlast
- Komplexes Cache-Management
Statische Seitengenerierung
Alle Seiten werden beim Build vorab generiert:
# Mit VitePress
npm run build
Vorteile:
- Schnellste Ladezeiten
- Beste SEO
- Hohe Sicherheit
- Einfache Bereitstellung
Nachteile:
- Begrenzte Unterstützung für dynamische Inhalte
- Längere Build-Zeiten
Erweiterungsmechanismen
Plugin-System
Viele Parser unterstützen Plugin-Erweiterungen:
// markdown-it Plugin-Beispiel
const md = require('markdown-it')()
.use(require('markdown-it-footnote'))
.use(require('markdown-it-deflist'))
.use(require('markdown-it-abbr'));
Benutzerdefinierter Renderer
// Benutzerdefinierter Link-Renderer
const renderer = new marked.Renderer();
renderer.link = function(href, title, text) {
return `<a href="${href}" target="_blank">${text}</a>`;
};
Performance-Optimierung
Caching-Strategie
const cache = new Map();
function parseMarkdown(content) {
const hash = generateHash(content);
if (cache.has(hash)) {
return cache.get(hash);
}
const result = marked.parse(content);
cache.set(hash, result);
return result;
}
Lazy Loading
// Nur sichtbaren Bereich parsen
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
parseAndRender(entry.target);
}
});
});
Streaming-Verarbeitung
// Stream-Parsing für große Dateien
const fs = require('fs');
const { Transform } = require('stream');
const markdownTransform = new Transform({
transform(chunk, encoding, callback) {
const html = marked.parse(chunk.toString());
callback(null, html);
}
});
fs.createReadStream('großes-dokument.md')
.pipe(markdownTransform)
.pipe(fs.createWriteStream('output.html'));
Häufige Probleme
1. Zeilenumbruch-Probleme
Verschiedene Parser behandeln Zeilenumbrüche unterschiedlich:
Zeile 1
Zeile 2 ← Kann als gleicher Absatz geparst werden
Zeile 1
Zeile 2 ← Zwei Leerzeichen am Zeilenende erzwingen einen Zeilenumbruch
Zeile 1
Zeile 2 ← Leere Zeile trennt Absätze
2. HTML-Mischung
Dies ist eine Mischung aus **Markdown** und <em>HTML</em>.
Achten Sie auf korrektes Schließen und Verschachteln von HTML-Tags.
3. Escaping von Sonderzeichen
Hier müssen Sie \* und \_ escapen.
Praktische Anwendungsszenarien
1. Blogsysteme
Markdown-Artikel → Statischer Seitengenerator → HTML-Website
2. Dokumentationsseiten
.md-Dokumente → VitePress/Docusaurus → Online-Dokumentation
3. README-Dateien
README.md → GitHub/GitLab → Projekt-Homepage
4. Notiz-Apps
Markdown-Notizen → Echtzeit-Rendering → Rich-Text-Anzeige
Nächste Schritte
Jetzt, da Sie wissen, wie Markdown funktioniert, können Sie: