feat(docs): enhance interactive demos and improve documentation

- Add new interactive components for frontend routing, browser rendering pipeline, and database transactions
- Improve existing demos with better visuals, explanations, and examples
- Update documentation structure and content for better clarity
- Add new utility scripts and update package.json with new commands
- Fix formatting and alignment in documentation tables
This commit is contained in:
sanbuphy
2026-02-13 22:10:03 +08:00
parent 599052b2e0
commit d174ceea32
88 changed files with 26273 additions and 15539 deletions
@@ -1,11 +1,16 @@
<template>
<div class="concurrency-model-demo">
<div class="demo-header">
<h4>并发模型对比</h4>
<p class="subtitle">不同语言处理并发请求的方式</p>
<span class="icon">🔄</span>
<span class="title">并发模型</span>
<span class="subtitle">不同语言处理多任务的方式</span>
</div>
<div class="model-grid">
<div class="intro-text">
想象你在<span class="highlight">餐厅工作</span>有的餐厅多个服务员同时服务多线程有的只有一个服务员但动作极快事件循环有的像流水线一样分工协作协程
</div>
<div class="models-grid">
<div
v-for="model in models"
:key="model.name"
@@ -14,84 +19,63 @@
@click="selectedModel = model.name"
>
<div class="model-icon">{{ model.icon }}</div>
<h5>{{ model.name }}</h5>
<div class="model-tag">{{ model.tag }}</div>
<div class="model-name">{{ model.name }}</div>
<div class="model-lang">{{ model.language }}</div>
<div class="model-desc">{{ model.description }}</div>
</div>
</div>
<transition name="fade" mode="out-in">
<div :key="selectedModel" class="model-detail">
<Transition name="fade" mode="out-in">
<div v-if="selectedModel" :key="selectedModel" class="model-detail">
<div class="detail-header">
<h5>{{ getModelDetail(selectedModel).title }}</h5>
<div class="model-stats">
<div class="stat">
<span class="stat-label">并发能力</span>
<div class="stat-bar">
<div
class="stat-fill"
:style="{
width: getModelDetail(selectedModel).concurrency + '%'
}"
></div>
</div>
<h6>{{ getModelInfo().title }}</h6>
</div>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-label">并发能力</span>
<div class="stat-bar">
<div class="stat-fill" :style="{ width: getModelInfo().concurrency + '%' }"></div>
</div>
<div class="stat">
<span class="stat-label">内存开销</span>
<div class="stat-bar">
<div
class="stat-fill memory"
:style="{ width: getModelDetail(selectedModel).memory + '%' }"
></div>
</div>
</div>
<div class="stat">
<span class="stat-label">CPU 利用率</span>
<div class="stat-bar">
<div
class="stat-fill cpu"
:style="{ width: getModelDetail(selectedModel).cpu + '%' }"
></div>
</div>
</div>
<div class="stat-item">
<span class="stat-label">内存开销</span>
<div class="stat-bar">
<div class="stat-fill memory" :style="{ width: getModelInfo().memory + '%' }"></div>
</div>
</div>
</div>
<div class="code-example">
<h6>代码示例</h6>
<pre><code>{{ getModelDetail(selectedModel).code }}</code></pre>
</div>
<div class="visualization">
<h6>并发可视化</h6>
<ConcurrencyVisualization :model="selectedModel" />
<code>{{ getModelInfo().code }}</code>
</div>
<div class="pros-cons">
<div class="pros">
<h6>优势</h6>
<strong> 优势</strong>
<ul>
<li v-for="pro in getModelDetail(selectedModel).pros" :key="pro">
{{ pro }}
</li>
<li v-for="pro in getModelInfo().pros" :key="pro">{{ pro }}</li>
</ul>
</div>
<div class="cons">
<h6>劣势</h6>
<strong> 劣势</strong>
<ul>
<li v-for="con in getModelDetail(selectedModel).cons" :key="con">
{{ con }}
</li>
<li v-for="con in getModelInfo().cons" :key="con">{{ con }}</li>
</ul>
</div>
</div>
</div>
</transition>
</Transition>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>Go 的协程适合高并发 I/OJava 的线程池适合稳定的企业级应用Node.js 的事件循环适合简单的 I/O 密集型任务根据场景选择而不是盲目追求"并发数"
</div>
</div>
</template>
<script setup>
import { ref, defineComponent } from 'vue'
import { ref } from 'vue'
const selectedModel = ref('Goroutine')
@@ -99,624 +83,244 @@ const models = [
{
name: 'Goroutine',
icon: '🐹',
tag: 'Go',
description: '轻量级协程,百万级并发'
language: 'Go',
description: '轻量级协程'
},
{
name: 'Thread',
name: 'Thread Pool',
icon: '🧵',
tag: 'Java',
description: '传统线程池,成熟稳定'
language: 'Java',
description: '线程池'
},
{
name: 'Async/Await',
name: 'Event Loop',
icon: '⚡',
tag: 'Node.js',
description: '事件循环,非阻塞 I/O'
language: 'Node.js',
description: '事件循环'
},
{
name: 'Async/Await',
icon: '🦀',
tag: 'Rust',
description: '零成本抽象,高性能'
language: 'Rust',
description: '异步运行时'
}
]
const modelDetails = {
const modelInfo = {
Goroutine: {
title: 'Go Goroutine (协程)',
concurrency: 95,
memory: 90,
cpu: 85,
code: `// Go: 启动 10 万个协程
package main
import (
"fmt"
"time"
)
func task(id int) {
fmt.Printf("Task %d\\n", id)
}
func main() {
for i := 0; i < 100000; i++ {
go task(i) // 启动协程
}
time.Sleep(time.Second)
}`,
pros: [
'✅ 轻量级:每个协程仅 2KB 栈内存',
'✅ 可创建百万级协程',
'✅ 语法简洁(go 关键字)',
'✅ 通信顺序进程(CSP)模型'
],
cons: [
'❌ 需要手动管理协程生命周期',
'❌ 错误处理繁琐(if err != nil',
'❌ 不如线程模型成熟'
]
code: 'go func() { /* 任务 */ }()',
pros: ['轻量级(2KB 栈内存)', '可创建百万级协程', '语法简洁'],
cons: ['需要手动管理生命周期', '错误处理繁琐']
},
Thread: {
title: 'Java Thread (线程池)',
'Thread Pool': {
title: 'Java Thread Pool (线程池)',
concurrency: 70,
memory: 40,
cpu: 80,
code: `// Java: 线程池处理并发
import java.util.concurrent.*;
ExecutorService executor =
Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId);
});
}
executor.shutdown();`,
pros: [
'✅ 成熟稳定,企业级应用首选',
'✅ 异常处理机制完善',
'✅ 调试工具丰富(JConsole、VisualVM',
'✅ 线程池优化(ExecutorService'
],
cons: [
'❌ 线程重(1-2MB 栈内存)',
'❌ 上下文切换开销大',
'❌ 并发数受限(通常 < 10000',
'❌ 锁竞争复杂'
]
code: 'executor.submit(() -> { /* 任务 */ });',
pros: ['成熟稳定', '异常处理完善', '工具丰富'],
cons: ['线程重(1-2MB 栈)', '上下文切换开销大']
},
'Async/Await': {
title: 'Node.js Async/Await (事件循环)',
'Event Loop': {
title: 'Node.js Event Loop (事件循环)',
concurrency: 85,
memory: 75,
cpu: 60,
code: `// Node.js: 异步处理
const axios = require('axios');
async function fetch(id) {
const response = await axios.get(url);
console.log(\`Task \${id}\`);
}
async function main() {
const tasks = [];
for (let i = 0; i < 10000; i++) {
tasks.push(fetch(i));
}
await Promise.all(tasks);
}
main();`,
pros: [
'✅ 适合 I/O 密集型应用',
'✅ 单线程,无锁竞争',
'✅ 事件驱动,非阻塞',
'✅ 语法优雅(async/await'
],
cons: [
'❌ 单线程,CPU 密集型性能差',
'❌ 回调地狱(虽然 async/await 有改善)',
'❌ 无法利用多核 CPU(需要 Worker Threads',
'❌ 错误堆栈复杂'
]
code: 'async function task() { /* 任务 */ }',
pros: ['适合 I/O 密集型', '单线程无锁竞争', '语法优雅'],
cons: ['CPU 密集型性能差', '无法利用多核']
},
RustAsync: {
'Async/Await': {
title: 'Rust Async/Await (零成本抽象)',
concurrency: 90,
memory: 95,
cpu: 90,
code: `// Rust: 异步运行时(tokio
use tokio::task;
async fn task(id: u32) {
println!("Task {}", id);
}
#[tokio::main]
async fn main() {
let mut handles = vec![];
for i in 0..100_000 {
let handle = task::spawn(async move {
task(i).await;
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
}`,
pros: [
'✅ 零成本抽象(编译成状态机)',
'✅ 内存安全(编译时保证)',
'✅ 性能接近手动管理',
'✅ 无 GC,无运行时开销'
],
cons: [
'❌ 学习曲线极其陡峭',
'❌ 需要运行时(tokio、async-std',
'❌ 编译时间长',
'❌ 生态不如 Go/Node.js 成熟'
]
code: 'task::spawn(async move { /* 任务 */ });',
pros: ['零成本抽象', '内存安全', '性能接近手动管理'],
cons: ['学习曲线陡峭', '需要运行时']
}
}
const getModelDetail = (model) => {
if (model === 'Async/Await') {
return selectedModel.value === 'Node.js'
? modelDetails['Async/Await']
: modelDetails.RustAsync
}
return modelDetails[model] || modelDetails.Goroutine
}
// ConcurrencyVisualization component
const ConcurrencyVisualization = defineComponent({
name: 'ConcurrencyVisualization',
props: {
model: String
},
template: `
<div class="concurrency-viz">
<div class="viz-container">
<div class="task-queue">
<div class="queue-label">任务队列</div>
<div class="queue-items">
<div
v-for="i in 20"
:key="i"
class="queue-item"
:class="{ processing: i <= activeWorkers }"
>
Task {{ i }}
</div>
</div>
</div>
<div class="workers">
<div class="workers-label">{{ workersLabel }}</div>
<div class="worker-pool">
<div
v-for="i in maxWorkers"
:key="i"
class="worker"
:class="{ active: i <= activeWorkers }"
>
{{ i <= activeWorkers ? '⚡' : '💤' }}
</div>
</div>
</div>
</div>
</div>
`,
setup(props) {
const workersLabel = computed(() => {
const labels = {
Goroutine: 'Goroutines (可无限创建)',
Thread: 'Thread Pool (固定数量)',
'Async/Await': 'Event Loop (单线程)',
RustAsync: 'Async Tasks (可无限创建)'
}
return labels[props.model] || 'Workers'
})
const maxWorkers = computed(() => {
const counts = {
Goroutine: 100,
Thread: 10,
'Async/Await': 1,
RustAsync: 100
}
return counts[props.model] || 10
})
const activeWorkers = computed(() => {
const actives = {
Goroutine: 100,
Thread: 10,
'Async/Await': 1,
RustAsync: 100
}
return actives[props.model] || 10
})
return {
workersLabel,
maxWorkers,
activeWorkers
}
}
})
</script>
<script>
import { computed, defineComponent } from 'vue'
export default {
components: {
ConcurrencyVisualization: defineComponent({
name: 'ConcurrencyVisualization',
props: {
model: String
},
template: `
<div class="concurrency-viz">
<div class="viz-container">
<div class="task-queue">
<div class="queue-label">任务队列</div>
<div class="queue-items">
<div
v-for="i in 20"
:key="i"
class="queue-item"
:class="{ processing: i <= activeWorkers }"
>
Task {{ i }}
</div>
</div>
</div>
<div class="workers">
<div class="workers-label">{{ workersLabel }}</div>
<div class="worker-pool">
<div
v-for="i in displayWorkers"
:key="i"
class="worker"
:class="{ active: i <= activeWorkers }"
>
{{ i <= activeWorkers ? '⚡' : '💤' }}
</div>
</div>
</div>
</div>
</div>
`,
setup(props) {
const workersLabel = computed(() => {
const labels = {
Goroutine: 'Goroutines (可无限创建)',
Thread: 'Thread Pool (固定数量)',
'Async/Await': 'Event Loop (单线程)',
RustAsync: 'Async Tasks (可无限创建)'
}
return labels[props.model] || 'Workers'
})
const maxWorkers = computed(() => {
const counts = {
Goroutine: 20,
Thread: 10,
'Async/Await': 1,
RustAsync: 20
}
return counts[props.model] || 10
})
const activeWorkers = computed(() => {
const actives = {
Goroutine: 20,
Thread: 10,
'Async/Await': 1,
RustAsync: 20
}
return actives[props.model] || 10
})
const displayWorkers = computed(() => {
// 限制显示数量,避免 DOM 过多
return Math.min(maxWorkers.value, 20)
})
return {
workersLabel,
maxWorkers,
activeWorkers,
displayWorkers
}
}
})
}
const getModelInfo = () => {
return modelInfo[selectedModel.value] || modelInfo.Goroutine
}
</script>
<style scoped>
.concurrency-model-demo {
background: var(--vp-c-bg);
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
}
.demo-header {
text-align: center;
margin-bottom: 2rem;
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.demo-header h4 {
margin: 0 0 0.5rem 0;
color: var(--vp-c-brand);
font-size: 1.5rem;
.demo-header .icon {
font-size: 1.25rem;
}
.subtitle {
margin: 0;
.demo-header .title {
font-weight: bold;
font-size: 1rem;
}
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.95rem;
font-size: 0.85rem;
margin-left: 0.5rem;
}
.model-grid {
.intro-text {
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.6;
margin-bottom: 1rem;
padding: 0.75rem;
background: var(--vp-c-bg);
border-radius: 6px;
}
.intro-text .highlight {
color: var(--vp-c-brand-1);
font-weight: 500;
}
.models-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
margin-bottom: 1rem;
}
.model-card {
background: var(--vp-c-bg-soft);
background: var(--vp-c-bg);
padding: 0.75rem;
border: 2px solid transparent;
border-radius: 8px;
padding: 1.5rem;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
transition: all 0.2s ease;
text-align: center;
}
.model-card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
border-color: var(--vp-c-brand);
}
.model-card.active {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg);
background: var(--vp-c-brand-soft);
}
.model-icon {
font-size: 3rem;
margin-bottom: 0.5rem;
font-size: 1.5rem;
margin-bottom: 0.25rem;
}
.model-card h5 {
margin: 0.5rem 0;
color: var(--vp-c-text-1);
font-size: 1.1rem;
}
.model-tag {
display: inline-block;
padding: 0.25rem 0.75rem;
background: var(--vp-c-brand);
color: white;
border-radius: 12px;
font-size: 0.75rem;
.model-name {
font-weight: 600;
margin-bottom: 0.5rem;
font-size: 0.85rem;
color: var(--vp-c-text-1);
}
.model-lang {
font-size: 0.7rem;
color: var(--vp-c-brand-1);
margin-bottom: 0.25rem;
}
.model-desc {
font-size: 0.85rem;
font-size: 0.7rem;
color: var(--vp-c-text-2);
line-height: 1.4;
}
.model-detail {
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 2rem;
animation: fade-in 0.3s ease;
background: var(--vp-c-bg);
padding: 0.75rem;
border-radius: 6px;
}
.detail-header {
margin-bottom: 2rem;
.detail-header h6 {
margin: 0 0 0.75rem 0;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.detail-header h5 {
margin: 0 0 1.5rem 0;
color: var(--vp-c-brand);
font-size: 1.3rem;
}
.model-stats {
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.stat {
.stat-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.25rem;
}
.stat-label {
font-size: 0.85rem;
font-size: 0.75rem;
font-weight: 600;
color: var(--vp-c-text-1);
}
.stat-bar {
height: 8px;
background: var(--vp-c-bg);
border-radius: 4px;
height: 6px;
background: var(--vp-c-bg-soft);
border-radius: 3px;
overflow: hidden;
}
.stat-fill {
height: 100%;
background: linear-gradient(90deg, var(--vp-c-brand), #8b5cf6);
background: var(--vp-c-brand);
transition: width 0.5s ease;
}
.stat-fill.memory {
background: linear-gradient(90deg, #f59e0b, #d97706);
}
.stat-fill.cpu {
background: linear-gradient(90deg, #10b981, #059669);
background: var(--vp-c-green-1);
}
.code-example {
background: #1e1e1e;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
overflow-x: auto;
}
.code-example h6 {
margin: 0 0 1rem 0;
color: #4ec9b0;
font-size: 1rem;
}
.code-example pre {
margin: 0;
padding: 0.5rem;
border-radius: 4px;
margin-bottom: 0.75rem;
}
.code-example code {
color: #4ec9b0;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.85rem;
line-height: 1.6;
color: #d4d4d4;
}
.visualization {
margin-bottom: 2rem;
}
.visualization h6 {
margin: 0 0 1rem 0;
color: var(--vp-c-text-1);
font-size: 1rem;
}
.concurrency-viz {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1.5rem;
border: 1px solid var(--vp-c-divider);
}
.viz-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.task-queue,
.workers {
display: flex;
flex-direction: column;
gap: 1rem;
}
.queue-label,
.workers-label {
font-weight: 600;
color: var(--vp-c-text-1);
font-size: 0.9rem;
}
.queue-items {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.queue-item {
padding: 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
transition: all 0.3s ease;
}
.queue-item.processing {
background: #dcfce7;
color: #15803d;
font-weight: 600;
}
.worker-pool {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.worker {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--vp-c-bg-soft);
border-radius: 8px;
font-size: 1.2rem;
transition: all 0.3s ease;
}
.worker.active {
background: #dcfce7;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
font-size: 0.75rem;
}
.pros-cons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
gap: 0.75rem;
}
.pros h6,
.cons h6 {
margin: 0 0 1rem 0;
font-size: 1rem;
.pros strong {
display: block;
margin-bottom: 0.25rem;
font-size: 0.8rem;
color: var(--vp-c-green-1);
}
.pros h6 {
color: #10b981;
}
.cons h6 {
color: #ef4444;
.cons strong {
display: block;
margin-bottom: 0.25rem;
font-size: 0.8rem;
color: var(--vp-c-red-1);
}
.pros ul,
@@ -728,15 +332,15 @@ export default {
.pros li,
.cons li {
padding: 0.5rem 0;
font-size: 0.9rem;
padding: 0.15rem 0;
font-size: 0.75rem;
color: var(--vp-c-text-2);
line-height: 1.5;
line-height: 1.3;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
transition: opacity 0.2s ease;
}
.fade-enter-from,
@@ -744,28 +348,16 @@ export default {
opacity: 0;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
margin-top: 0.75rem;
}
@media (max-width: 768px) {
.model-stats {
grid-template-columns: 1fr;
}
.viz-container {
grid-template-columns: 1fr;
}
.pros-cons {
grid-template-columns: 1fr;
}
.info-box .icon {
margin-right: 0.25rem;
}
</style>