How Markdown Works
Understanding how Markdown works can help you make better use of this powerful tool. This chapter will dive into how Markdown is transformed from plain text into beautiful, formatted documents.
Basic Workflow
The Markdown workflow can be summarized in the following steps:
Markdown source file (.md) → Markdown parser → HTML document → Browser rendering
1. Writing the Markdown Source File
You use any text editor to create a .md
file and write content using Markdown syntax:
# My Document
This is an **important** paragraph.
## List Example
- Item 1
- Item 2
- Item 3
2. Markdown Parser Processing
The parser reads the Markdown file, recognizes syntax elements, and converts them to corresponding HTML:
<h1>My Document</h1>
<p>This is an <strong>important</strong> paragraph.</p>
<h2>List Example</h2>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
3. Browser Rendering
The generated HTML is displayed as a formatted document in the browser.
How Parsers Work
Lexical Analysis
The parser first performs lexical analysis, breaking the Markdown text into tokens:
#
recognized as a heading token**text**
recognized as a bold token- item
recognized as a list item token
Syntax Parsing
Then it performs syntax parsing to build an Abstract Syntax Tree (AST):
Document
├── Heading (level 1): "My Document"
├── Paragraph
│ ├── Text: "This is an"
│ ├── Bold: "important"
│ └── Text: "paragraph."
├── Heading (level 2): "List Example"
└── Unordered List
├── List Item: "Item 1"
├── List Item: "Item 2"
└── List Item: "Item 3"
HTML Generation
Finally, the syntax tree is traversed to generate the corresponding HTML output.
Mainstream Parsers
CommonMark
- Standard specification - Provides a unified Markdown parsing standard
- Strictly defined - Eliminates ambiguities between different implementations
- Widely supported - Adopted by multiple parsers
GitHub Flavored Markdown (GFM)
Based on CommonMark, with additions:
- Table support
- Strikethrough
- Task lists
- Autolink detection
- Syntax-highlighted code blocks
Other Parsers
Parser | Language | Features |
---|---|---|
marked | JavaScript | Fast, lightweight |
markdown-it | JavaScript | Pluggable, highly extensible |
Python-Markdown | Python | Feature-rich, plugin system |
kramdown | Ruby | Supports multiple output formats |
Pandoc | Haskell | Universal document converter |
Rendering Engines
Client-side Rendering
Parse Markdown in the browser in real time:
// Using marked.js
const html = marked.parse('# Hello World');
document.body.innerHTML = html;
Advantages:
- No server processing required
- Real-time preview
- Reduces server load
Disadvantages:
- Depends on JavaScript
- Not SEO-friendly
- Slow initial load
Server-side Rendering
Pre-generate HTML on the server:
// Node.js example
const fs = require('fs');
const marked = require('marked');
const markdown = fs.readFileSync('document.md', 'utf8');
const html = marked.parse(markdown);
Advantages:
- SEO-friendly
- Fast loading
- Does not depend on client-side JavaScript
Disadvantages:
- Server processing overhead
- Complex cache management
Static Site Generation
Pre-generate all pages at build time:
# Using VitePress
npm run build
Advantages:
- Fastest loading speed
- Best SEO
- High security
- Easy deployment
Disadvantages:
- Limited support for dynamic content
- Longer build times
Extension Mechanisms
Plugin System
Many parsers support plugin extensions:
// markdown-it plugin example
const md = require('markdown-it')()
.use(require('markdown-it-footnote'))
.use(require('markdown-it-deflist'))
.use(require('markdown-it-abbr'));
Custom Renderer
// Custom link renderer
const renderer = new marked.Renderer();
renderer.link = function(href, title, text) {
return `<a href="${href}" target="_blank">${text}</a>`;
};
Performance Optimization
Caching Strategy
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
// Only parse content in the visible area
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
parseAndRender(entry.target);
}
});
});
Streaming Processing
// Stream parsing for large files
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('large-document.md')
.pipe(markdownTransform)
.pipe(fs.createWriteStream('output.html'));
Common Issues
1. Line Break Issues
Different parsers may handle line breaks differently:
Line 1
Line 2 ← These may be parsed as the same paragraph
Line 1
Line 2 ← Two spaces at the end of the line force a line break
Line 1
Line 2 ← Blank line separates paragraphs
2. Mixing HTML
This is a mix of **Markdown** and <em>HTML</em>.
Pay attention to correct closing and nesting of HTML tags.
3. Special Character Escaping
You need to escape \* and \_ characters here.
Practical Application Scenarios
1. Blog Systems
Markdown articles → Static site generator → HTML website
2. Documentation Sites
.md documents → VitePress/Docusaurus → Online documentation
3. README Files
README.md → GitHub/GitLab → Project homepage
4. Note-taking Apps
Markdown notes → Real-time rendering → Rich text display
Next Steps
Now that you understand how Markdown works, you can: