feat: update docs and components, fix DLQ demo bug
This commit is contained in:
@@ -0,0 +1,688 @@
|
||||
<template>
|
||||
<div class="backend-languages-demo">
|
||||
<div class="language-grid">
|
||||
<div
|
||||
v-for="lang in languages"
|
||||
:key="lang.name"
|
||||
class="language-card"
|
||||
:class="{ active: selectedLang === lang.name }"
|
||||
@click="selectedLang = lang.name"
|
||||
>
|
||||
<div class="lang-header">
|
||||
<div class="lang-icon">{{ lang.icon }}</div>
|
||||
<h4>{{ lang.name }}</h4>
|
||||
<div class="lang-year">{{ lang.year }}</div>
|
||||
</div>
|
||||
<div class="lang-tags">
|
||||
<span
|
||||
v-for="tag in lang.tags"
|
||||
:key="tag"
|
||||
class="tag"
|
||||
:class="`tag-${tag.type}`"
|
||||
>
|
||||
{{ tag.label }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="lang-description">{{ lang.description }}</div>
|
||||
<div class="lang-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-label">性能</span>
|
||||
<div class="stat-bar">
|
||||
<div
|
||||
class="stat-fill performance"
|
||||
:style="{ width: lang.stats.performance + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">开发效率</span>
|
||||
<div class="stat-bar">
|
||||
<div
|
||||
class="stat-fill efficiency"
|
||||
:style="{ width: lang.stats.efficiency + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">生态</span>
|
||||
<div class="stat-bar">
|
||||
<div
|
||||
class="stat-fill ecosystem"
|
||||
:style="{ width: lang.stats.ecosystem + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">学习曲线</span>
|
||||
<div class="stat-bar">
|
||||
<div
|
||||
class="stat-fill learning"
|
||||
:style="{ width: lang.stats.learning + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="selectedLang" class="lang-detail">
|
||||
<h3>{{ getLangDetail(selectedLang).title }}</h3>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-section">
|
||||
<h4>核心特点</h4>
|
||||
<ul>
|
||||
<li v-for="feature in getLangDetail(selectedLang).features" :key="feature">
|
||||
{{ feature }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="detail-section">
|
||||
<h4>典型应用</h4>
|
||||
<ul>
|
||||
<li v-for="app in getLangDetail(selectedLang).applications" :key="app">
|
||||
{{ app }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="detail-section">
|
||||
<h4>优劣势</h4>
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<strong>优势:</strong>
|
||||
<ul>
|
||||
<li v-for="pro in getLangDetail(selectedLang).pros" :key="pro">
|
||||
{{ pro }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<strong>劣势:</strong>
|
||||
<ul>
|
||||
<li v-for="con in getLangDetail(selectedLang).cons" :key="con">
|
||||
{{ con }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedLang = ref('Java')
|
||||
|
||||
const languages = [
|
||||
{
|
||||
name: 'Java',
|
||||
icon: '☕',
|
||||
year: '1995',
|
||||
tags: [
|
||||
{ type: 'primary', label: '企业级' },
|
||||
{ type: 'success', label: '跨平台' },
|
||||
{ type: 'info', label: '强类型' }
|
||||
],
|
||||
description: '企业级开发的霸主,Spring Boot 生态成熟',
|
||||
stats: { performance: 75, efficiency: 60, ecosystem: 95, learning: 40 }
|
||||
},
|
||||
{
|
||||
name: 'Python',
|
||||
icon: '🐍',
|
||||
year: '1991',
|
||||
tags: [
|
||||
{ type: 'primary', label: 'AI/ML' },
|
||||
{ type: 'success', label: '简洁' },
|
||||
{ type: 'warning', label: '动态类型' }
|
||||
],
|
||||
description: 'AI 与数据分析的首选语言',
|
||||
stats: { performance: 30, efficiency: 95, ecosystem: 95, learning: 95 }
|
||||
},
|
||||
{
|
||||
name: 'Go',
|
||||
icon: '🐹',
|
||||
year: '2009',
|
||||
tags: [
|
||||
{ type: 'primary', label: '云原生' },
|
||||
{ type: 'success', label: '高性能' },
|
||||
{ type: 'info', label: '并发' }
|
||||
],
|
||||
description: 'Google 出品,云原生时代的宠儿',
|
||||
stats: { performance: 90, efficiency: 85, ecosystem: 75, learning: 80 }
|
||||
},
|
||||
{
|
||||
name: 'Node.js',
|
||||
icon: '💚',
|
||||
year: '2009',
|
||||
tags: [
|
||||
{ type: 'primary', label: '全栈' },
|
||||
{ type: 'success', label: 'NPM' },
|
||||
{ type: 'info', label: '异步' }
|
||||
],
|
||||
description: 'JavaScript 运行时,前后端统一',
|
||||
stats: { performance: 70, efficiency: 85, ecosystem: 95, learning: 75 }
|
||||
},
|
||||
{
|
||||
name: 'C#',
|
||||
icon: '💜',
|
||||
year: '2000',
|
||||
tags: [
|
||||
{ type: 'primary', label: '.NET' },
|
||||
{ type: 'success', label: '跨平台' },
|
||||
{ type: 'info', label: 'Unity' }
|
||||
],
|
||||
description: '微软出品,Windows 生态王者',
|
||||
stats: { performance: 80, efficiency: 70, ecosystem: 85, learning: 50 }
|
||||
},
|
||||
{
|
||||
name: 'Rust',
|
||||
icon: '🦀',
|
||||
year: '2010',
|
||||
tags: [
|
||||
{ type: 'primary', label: '系统级' },
|
||||
{ type: 'success', label: '内存安全' },
|
||||
{ type: 'danger', label: '难学' }
|
||||
],
|
||||
description: 'Mozilla 出品,内存安全的系统语言',
|
||||
stats: { performance: 95, efficiency: 40, ecosystem: 70, learning: 20 }
|
||||
},
|
||||
{
|
||||
name: 'C++',
|
||||
icon: '⚡',
|
||||
year: '1985',
|
||||
tags: [
|
||||
{ type: 'primary', label: '高性能' },
|
||||
{ type: 'success', label: '游戏' },
|
||||
{ type: 'danger', label: '复杂' }
|
||||
],
|
||||
description: '高性能计算的基石',
|
||||
stats: { performance: 98, efficiency: 35, ecosystem: 90, learning: 25 }
|
||||
},
|
||||
{
|
||||
name: 'Ruby',
|
||||
icon: '💎',
|
||||
year: '1995',
|
||||
tags: [
|
||||
{ type: 'primary', label: 'Rails' },
|
||||
{ type: 'success', label: '优雅' },
|
||||
{ type: 'warning', label: '慢' }
|
||||
],
|
||||
description: 'Ruby on Rails,快速开发典范',
|
||||
stats: { performance: 25, efficiency: 90, ecosystem: 70, learning: 85 }
|
||||
}
|
||||
]
|
||||
|
||||
const langDetails = {
|
||||
Java: {
|
||||
title: 'Java - 企业级开发的霸主',
|
||||
features: [
|
||||
'JVM (Java Virtual Machine) 实现跨平台',
|
||||
'强类型系统,编译时检查',
|
||||
'Spring 全家桶生态成熟',
|
||||
'JIT 编译器提供接近 C++ 的性能'
|
||||
],
|
||||
applications: [
|
||||
'大型企业系统(银行、保险、电商)',
|
||||
'Android 应用开发',
|
||||
'大数据处理(Hadoop、Spark)',
|
||||
'微服务架构(Spring Cloud)'
|
||||
],
|
||||
pros: [
|
||||
'✅ 生态极其成熟,框架完备',
|
||||
'✅ 强类型,编译时检查',
|
||||
'✅ 多线程模型成熟',
|
||||
'✅ 跨平台,JVM 优化强大'
|
||||
],
|
||||
cons: [
|
||||
'❌ 代码冗长,样板代码多',
|
||||
'❌ 启动慢,内存占用高',
|
||||
'❌ 学习曲线陡峭(Spring 全家桶)',
|
||||
'❌ 版本更新快,兼容性问题'
|
||||
]
|
||||
},
|
||||
Python: {
|
||||
title: 'Python - AI 与脚本之王',
|
||||
features: [
|
||||
'极简语法,像读英语一样',
|
||||
'AI 生态无与伦比(NumPy、PyTorch)',
|
||||
'快速开发,代码量少',
|
||||
'丰富的科学计算库'
|
||||
],
|
||||
applications: [
|
||||
'AI/机器学习(所有主流框架)',
|
||||
'数据分析(Pandas、Jupyter)',
|
||||
'脚本自动化(运维、数据处理)',
|
||||
'Web 开发(Django、Flask)'
|
||||
],
|
||||
pros: [
|
||||
'✅ 语法简单,学习曲线平缓',
|
||||
'✅ AI 生态无与伦比',
|
||||
'✅ 快速开发,代码量少',
|
||||
'✅ 社区活跃,库丰富'
|
||||
],
|
||||
cons: [
|
||||
'❌ 运行速度慢(比 Java/Go 慢 10-100 倍)',
|
||||
'❌ 动态类型,运行时错误多',
|
||||
'❌ GIL 限制,多线程性能差',
|
||||
'❌ 打包部署复杂(依赖地狱)'
|
||||
]
|
||||
},
|
||||
Go: {
|
||||
title: 'Go - 云原生时代的宠儿',
|
||||
features: [
|
||||
'Goroutine (协程) 轻量级并发',
|
||||
'简洁语法,25 个关键字',
|
||||
'快速编译,比 Java 快 10 倍',
|
||||
'单一可执行文件,无依赖'
|
||||
],
|
||||
applications: [
|
||||
'云原生基础设施(Docker、K8s)',
|
||||
'微服务架构',
|
||||
'DevOps 工具(Terraform、Prometheus)',
|
||||
'区块链(Hyperledger Fabric)'
|
||||
],
|
||||
pros: [
|
||||
'✅ 原生并发,性能接近 C++',
|
||||
'✅ 简洁语法,学习曲线平缓',
|
||||
'✅ 编译快,部署简单',
|
||||
'✅ 单一可执行文件,无依赖'
|
||||
],
|
||||
cons: [
|
||||
'❌ 生态不如 Java/Python 成熟',
|
||||
'❌ 错误处理繁琐(if err != nil)',
|
||||
'❌ 泛型支持较弱(Go 1.18+ 才引入)',
|
||||
'❌ 不如 Java/Python 灵活'
|
||||
]
|
||||
},
|
||||
'Node.js': {
|
||||
title: 'Node.js - 全栈工程师的利器',
|
||||
features: [
|
||||
'事件驱动,非阻塞 I/O',
|
||||
'前后端统一语言',
|
||||
'NPM 世界最大的包仓库',
|
||||
'适合实时应用'
|
||||
],
|
||||
applications: [
|
||||
'全栈 Web 应用(React + Node.js)',
|
||||
'实时系统(聊天应用、协作工具)',
|
||||
'Serverless(AWS Lambda、Vercel)',
|
||||
'CLI 工具(VS Code、Webpack)'
|
||||
],
|
||||
pros: [
|
||||
'✅ 前后端统一,减少语言切换成本',
|
||||
'✅ NPM 生态庞大,库丰富',
|
||||
'✅ 适合 I/O 密集型应用',
|
||||
'✅ 社区活跃,更新快'
|
||||
],
|
||||
cons: [
|
||||
'❌ 单线程,CPU 密集型任务性能差',
|
||||
'❌ 回调地狱(async/await 有改善)',
|
||||
'❌ 动态类型,运行时错误多',
|
||||
'❌ 版本兼容性问题多'
|
||||
]
|
||||
},
|
||||
"C#": {
|
||||
title: 'C# - Windows 生态的王者',
|
||||
features: [
|
||||
'微软 Visual Studio 极其强大',
|
||||
'.NET Core 实现跨平台',
|
||||
'高性能,CoreFX 优化',
|
||||
'Unity 游戏开发官方语言'
|
||||
],
|
||||
applications: [
|
||||
'Windows 应用开发',
|
||||
'游戏开发(Unity 引擎)',
|
||||
'Web 开发(ASP.NET Core)',
|
||||
'Azure 云服务'
|
||||
],
|
||||
pros: [
|
||||
'✅ Visual Studio 极其强大',
|
||||
'✅ ASP.NET Core 性能优秀',
|
||||
'✅ 跨平台(.NET Core)',
|
||||
'✅ 游戏开发(Unity)'
|
||||
],
|
||||
cons: [
|
||||
'❌ Windows 历史包袱重',
|
||||
'❌ 社区不如 Java/Python 活跃',
|
||||
'❌ 学习曲线陡峭',
|
||||
'❌ 开源生态相对较弱'
|
||||
]
|
||||
},
|
||||
Rust: {
|
||||
title: 'Rust - 系统级编程的未来',
|
||||
features: [
|
||||
'所有权系统保证内存安全',
|
||||
'零成本抽象',
|
||||
'编译时保证无内存泄漏',
|
||||
'WebAssembly 支持'
|
||||
],
|
||||
applications: [
|
||||
'系统编程(操作系统、数据库)',
|
||||
'区块链(Solana、Polkadot)',
|
||||
'WebAssembly(前端高性能计算)',
|
||||
'基础设施(AWS Firecracker)'
|
||||
],
|
||||
pros: [
|
||||
'✅ 内存安全,无 GC',
|
||||
'✅ 性能接近 C++',
|
||||
'✅ 现代化语法',
|
||||
'✅ WebAssembly 支持'
|
||||
],
|
||||
cons: [
|
||||
'❌ 学习曲线极其陡峭',
|
||||
'❌ 编译时间长',
|
||||
'❌ 生态不如 Go/Rust 成熟',
|
||||
'❌ 开发速度慢'
|
||||
]
|
||||
},
|
||||
'C++': {
|
||||
title: 'C++ - 高性能计算的基石',
|
||||
features: [
|
||||
'极致性能,无语言能超越',
|
||||
'底层控制,直接操作内存',
|
||||
'游戏引擎标准',
|
||||
'现代 C++ (11/14/17/20)'
|
||||
],
|
||||
applications: [
|
||||
'游戏开发(Unreal Engine)',
|
||||
'高频交易(金融系统)',
|
||||
'浏览器引擎(Chrome V8)',
|
||||
'AI 框架底层(PyTorch、TF)'
|
||||
],
|
||||
pros: [
|
||||
'✅ 性能极致',
|
||||
'✅ 底层控制力强',
|
||||
'✅ 游戏开发标准',
|
||||
'✅ 生态成熟'
|
||||
],
|
||||
cons: [
|
||||
'❌ 学习曲线极其陡峭',
|
||||
'❌ 内存管理复杂(易泄漏)',
|
||||
'❌ 开发效率低',
|
||||
'❌ 不适合 Web 开发'
|
||||
]
|
||||
},
|
||||
Ruby: {
|
||||
title: 'Ruby - 快速开发的典范',
|
||||
features: [
|
||||
'Ruby on Rails 框架',
|
||||
'约定优于配置',
|
||||
'代码优雅',
|
||||
'快速开发'
|
||||
],
|
||||
applications: [
|
||||
'初创公司(GitHub、Airbnb)',
|
||||
'快速原型(MVP、黑客松)',
|
||||
'Web 开发(Rails、Sinatra)',
|
||||
'CI/CD 脚本'
|
||||
],
|
||||
pros: [
|
||||
'✅ Rails 框架极其成熟',
|
||||
'✅ 快速开发,代码优雅',
|
||||
'✅ 约定优于配置',
|
||||
'✅ 社区活跃'
|
||||
],
|
||||
cons: [
|
||||
'❌ 性能较差',
|
||||
'❌ 动态类型,运行时错误多',
|
||||
'❌ 多线程性能差',
|
||||
'❌ 生态不如 Java/Python 广泛'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const getLangDetail = (name) => {
|
||||
return langDetails[name] || {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.backend-languages-demo {
|
||||
border-radius: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
overflow: hidden;
|
||||
margin: 2rem 0;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.language-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.language-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.language-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.language-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.lang-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.lang-icon {
|
||||
font-size: 2.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.lang-header h4 {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.lang-year {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-3);
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.lang-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tag-primary {
|
||||
background: #e0f2fe;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.tag-success {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.tag-info {
|
||||
background: #e0e7ff;
|
||||
color: #4338ca;
|
||||
}
|
||||
|
||||
.tag-warning {
|
||||
background: #fef3c7;
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.tag-danger {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.lang-description {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
min-width: 60px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-bar {
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
background: var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-fill {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.stat-fill.performance {
|
||||
background: linear-gradient(90deg, #fbbf24, #f59e0b);
|
||||
}
|
||||
|
||||
.stat-fill.efficiency {
|
||||
background: linear-gradient(90deg, #34d399, #10b981);
|
||||
}
|
||||
|
||||
.stat-fill.ecosystem {
|
||||
background: linear-gradient(90deg, #60a5fa, #3b82f6);
|
||||
}
|
||||
|
||||
.stat-fill.learning {
|
||||
background: linear-gradient(90deg, #f472b6, #ec4899);
|
||||
}
|
||||
|
||||
.lang-detail {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
animation: fade-in 0.3s ease;
|
||||
}
|
||||
|
||||
.lang-detail h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.detail-section h4 {
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.detail-section ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.detail-section li {
|
||||
padding: 0.5rem 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
position: relative;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section li::before {
|
||||
content: '▸';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.pros,
|
||||
.cons {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.pros strong {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.cons strong {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,769 @@
|
||||
<template>
|
||||
<div class="concurrency-model-demo">
|
||||
<div class="demo-header">
|
||||
<h4>并发模型对比</h4>
|
||||
<p class="subtitle">不同语言处理并发请求的方式</p>
|
||||
</div>
|
||||
|
||||
<div class="model-grid">
|
||||
<div
|
||||
v-for="model in models"
|
||||
:key="model.name"
|
||||
class="model-card"
|
||||
:class="{ active: selectedModel === model.name }"
|
||||
@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-desc">{{ model.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition name="fade" mode="out-in">
|
||||
<div :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>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<div class="code-example">
|
||||
<h6>代码示例</h6>
|
||||
<pre><code>{{ getModelDetail(selectedModel).code }}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="visualization">
|
||||
<h6>并发可视化</h6>
|
||||
<ConcurrencyVisualization :model="selectedModel" />
|
||||
</div>
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<h6>优势</h6>
|
||||
<ul>
|
||||
<li v-for="pro in getModelDetail(selectedModel).pros" :key="pro">
|
||||
{{ pro }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<h6>劣势</h6>
|
||||
<ul>
|
||||
<li v-for="con in getModelDetail(selectedModel).cons" :key="con">
|
||||
{{ con }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineComponent } from 'vue'
|
||||
|
||||
const selectedModel = ref('Goroutine')
|
||||
|
||||
const models = [
|
||||
{
|
||||
name: 'Goroutine',
|
||||
icon: '🐹',
|
||||
tag: 'Go',
|
||||
description: '轻量级协程,百万级并发'
|
||||
},
|
||||
{
|
||||
name: 'Thread',
|
||||
icon: '🧵',
|
||||
tag: 'Java',
|
||||
description: '传统线程池,成熟稳定'
|
||||
},
|
||||
{
|
||||
name: 'Async/Await',
|
||||
icon: '⚡',
|
||||
tag: 'Node.js',
|
||||
description: '事件循环,非阻塞 I/O'
|
||||
},
|
||||
{
|
||||
name: 'Async/Await',
|
||||
icon: '🦀',
|
||||
tag: 'Rust',
|
||||
description: '零成本抽象,高性能'
|
||||
}
|
||||
]
|
||||
|
||||
const modelDetails = {
|
||||
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)',
|
||||
'❌ 不如线程模型成熟'
|
||||
]
|
||||
},
|
||||
Thread: {
|
||||
title: 'Java Thread (线程池)',
|
||||
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)',
|
||||
'❌ 锁竞争复杂'
|
||||
]
|
||||
},
|
||||
'Async/Await': {
|
||||
title: 'Node.js Async/Await (事件循环)',
|
||||
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)',
|
||||
'❌ 错误堆栈复杂'
|
||||
]
|
||||
},
|
||||
RustAsync: {
|
||||
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 成熟'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</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);
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.demo-header h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.model-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.model-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.model-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.model-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.model-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.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;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.model-desc {
|
||||
font-size: 0.85rem;
|
||||
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;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header h5 {
|
||||
margin: 0 0 1.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.model-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.stat-bar {
|
||||
height: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--vp-c-brand), #8b5cf6);
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.stat-fill.memory {
|
||||
background: linear-gradient(90deg, #f59e0b, #d97706);
|
||||
}
|
||||
|
||||
.stat-fill.cpu {
|
||||
background: linear-gradient(90deg, #10b981, #059669);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.code-example code {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.pros h6,
|
||||
.cons h6 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.pros h6 {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.cons h6 {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.pros ul,
|
||||
.cons ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pros li,
|
||||
.cons li {
|
||||
padding: 0.5rem 0;
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.model-stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.viz-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
<template>
|
||||
<div class="developer-efficiency-demo">
|
||||
<div class="demo-header">
|
||||
<h4>开发效率对比</h4>
|
||||
<p class="subtitle">不同语言完成相同任务所需的代码量和时间</p>
|
||||
</div>
|
||||
|
||||
<div class="task-selector">
|
||||
<label>选择任务:</label>
|
||||
<select v-model="selectedTask" @change="updateMetrics">
|
||||
<option v-for="task in tasks" :key="task.id" :value="task.id">
|
||||
{{ task.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-card">
|
||||
<div class="metric-header">
|
||||
<span class="metric-icon">📝</span>
|
||||
<h5>代码行数</h5>
|
||||
</div>
|
||||
<div class="metric-chart">
|
||||
<div
|
||||
v-for="lang in sortedLanguages"
|
||||
:key="lang.name"
|
||||
class="metric-bar"
|
||||
>
|
||||
<div class="bar-label">{{ lang.name }}</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar-fill"
|
||||
:style="{ width: getBarWidth(lang.lines) + '%' }"
|
||||
>
|
||||
<span class="bar-value">{{ lang.lines }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-insight">
|
||||
<p>
|
||||
<strong>💡 分析:</strong>
|
||||
{{ getTaskDetail(selectedTask).linesInsight }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-card">
|
||||
<div class="metric-header">
|
||||
<span class="metric-icon">⏱️</span>
|
||||
<h5>开发时间(小时)</h5>
|
||||
</div>
|
||||
<div class="metric-chart">
|
||||
<div
|
||||
v-for="lang in sortedLanguages"
|
||||
:key="lang.name"
|
||||
class="metric-bar"
|
||||
>
|
||||
<div class="bar-label">{{ lang.name }}</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar-fill time"
|
||||
:style="{ width: getBarWidth(lang.time) + '%' }"
|
||||
>
|
||||
<span class="bar-value">{{ lang.time }}h</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-insight">
|
||||
<p>
|
||||
<strong>💡 分析:</strong>
|
||||
{{ getTaskDetail(selectedTask).timeInsight }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-card">
|
||||
<div class="metric-header">
|
||||
<span class="metric-icon">🐛</span>
|
||||
<h5>调试时间(小时)</h5>
|
||||
</div>
|
||||
<div class="metric-chart">
|
||||
<div
|
||||
v-for="lang in sortedLanguages"
|
||||
:key="lang.name"
|
||||
class="metric-bar"
|
||||
>
|
||||
<div class="bar-label">{{ lang.name }}</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar-fill debug"
|
||||
:style="{ width: getBarWidth(lang.debug) + '%' }"
|
||||
>
|
||||
<span class="bar-value">{{ lang.debug }}h</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-insight">
|
||||
<p>
|
||||
<strong>💡 分析:</strong>
|
||||
{{ getTaskDetail(selectedTask).debugInsight }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="efficiency-radar">
|
||||
<h5>综合效率雷达图</h5>
|
||||
<div class="radar-chart">
|
||||
<div class="radar-grid">
|
||||
<div
|
||||
v-for="lang in languages"
|
||||
:key="lang.name"
|
||||
class="radar-point"
|
||||
:style="getRadarPosition(lang)"
|
||||
>
|
||||
<div class="point-label">{{ lang.name }}</div>
|
||||
<div class="point-dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="radar-legend">
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot"></span>
|
||||
<span>左上:代码少,开发快</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot"></span>
|
||||
<span>右上:调试快,类型安全</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot"></span>
|
||||
<span>左下:生态好,库丰富</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot"></span>
|
||||
<span>右下:学习简单,上手快</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const selectedTask = ref('rest')
|
||||
|
||||
const tasks = [
|
||||
{ id: 'rest', name: 'REST API 服务' },
|
||||
{ id: 'web', name: 'Web 应用' },
|
||||
{ id: 'script', name: '数据处理脚本' },
|
||||
{ id: 'micro', name: '微服务' }
|
||||
]
|
||||
|
||||
const languages = ['Python', 'Ruby', 'Go', 'Node.js', 'Java', 'C#', 'Rust', 'C++']
|
||||
|
||||
const taskMetrics = {
|
||||
rest: {
|
||||
Python: { lines: 50, time: 4, debug: 2 },
|
||||
Ruby: { lines: 45, time: 3.5, debug: 2.5 },
|
||||
Go: { lines: 80, time: 5, debug: 1.5 },
|
||||
'Node.js': { lines: 60, time: 4.5, debug: 2 },
|
||||
Java: { lines: 150, time: 8, debug: 2 },
|
||||
"C#": { lines: 120, time: 7, debug: 2 },
|
||||
Rust: { lines: 100, time: 10, debug: 3 },
|
||||
'C++': { lines: 180, time: 12, debug: 5 },
|
||||
linesInsight:
|
||||
'Python 和 Ruby 用最少的代码实现 REST API,得益于简洁的语法和强大的框架(Flask、Sinatra)。Go 虽然语法简洁,但需要显式类型声明。Java 和 C# 的样板代码最多。',
|
||||
timeInsight:
|
||||
'Ruby 和 Python 开发最快,适合快速迭代。Go 和 Node.js 居中,平衡了开发速度和性能。Java 和 C# 开发时间较长,但后期维护成本低。Rust 和 C++ 开发时间最长,主要受学习曲线和编译时间影响。',
|
||||
debugInsight:
|
||||
'Go、Java、C# 的静态类型让调试更容易,大部分错误在编译时就能发现。Python 和 Ruby 虽然开发快,但运行时错误多,调试时间长。Rust 的借用检查器虽然学习曲线陡峭,但能提前发现大量 bug。'
|
||||
},
|
||||
web: {
|
||||
Python: { lines: 200, time: 10, debug: 5 },
|
||||
Ruby: { lines: 180, time: 9, debug: 5 },
|
||||
Go: { lines: 300, time: 12, debug: 4 },
|
||||
'Node.js': { lines: 250, time: 11, debug: 5 },
|
||||
Java: { lines: 500, time: 20, debug: 6 },
|
||||
"C#": { lines: 400, time: 18, debug: 6 },
|
||||
Rust: { lines: 350, time: 25, debug: 8 },
|
||||
'C++': { lines: 600, time: 30, debug: 12 },
|
||||
linesInsight:
|
||||
'Rails 和 Django 的"约定优于配置"让 Web 开发极其高效,代码量最少。全栈的 Node.js 也表现不错。Go 需要 more boilerplate。Java 的 Spring Boot 虽然强大,但配置和样板代码较多。',
|
||||
timeInsight:
|
||||
'Ruby (Rails) 和 Python (Django) 是 Web 开发的效率之王,内置 ORM、模板引擎、认证等功能,开箱即用。Node.js 的全栈特性让前后端统一,减少沟通成本。Go 和 Java 需要更多配置和 boilerplate。',
|
||||
debugInsight:
|
||||
'静态类型语言(Go、Java、C#)在大型 Web 项目中优势明显,IDE 支持更好,重构更安全。Python 和 Ruby 在小项目中调试很快,但随着项目增长,动态类型带来的维护成本会急剧上升。'
|
||||
},
|
||||
script: {
|
||||
Python: { lines: 20, time: 1, debug: 0.5 },
|
||||
Ruby: { lines: 18, time: 1, debug: 0.5 },
|
||||
Go: { lines: 40, time: 2, debug: 0.5 },
|
||||
'Node.js': { lines: 25, time: 1.5, debug: 0.5 },
|
||||
Java: { lines: 80, time: 4, debug: 1 },
|
||||
"C#": { lines: 70, time: 3.5, debug: 1 },
|
||||
Rust: { lines: 50, time: 4, debug: 1 },
|
||||
'C++': { lines: 100, time: 5, debug: 2 },
|
||||
linesInsight:
|
||||
'Python 是脚本自动化的绝对王者,标准库丰富,第三方库如 Pandas、Requests 让数据处理极其简单。Ruby 也很优秀。其他语言对于简单脚本来说都太重量级了。',
|
||||
timeInsight:
|
||||
'Python 和 Ruby 是脚本任务的首选,几行代码就能完成复杂的数据处理。Node.js 在处理 JSON 数据时也很方便。编译型语言(Go、Java、C++)对于简单脚本来说 overhead 太大。',
|
||||
debugInsight:
|
||||
'Python 的交互式解释器(REPL)和丰富的调试工具(pdb、ipdb)让脚本调试极其高效。Ruby 的 Pry 也很强大。其他语言的编译/运行循环对于脚本开发来说太慢了。'
|
||||
},
|
||||
micro: {
|
||||
Python: { lines: 100, time: 6, debug: 3 },
|
||||
Ruby: { lines: 90, time: 5.5, debug: 3.5 },
|
||||
Go: { lines: 120, time: 7, debug: 2 },
|
||||
'Node.js': { lines: 110, time: 6.5, debug: 3 },
|
||||
Java: { lines: 250, time: 15, debug: 4 },
|
||||
"C#": { lines: 200, time: 13, debug: 4 },
|
||||
Rust: { lines: 140, time: 18, debug: 5 },
|
||||
'C++': { lines: 300, time: 22, debug: 8 },
|
||||
linesInsight:
|
||||
'Go 是微服务的理想选择,单一二进制文件 + 内置 HTTP 服务器 + 强大的标准库。Node.js 的 Express/Koa 也很轻量。Python 的 FastAPI 表现不错,但性能和并发不如 Go。',
|
||||
timeInsight:
|
||||
'Go 和 Node.js 在微服务开发中效率最高,启动快,部署简单。Python 和 Ruby 适合快速原型,但生产环境需要更多优化。Java 和 C# 的 Spring Cloud/.NET 虽然强大,但对于简单微服务来说太重量级。',
|
||||
debugInsight:
|
||||
'Go 和 Rust 的类型系统和错误处理让微服务的调试和测试更容易。Java 和 C# 的成熟工具链(JUnit、NUnit)也很有优势。Python 和 Ruby 的动态类型在分布式系统中可能带来运行时错误。'
|
||||
}
|
||||
}
|
||||
|
||||
const currentMetrics = computed(() => {
|
||||
return taskMetrics[selectedTask.value]
|
||||
})
|
||||
|
||||
const sortedLanguages = computed(() => {
|
||||
return languages
|
||||
.map((lang) => ({
|
||||
name: lang,
|
||||
...currentMetrics.value[lang]
|
||||
}))
|
||||
.sort((a, b) => a.lines - b.lines)
|
||||
})
|
||||
|
||||
const getBarWidth = (value) => {
|
||||
const max = Math.max(...Object.values(currentMetrics.value).flatMap((v) => [v.lines, v.time * 20, v.debug * 20]))
|
||||
return (value / max) * 100
|
||||
}
|
||||
|
||||
const getTaskDetail = (taskId) => {
|
||||
return taskMetrics[taskId]
|
||||
}
|
||||
|
||||
const getRadarPosition = (langName) => {
|
||||
const metrics = currentMetrics.value[langName]
|
||||
const avgLines = Object.values(currentMetrics.value).reduce((sum, v) => sum + v.lines, 0) / languages.length
|
||||
const avgTime = Object.values(currentMetrics.value).reduce((sum, v) => sum + v.time, 0) / languages.length
|
||||
const avgDebug = Object.values(currentMetrics.value).reduce((sum, v) => sum + v.debug, 0) / languages.length
|
||||
|
||||
// Normalize metrics (lower is better, so we invert)
|
||||
const linesScore = 1 - metrics.lines / 300 // Max lines ~300
|
||||
const timeScore = 1 - metrics.time / 30 // Max time ~30
|
||||
const debugScore = 1 - metrics.debug / 12 // Max debug ~12
|
||||
|
||||
// Position in 2D space
|
||||
// X: code efficiency (linesScore) vs ecosystem (hardcoded)
|
||||
// Y: speed (timeScore) vs maintainability (debugScore)
|
||||
const x = 50 + (linesScore - 0.5) * 80
|
||||
const y = 50 + (timeScore - 0.5) * 80
|
||||
|
||||
return {
|
||||
left: `${x}%`,
|
||||
top: `${y}%`
|
||||
}
|
||||
}
|
||||
|
||||
const updateMetrics = () => {
|
||||
// Trigger reactivity
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.developer-efficiency-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);
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.demo-header h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.task-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.task-selector label {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.task-selector select {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.metric-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.metric-header h5 {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.metric-chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.metric-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
min-width: 70px;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
flex: 1;
|
||||
height: 24px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 0.5rem;
|
||||
background: linear-gradient(90deg, var(--vp-c-brand), #8b5cf6);
|
||||
transition: width 0.5s ease;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.bar-fill.time {
|
||||
background: linear-gradient(90deg, #10b981, #059669);
|
||||
}
|
||||
|
||||
.bar-fill.debug {
|
||||
background: linear-gradient(90deg, #f59e0b, #d97706);
|
||||
}
|
||||
|
||||
.metric-insight {
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
border-left: 3px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.metric-insight p {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.efficiency-radar {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.efficiency-radar h5 {
|
||||
margin: 0 0 2rem 0;
|
||||
text-align: center;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.radar-chart {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.radar-grid {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.radar-point {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.radar-point:hover {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
}
|
||||
|
||||
.point-label {
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.point-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: var(--vp-c-brand);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
|
||||
.radar-legend {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.legend-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: var(--vp-c-brand);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metrics-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
min-width: 55px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.efficiency-radar {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.radar-chart {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<div class="language-comparison-demo">
|
||||
<div class="comparison-table-wrapper">
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>维度</th>
|
||||
<th v-for="lang in languages" :key="lang.name" class="lang-col">
|
||||
{{ lang.name }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="dimension in dimensions" :key="dimension.key">
|
||||
<td class="dimension-label">{{ dimension.label }}</td>
|
||||
<td v-for="lang in languages" :key="lang.name" class="score-cell">
|
||||
<div class="score-bar">
|
||||
<div
|
||||
class="score-fill"
|
||||
:class="`score-${dimension.key}`"
|
||||
:style="{ width: getScore(lang.name, dimension.key) + '%' }"
|
||||
>
|
||||
<span class="score-text">{{
|
||||
getScore(lang.name, dimension.key)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="insight-panel">
|
||||
<h4>💡 洞察分析</h4>
|
||||
<div class="insight-content">
|
||||
<div class="insight-item">
|
||||
<strong>性能王者:</strong>
|
||||
<span>C++ 和 Rust 在性能方面遥遥领先,但学习曲线极其陡峭。</span>
|
||||
</div>
|
||||
<div class="insight-item">
|
||||
<strong>开发效率:</strong>
|
||||
<span
|
||||
>Python 和 Ruby 在快速开发方面无与伦比,适合原型和初创公司。</span
|
||||
>
|
||||
</div>
|
||||
<div class="insight-item">
|
||||
<strong>生态成熟度:</strong>
|
||||
<span
|
||||
>Java、Python、Node.js 拥有最成熟的生态系统,库和框架丰富。</span
|
||||
>
|
||||
</div>
|
||||
<div class="insight-item">
|
||||
<strong>学习曲线:</strong>
|
||||
<span
|
||||
>Python、Ruby、Go 最容易上手,Rust 和 C++ 需要较长时间学习。</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const languages = [
|
||||
{ name: 'Java' },
|
||||
{ name: 'Python' },
|
||||
{ name: 'Go' },
|
||||
{ name: 'Node.js' },
|
||||
{ name: 'Rust' },
|
||||
{ name: 'C++' }
|
||||
]
|
||||
|
||||
const dimensions = [
|
||||
{ key: 'performance', label: '性能' },
|
||||
{ key: 'efficiency', label: '开发效率' },
|
||||
{ key: 'ecosystem', label: '生态成熟度' },
|
||||
{ key: 'learning', label: '学习曲线' },
|
||||
{ key: 'concurrency', label: '并发能力' },
|
||||
{ key: 'memory', label: '内存管理' }
|
||||
]
|
||||
|
||||
const scores = {
|
||||
Java: {
|
||||
performance: 75,
|
||||
efficiency: 60,
|
||||
ecosystem: 95,
|
||||
learning: 40,
|
||||
concurrency: 80,
|
||||
memory: 70
|
||||
},
|
||||
Python: {
|
||||
performance: 30,
|
||||
efficiency: 95,
|
||||
ecosystem: 95,
|
||||
learning: 95,
|
||||
concurrency: 30,
|
||||
memory: 40
|
||||
},
|
||||
Go: {
|
||||
performance: 90,
|
||||
efficiency: 85,
|
||||
ecosystem: 75,
|
||||
learning: 80,
|
||||
concurrency: 95,
|
||||
memory: 85
|
||||
},
|
||||
'Node.js': {
|
||||
performance: 70,
|
||||
efficiency: 85,
|
||||
ecosystem: 95,
|
||||
learning: 75,
|
||||
concurrency: 85,
|
||||
memory: 75
|
||||
},
|
||||
Rust: {
|
||||
performance: 95,
|
||||
efficiency: 40,
|
||||
ecosystem: 70,
|
||||
learning: 20,
|
||||
concurrency: 90,
|
||||
memory: 98
|
||||
},
|
||||
'C++': {
|
||||
performance: 98,
|
||||
efficiency: 35,
|
||||
ecosystem: 90,
|
||||
learning: 25,
|
||||
concurrency: 85,
|
||||
memory: 70
|
||||
}
|
||||
}
|
||||
|
||||
const getScore = (lang, dimension) => {
|
||||
return scores[lang][dimension]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-comparison-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);
|
||||
}
|
||||
|
||||
.comparison-table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
border-bottom: 2px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.comparison-table th.lang-col {
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.dimension-label {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.score-cell {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.score-bar {
|
||||
height: 30px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.score-fill {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 0.5rem;
|
||||
transition: width 0.5s ease;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.score-performance {
|
||||
background: linear-gradient(90deg, #fbbf24, #f59e0b);
|
||||
}
|
||||
|
||||
.score-efficiency {
|
||||
background: linear-gradient(90deg, #34d399, #10b981);
|
||||
}
|
||||
|
||||
.score-ecosystem {
|
||||
background: linear-gradient(90deg, #60a5fa, #3b82f6);
|
||||
}
|
||||
|
||||
.score-learning {
|
||||
background: linear-gradient(90deg, #f472b6, #ec4899);
|
||||
}
|
||||
|
||||
.score-concurrency {
|
||||
background: linear-gradient(90deg, #a78bfa, #8b5cf6);
|
||||
}
|
||||
|
||||
.score-memory {
|
||||
background: linear-gradient(90deg, #fb923c, #ea580c);
|
||||
}
|
||||
|
||||
.score-text {
|
||||
color: white;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.insight-panel {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.insight-panel h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.insight-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.insight-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.insight-item strong {
|
||||
color: var(--vp-c-text-1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.insight-item span {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.comparison-table-wrapper {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.insight-content {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+980
@@ -0,0 +1,980 @@
|
||||
<template>
|
||||
<div class="language-ecosystem-demo">
|
||||
<div class="demo-header">
|
||||
<h4>生态系统对比</h4>
|
||||
<p class="subtitle">不同语言的包管理器、框架和社区活跃度</p>
|
||||
</div>
|
||||
|
||||
<div class="ecosystem-tabs">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
class="tab-btn"
|
||||
:class="{ active: selectedTab === tab.id }"
|
||||
@click="selectedTab = tab.id"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<transition name="fade" mode="out-in">
|
||||
<div :key="selectedTab" class="tab-content">
|
||||
<!-- Package Managers -->
|
||||
<div v-if="selectedTab === 'packages'" class="packages-section">
|
||||
<div class="packages-grid">
|
||||
<div
|
||||
v-for="pkg in packageManagers"
|
||||
:key="pkg.name"
|
||||
class="package-card"
|
||||
>
|
||||
<div class="pkg-header">
|
||||
<span class="pkg-icon">{{ pkg.icon }}</span>
|
||||
<div class="pkg-info">
|
||||
<h5>{{ pkg.name }}</h5>
|
||||
<span class="pkg-lang">{{ pkg.language }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pkg-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-label">包数量</span>
|
||||
<span class="stat-value">{{ formatNumber(pkg.count) }}</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">周下载量</span>
|
||||
<span class="stat-value">{{ formatDownloads(pkg.downloads) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pkg-command">
|
||||
<code>{{ pkg.command }}</code>
|
||||
</div>
|
||||
<div class="pkg-features">
|
||||
<div
|
||||
v-for="feature in pkg.features"
|
||||
:key="feature"
|
||||
class="feature-tag"
|
||||
>
|
||||
{{ feature }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Web Frameworks -->
|
||||
<div v-else-if="selectedTab === 'frameworks'" class="frameworks-section">
|
||||
<div class="frameworks-table-wrapper">
|
||||
<table class="frameworks-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>框架</th>
|
||||
<th>语言</th>
|
||||
<th>性能</th>
|
||||
<th>学习曲线</th>
|
||||
<th>特点</th>
|
||||
<th>适用场景</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="fw in frameworks" :key="fw.name">
|
||||
<td class="fw-name">
|
||||
<span class="fw-icon">{{ fw.icon }}</span>
|
||||
{{ fw.name }}
|
||||
</td>
|
||||
<td>{{ fw.language }}</td>
|
||||
<td>
|
||||
<div class="rating-bar">
|
||||
<div
|
||||
class="rating-fill"
|
||||
:style="{ width: fw.performance + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="rating-bar learning">
|
||||
<div
|
||||
class="rating-fill"
|
||||
:style="{ width: fw.learning + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="tags">
|
||||
<span
|
||||
v-for="tag in fw.tags"
|
||||
:key="tag"
|
||||
class="tag"
|
||||
:class="`tag-${tag.type}`"
|
||||
>
|
||||
{{ tag.label }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ fw.useCase }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Community -->
|
||||
<div v-else-if="selectedTab === 'community'" class="community-section">
|
||||
<div class="community-metrics">
|
||||
<div class="metric-card">
|
||||
<div class="metric-header">
|
||||
<span class="metric-icon">📦</span>
|
||||
<h5>GitHub Stars</h5>
|
||||
</div>
|
||||
<div class="metric-chart">
|
||||
<div
|
||||
v-for="lang in communityStats"
|
||||
:key="lang.name"
|
||||
class="chart-bar"
|
||||
>
|
||||
<div class="bar-label">{{ lang.name }}</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar-fill"
|
||||
:style="{ width: (lang.stars / 100) + '%' }"
|
||||
>
|
||||
<span class="bar-value">{{ lang.stars }}M</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-card">
|
||||
<div class="metric-header">
|
||||
<span class="metric-icon">💬</span>
|
||||
<h5>Stack Overflow 问题</h5>
|
||||
</div>
|
||||
<div class="metric-chart">
|
||||
<div
|
||||
v-for="lang in communityStats"
|
||||
:key="lang.name"
|
||||
class="chart-bar"
|
||||
>
|
||||
<div class="bar-label">{{ lang.name }}</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar-fill questions"
|
||||
:style="{ width: (lang.questions / 30) + '%' }"
|
||||
>
|
||||
<span class="bar-value">{{ lang.questions }}M</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="community-insights">
|
||||
<h5>社区活跃度分析</h5>
|
||||
<div class="insight-grid">
|
||||
<div class="insight-card">
|
||||
<h6>最活跃</h6>
|
||||
<p>JavaScript/Node.js 社区最活跃,NPM 每周新增数万个包,问题响应速度最快。</p>
|
||||
</div>
|
||||
<div class="insight-card">
|
||||
<h6>最专业</h6>
|
||||
<p>Java 社区最专业,企业级问题讨论深入,Stack Overflow 质量最高。</p>
|
||||
</div>
|
||||
<div class="insight-card">
|
||||
<h6>增长最快</h6>
|
||||
<p>Rust 和 Go 社区增长最快,新一代开发者涌入,问题讨论质量高。</p>
|
||||
</div>
|
||||
<div class="insight-card">
|
||||
<h6>最友好</h6>
|
||||
<p>Python 和 Ruby 社区对新手最友好,问题回复耐心,文档详尽。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Learning Resources -->
|
||||
<div v-else-if="selectedTab === 'learning'" class="learning-section">
|
||||
<div class="resources-grid">
|
||||
<div
|
||||
v-for="resource in learningResources"
|
||||
:key="resource.language"
|
||||
class="resource-card"
|
||||
>
|
||||
<div class="resource-header">
|
||||
<span class="resource-icon">{{ resource.icon }}</span>
|
||||
<h5>{{ resource.language }}</h5>
|
||||
</div>
|
||||
<div class="resource-content">
|
||||
<div class="resource-section">
|
||||
<h6>官方文档</h6>
|
||||
<div class="doc-rating">
|
||||
<span
|
||||
v-for="i in 5"
|
||||
:key="i"
|
||||
class="star"
|
||||
:class="{ filled: i <= resource.docQuality }"
|
||||
>
|
||||
★
|
||||
</span>
|
||||
</div>
|
||||
<p class="doc-comment">{{ resource.docComment }}</p>
|
||||
</div>
|
||||
<div class="resource-section">
|
||||
<h6>推荐书籍</h6>
|
||||
<ul>
|
||||
<li v-for="book in resource.books" :key="book">{{ book }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="resource-section">
|
||||
<h6>在线课程</h6>
|
||||
<div class="courses">
|
||||
<span
|
||||
v-for="course in resource.courses"
|
||||
:key="course"
|
||||
class="course-tag"
|
||||
>
|
||||
{{ course }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-section">
|
||||
<h6>学习曲线</h6>
|
||||
<div class="learning-curve">
|
||||
<div
|
||||
class="curve-bar"
|
||||
:style="{ width: resource.learningCurve + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<p class="curve-label">{{ resource.curveLabel }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedTab = ref('packages')
|
||||
|
||||
const tabs = [
|
||||
{ id: 'packages', label: '包管理器' },
|
||||
{ id: 'frameworks', label: 'Web 框架' },
|
||||
{ id: 'community', label: '社区活跃度' },
|
||||
{ id: 'learning', label: '学习资源' }
|
||||
]
|
||||
|
||||
const packageManagers = [
|
||||
{
|
||||
name: 'NPM',
|
||||
icon: '💚',
|
||||
language: 'Node.js',
|
||||
count: 2000000,
|
||||
downloads: '50B/week',
|
||||
command: 'npm install express',
|
||||
features: ['最大生态', '版本管理灵活', '依赖地狱风险']
|
||||
},
|
||||
{
|
||||
name: 'PyPI',
|
||||
icon: '🐍',
|
||||
language: 'Python',
|
||||
count: 500000,
|
||||
downloads: '10B/week',
|
||||
command: 'pip install django',
|
||||
features: ['虚拟环境', '依赖管理清晰', '打包部署复杂']
|
||||
},
|
||||
{
|
||||
name: 'Maven',
|
||||
icon: '☕',
|
||||
language: 'Java',
|
||||
count: 300000,
|
||||
downloads: '5B/week',
|
||||
command: 'mvn install',
|
||||
features: ['企业级', '依赖管理严格', 'XML 配置冗长']
|
||||
},
|
||||
{
|
||||
name: 'Go Modules',
|
||||
icon: '🐹',
|
||||
language: 'Go',
|
||||
count: 100000,
|
||||
downloads: '500M/week',
|
||||
command: 'go get github.com/gin-gonic/gin',
|
||||
features: ['简洁', '无依赖地狱', '版本支持完善']
|
||||
},
|
||||
{
|
||||
name: 'Cargo',
|
||||
icon: '🦀',
|
||||
language: 'Rust',
|
||||
count: 100000,
|
||||
downloads: '200M/week',
|
||||
command: 'cargo add serde',
|
||||
features: ['现代化', '构建工具集成', '编译时间长']
|
||||
},
|
||||
{
|
||||
name: 'RubyGems',
|
||||
icon: '💎',
|
||||
language: 'Ruby',
|
||||
count: 150000,
|
||||
downloads: '300M/week',
|
||||
command: 'gem install rails',
|
||||
features: ['Bundler 管理', 'Gemfile 简洁', '版本冲突问题']
|
||||
}
|
||||
]
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
name: 'Express',
|
||||
icon: '💚',
|
||||
language: 'Node.js',
|
||||
performance: 70,
|
||||
learning: 90,
|
||||
tags: [
|
||||
{ type: 'success', label: '简洁' },
|
||||
{ type: 'info', label: '灵活' }
|
||||
],
|
||||
useCase: '快速原型、API 服务'
|
||||
},
|
||||
{
|
||||
name: 'Django',
|
||||
icon: '🐍',
|
||||
language: 'Python',
|
||||
performance: 40,
|
||||
learning: 85,
|
||||
tags: [
|
||||
{ type: 'success', label: '大而全' },
|
||||
{ type: 'info', label: 'ORM' }
|
||||
],
|
||||
useCase: '数据驱动应用、快速开发'
|
||||
},
|
||||
{
|
||||
name: 'Spring Boot',
|
||||
icon: '☕',
|
||||
language: 'Java',
|
||||
performance: 75,
|
||||
learning: 50,
|
||||
tags: [
|
||||
{ type: 'success', label: '企业级' },
|
||||
{ type: 'warning', label: '复杂' }
|
||||
],
|
||||
useCase: '企业级应用、微服务'
|
||||
},
|
||||
{
|
||||
name: 'Gin',
|
||||
icon: '🐹',
|
||||
language: 'Go',
|
||||
performance: 95,
|
||||
learning: 80,
|
||||
tags: [
|
||||
{ type: 'success', label: '高性能' },
|
||||
{ type: 'info', label: '轻量' }
|
||||
],
|
||||
useCase: '高性能 API、微服务'
|
||||
},
|
||||
{
|
||||
name: 'Rails',
|
||||
icon: '💎',
|
||||
language: 'Ruby',
|
||||
performance: 35,
|
||||
learning: 85,
|
||||
tags: [
|
||||
{ type: 'success', label: '约定优于配置' },
|
||||
{ type: 'info', label: 'MVC' }
|
||||
],
|
||||
useCase: '初创公司、快速迭代'
|
||||
},
|
||||
{
|
||||
name: 'Flask',
|
||||
icon: '🐍',
|
||||
language: 'Python',
|
||||
performance: 45,
|
||||
learning: 95,
|
||||
tags: [
|
||||
{ type: 'success', label: '轻量' },
|
||||
{ type: 'info', label: '灵活' }
|
||||
],
|
||||
useCase: '小型项目、学习'
|
||||
},
|
||||
{
|
||||
name: 'FastAPI',
|
||||
icon: '🐍',
|
||||
language: 'Python',
|
||||
performance: 65,
|
||||
learning: 85,
|
||||
tags: [
|
||||
{ type: 'success', label: '异步' },
|
||||
{ type: 'info', label: '类型安全' }
|
||||
],
|
||||
useCase: '现代 API、异步任务'
|
||||
},
|
||||
{
|
||||
name: 'Actix',
|
||||
icon: '🦀',
|
||||
language: 'Rust',
|
||||
performance: 98,
|
||||
learning: 30,
|
||||
tags: [
|
||||
{ type: 'success', label: '极致性能' },
|
||||
{ type: 'danger', label: '难学' }
|
||||
],
|
||||
useCase: '高性能服务、系统编程'
|
||||
}
|
||||
]
|
||||
|
||||
const communityStats = [
|
||||
{ name: 'JavaScript', stars: 85, questions: 28 },
|
||||
{ name: 'Python', stars: 75, questions: 25 },
|
||||
{ name: 'Java', stars: 65, questions: 22 },
|
||||
{ name: 'Go', stars: 55, questions: 8 },
|
||||
{ name: 'Rust', stars: 60, questions: 5 },
|
||||
{ name: 'Ruby', stars: 40, questions: 6 },
|
||||
{ name: 'C#', stars: 50, questions: 12 },
|
||||
{ name: 'C++', stars: 70, questions: 18 }
|
||||
]
|
||||
|
||||
const learningResources = [
|
||||
{
|
||||
language: 'Python',
|
||||
icon: '🐍',
|
||||
docQuality: 5,
|
||||
docComment: '官方文档极其详尽,教程丰富',
|
||||
books: ['Fluent Python', 'Python Cookbook'],
|
||||
courses: ['Coursera', 'edX', 'Udemy'],
|
||||
learningCurve: 95,
|
||||
curveLabel: '最简单'
|
||||
},
|
||||
{
|
||||
language: 'Go',
|
||||
icon: '🐹',
|
||||
docQuality: 5,
|
||||
docComment: '官方教程优秀,A Tour of Go 经典',
|
||||
books: ['The Go Programming Language', 'Go in Action'],
|
||||
courses: ['Udemy', 'Coursera', '官方文档'],
|
||||
learningCurve: 80,
|
||||
curveLabel: '简单'
|
||||
},
|
||||
{
|
||||
language: 'JavaScript',
|
||||
icon: '💚',
|
||||
docQuality: 4,
|
||||
docComment: 'MDN 文档权威,但碎片化',
|
||||
books: ['Eloquent JavaScript', 'You Don\'t Know JS'],
|
||||
courses: ['freeCodeCamp', 'Udemy', 'Codecademy'],
|
||||
learningCurve: 75,
|
||||
curveLabel: '中等'
|
||||
},
|
||||
{
|
||||
language: 'Java',
|
||||
icon: '☕',
|
||||
docQuality: 4,
|
||||
docComment: 'Oracle 官方文档完善,但冗长',
|
||||
books: ['Effective Java', 'Java Concurrency'],
|
||||
courses: ['Coursera', 'Udemy', 'Oracle 官方'],
|
||||
learningCurve: 40,
|
||||
curveLabel: '较难'
|
||||
},
|
||||
{
|
||||
language: 'Rust',
|
||||
icon: '🦀',
|
||||
docQuality: 5,
|
||||
docComment: 'The Rust Book 极其详细',
|
||||
books: ['The Rust Programming Language', 'Rust in Action'],
|
||||
courses: ['Udemy', 'Exercism', '官方文档'],
|
||||
learningCurve: 20,
|
||||
curveLabel: '极难'
|
||||
},
|
||||
{
|
||||
language: 'C#',
|
||||
icon: '💜',
|
||||
docQuality: 5,
|
||||
docComment: 'Microsoft 文档极其详细',
|
||||
books: ['C# in Depth', 'Pro C#'],
|
||||
courses: ['Microsoft Learn', 'Udemy', 'Pluralsight'],
|
||||
learningCurve: 50,
|
||||
curveLabel: '中等'
|
||||
}
|
||||
]
|
||||
|
||||
const formatNumber = (num) => {
|
||||
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M+'
|
||||
if (num >= 1000) return (num / 1000).toFixed(0) + 'K+'
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
const formatDownloads = (downloads) => {
|
||||
return downloads
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-ecosystem-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);
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.demo-header h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.ecosystem-tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 8px;
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
animation: fade-in 0.3s ease;
|
||||
}
|
||||
|
||||
.packages-grid,
|
||||
.resources-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.package-card,
|
||||
.resource-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.package-card:hover,
|
||||
.resource-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.pkg-header,
|
||||
.resource-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.pkg-icon,
|
||||
.resource-icon {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.pkg-info h5,
|
||||
.resource-header h5 {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.pkg-lang {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.pkg-stats {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.pkg-command {
|
||||
background: #1e1e1e;
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pkg-command code {
|
||||
color: #4ec9b0;
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.pkg-features,
|
||||
.courses {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.feature-tag,
|
||||
.course-tag {
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.frameworks-table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.frameworks-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.frameworks-table th {
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
border-bottom: 2px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.frameworks-table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.fw-name {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.fw-icon {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.rating-bar {
|
||||
width: 100px;
|
||||
height: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rating-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--vp-c-brand), #8b5cf6);
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.rating-bar.learning .rating-fill {
|
||||
background: linear-gradient(90deg, #10b981, #059669);
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tag-success {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.tag-info {
|
||||
background: #e0e7ff;
|
||||
color: #4338ca;
|
||||
}
|
||||
|
||||
.tag-warning {
|
||||
background: #fef3c7;
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.tag-danger {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.community-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.metric-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.metric-header h5 {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.metric-chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
min-width: 80px;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
flex: 1;
|
||||
height: 24px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 0.5rem;
|
||||
background: linear-gradient(90deg, var(--vp-c-brand), #8b5cf6);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.bar-fill.questions {
|
||||
background: linear-gradient(90deg, #f59e0b, #d97706);
|
||||
}
|
||||
|
||||
.community-insights h5 {
|
||||
margin: 0 0 1.5rem 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.insight-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.insight-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.insight-card h6 {
|
||||
margin: 0 0 0.75rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.insight-card p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.resource-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.resource-section h6 {
|
||||
margin: 0 0 0.75rem 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.doc-rating {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.star {
|
||||
color: var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.star.filled {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
.doc-comment {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.resource-section ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.resource-section li {
|
||||
padding: 0.25rem 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
position: relative;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.resource-section li::before {
|
||||
content: '▸';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.learning-curve {
|
||||
height: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.curve-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #10b981, #059669);
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.curve-label {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ecosystem-tabs {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.packages-grid,
|
||||
.resources-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.community-metrics {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.frameworks-table {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.frameworks-table th,
|
||||
.frameworks-table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,575 @@
|
||||
<template>
|
||||
<div class="language-selector-demo">
|
||||
<div class="selector-header">
|
||||
<h4>🎯 语言选择器</h4>
|
||||
<p class="subtitle">根据项目需求选择最适合的后端语言</p>
|
||||
</div>
|
||||
|
||||
<div class="selection-flow">
|
||||
<!-- Step 1: Project Type -->
|
||||
<div class="step-card" :class="{ active: currentStep === 1 }">
|
||||
<div class="step-number">1</div>
|
||||
<h5>项目类型</h5>
|
||||
<div class="options-grid">
|
||||
<button
|
||||
v-for="option in projectTypes"
|
||||
:key="option.id"
|
||||
class="option-btn"
|
||||
:class="{ selected: answers.projectType === option.id }"
|
||||
@click="selectProjectType(option.id)"
|
||||
>
|
||||
<span class="option-icon">{{ option.icon }}</span>
|
||||
<span class="option-label">{{ option.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Performance Requirement -->
|
||||
<div
|
||||
class="step-card"
|
||||
:class="{ active: currentStep === 2, disabled: !answers.projectType }"
|
||||
>
|
||||
<div class="step-number">2</div>
|
||||
<h5>性能要求</h5>
|
||||
<div class="options-grid">
|
||||
<button
|
||||
v-for="option in performanceLevels"
|
||||
:key="option.id"
|
||||
class="option-btn"
|
||||
:class="{ selected: answers.performance === option.id }"
|
||||
@click="selectPerformance(option.id)"
|
||||
>
|
||||
<span class="option-icon">{{ option.icon }}</span>
|
||||
<span class="option-label">{{ option.label }}</span>
|
||||
<span class="option-desc">{{ option.desc }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Team Background -->
|
||||
<div
|
||||
class="step-card"
|
||||
:class="{
|
||||
active: currentStep === 3,
|
||||
disabled: !answers.performance
|
||||
}"
|
||||
>
|
||||
<div class="step-number">3</div>
|
||||
<h5>团队背景</h5>
|
||||
<div class="options-grid">
|
||||
<button
|
||||
v-for="option in teamBackgrounds"
|
||||
:key="option.id"
|
||||
class="option-btn"
|
||||
:class="{ selected: answers.team === option.id }"
|
||||
@click="selectTeam(option.id)"
|
||||
>
|
||||
<span class="option-icon">{{ option.icon }}</span>
|
||||
<span class="option-label">{{ option.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Time to Market -->
|
||||
<div
|
||||
class="step-card"
|
||||
:class="{ active: currentStep === 4, disabled: !answers.team }"
|
||||
>
|
||||
<div class="step-number">4</div>
|
||||
<h5>上市时间</h5>
|
||||
<div class="options-grid">
|
||||
<button
|
||||
v-for="option in timeConstraints"
|
||||
:key="option.id"
|
||||
class="option-btn"
|
||||
:class="{ selected: answers.time === option.id }"
|
||||
@click="selectTime(option.id)"
|
||||
>
|
||||
<span class="option-icon">{{ option.icon }}</span>
|
||||
<span class="option-label">{{ option.label }}</span>
|
||||
<span class="option-desc">{{ option.desc }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recommendation -->
|
||||
<transition name="result">
|
||||
<div v-if="recommendation" class="recommendation-panel">
|
||||
<h5>🎉 推荐语言</h5>
|
||||
<div class="recommendation-card">
|
||||
<div class="rec-language">
|
||||
<span class="rec-icon">{{ recommendation.icon }}</span>
|
||||
<span class="rec-name">{{ recommendation.language }}</span>
|
||||
</div>
|
||||
<div class="rec-reason">
|
||||
<h6>选择理由</h6>
|
||||
<p>{{ recommendation.reason }}</p>
|
||||
</div>
|
||||
<div class="rec-alternatives">
|
||||
<h6>备选方案</h6>
|
||||
<div class="alt-list">
|
||||
<span
|
||||
v-for="alt in recommendation.alternatives"
|
||||
:key="alt"
|
||||
class="alt-tag"
|
||||
>
|
||||
{{ alt }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="reset-btn" @click="reset">🔄 重新选择</button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const currentStep = ref(1)
|
||||
const answers = ref({
|
||||
projectType: null,
|
||||
performance: null,
|
||||
team: null,
|
||||
time: null
|
||||
})
|
||||
|
||||
const projectTypes = [
|
||||
{ id: 'web', icon: '🌐', label: 'Web 应用' },
|
||||
{ id: 'api', icon: '🔌', label: 'API 服务' },
|
||||
{ id: 'microservice', icon: '⚙️', label: '微服务' },
|
||||
{ id: 'ai', icon: '🤖', label: 'AI/ML' },
|
||||
{ id: 'game', icon: '🎮', label: '游戏' },
|
||||
{ id: 'system', icon: '💻', label: '系统编程' }
|
||||
]
|
||||
|
||||
const performanceLevels = [
|
||||
{ id: 'extreme', icon: '⚡', label: '极致性能', desc: '每秒百万级请求' },
|
||||
{ id: 'high', icon: '🚀', label: '高性能', desc: '每秒十万级请求' },
|
||||
{ id: 'medium', icon: '📊', label: '中等性能', desc: '每秒万级请求' },
|
||||
{ id: 'low', icon: '🐌', label: '性能不敏感', desc: '快速开发优先' }
|
||||
]
|
||||
|
||||
const teamBackgrounds = [
|
||||
{ id: 'frontend', icon: '💚', label: '前端团队' },
|
||||
{ id: 'python', icon: '🐍', label: 'Python 背景' },
|
||||
{ id: 'java', icon: '☕', label: 'Java 背景' },
|
||||
{ id: 'mixed', icon: '👥', label: '混合团队' },
|
||||
{ id: 'new', icon: '🆕', label: '新团队' }
|
||||
]
|
||||
|
||||
const timeConstraints = [
|
||||
{ id: 'urgent', icon: '🔥', label: '紧急', desc: '1-2 周上线' },
|
||||
{ id: 'normal', icon: '📅', label: '正常', desc: '1-2 个月' },
|
||||
{ id: 'flexible', icon: '🎯', label: '灵活', desc: '3 个月以上' }
|
||||
]
|
||||
|
||||
const recommendation = computed(() => {
|
||||
if (!answers.value.projectType || !answers.value.performance || !answers.value.team || !answers.value.time) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { projectType, performance, team, time } = answers.value
|
||||
|
||||
// 决策逻辑
|
||||
if (projectType === 'ai') {
|
||||
return {
|
||||
language: 'Python',
|
||||
icon: '🐍',
|
||||
reason: 'AI/ML 领域的绝对统治地位,生态无与伦比(NumPy、PyTorch、TensorFlow)。虽然性能不如 C++/Rust,但 95% 的 AI 项目都在用 Python。',
|
||||
alternatives: ['C++ (模型部署)', 'Julia (科学计算)']
|
||||
}
|
||||
}
|
||||
|
||||
if (projectType === 'game') {
|
||||
return {
|
||||
language: 'C++',
|
||||
icon: '⚡',
|
||||
reason: '游戏开发的行业标准(Unreal Engine)。极致性能,底层控制力强。如果使用 Unity 引擎,则 C# 是首选。',
|
||||
alternatives: ['C# (Unity)', 'Rust (独立游戏)']
|
||||
}
|
||||
}
|
||||
|
||||
if (projectType === 'system') {
|
||||
if (performance === 'extreme') {
|
||||
return {
|
||||
language: 'Rust',
|
||||
icon: '🦀',
|
||||
reason: '内存安全 + 极致性能,现代化系统语言。虽然学习曲线陡峭,但编译时保证无内存泄漏,适合长期维护的基础设施。',
|
||||
alternatives: ['C++ (传统选择)', 'Go (云原生)']
|
||||
}
|
||||
}
|
||||
return {
|
||||
language: 'Go',
|
||||
icon: '🐹',
|
||||
reason: '云原生时代的宠儿(Docker、K8s 都是 Go 写的)。简洁语法 + 原生并发 + 快速编译,非常适合系统编程和 DevOps 工具。',
|
||||
alternatives: ['Rust (更安全)', 'C++ (更成熟)']
|
||||
}
|
||||
}
|
||||
|
||||
if (projectType === 'microservice') {
|
||||
if (performance === 'extreme' || performance === 'high') {
|
||||
return {
|
||||
language: 'Go',
|
||||
icon: '🐹',
|
||||
reason: '云原生的首选语言。Goroutine 轻量级并发可轻松处理百万级请求,编译后的单一可执行文件部署极其简单。',
|
||||
alternatives: ['Java (Spring Cloud)', 'Rust (极致性能)']
|
||||
}
|
||||
}
|
||||
return {
|
||||
language: 'Node.js',
|
||||
icon: '💚',
|
||||
reason: '前后端统一,减少语言切换成本。NPM 生态丰富,适合 I/O 密集型的微服务。',
|
||||
alternatives: ['Go (更高性能)', 'Python (快速开发)']
|
||||
}
|
||||
}
|
||||
|
||||
if (team === 'frontend') {
|
||||
return {
|
||||
language: 'Node.js',
|
||||
icon: '💚',
|
||||
reason: '前端团队零学习成本,TypeScript 提供类型安全。全栈开发减少沟通成本,适合快速迭代和 MVP 开发。',
|
||||
alternatives: ['Go (后端性能优化)', 'TypeScript (类型安全)']
|
||||
}
|
||||
}
|
||||
|
||||
if (team === 'python') {
|
||||
return {
|
||||
language: 'Python',
|
||||
icon: '🐍',
|
||||
reason: '利用团队现有技能,快速开发。Django/FastAPI 生态成熟,适合数据驱动的 Web 应用。',
|
||||
alternatives: ['Go (性能提升)', 'Node.js (全栈)']
|
||||
}
|
||||
}
|
||||
|
||||
if (team === 'java') {
|
||||
return {
|
||||
language: 'Java',
|
||||
icon: '☕',
|
||||
reason: '企业级开发的最佳选择。Spring Boot 生态极其成熟,团队熟悉度高,维护成本最低。',
|
||||
alternatives: ['Go (云原生)', 'Kotlin (更现代)']
|
||||
}
|
||||
}
|
||||
|
||||
if (time === 'urgent') {
|
||||
if (projectType === 'web' || projectType === 'api') {
|
||||
return {
|
||||
language: 'Python',
|
||||
icon: '🐍',
|
||||
reason: '开发速度最快的语言。FastAPI/Django 让你在几天内就能搭建起完整的 Web 应用,适合快速验证想法和 MVP。',
|
||||
alternatives: ['Ruby (Rails)', 'Node.js (全栈)']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 默认推荐
|
||||
if (performance === 'extreme') {
|
||||
return {
|
||||
language: 'Go',
|
||||
icon: '🐹',
|
||||
reason: '高性能 + 简洁语法 + 快速开发的最佳平衡点。Goroutine 并发模型让处理高并发变得简单,而不会像 Java 那么复杂。',
|
||||
alternatives: ['Rust (更安全)', 'C++ (更极致)']
|
||||
}
|
||||
}
|
||||
|
||||
if (performance === 'high') {
|
||||
return {
|
||||
language: 'Go',
|
||||
icon: '🐹',
|
||||
reason: '云原生时代的高性能语言。相比 Java 更简洁,相比 Node.js 性能更好,相比 C++ 更容易维护。',
|
||||
alternatives: ['Java (更成熟)', 'Node.js (更灵活)']
|
||||
}
|
||||
}
|
||||
|
||||
// Default: Node.js or Python
|
||||
return {
|
||||
language: 'Node.js',
|
||||
icon: '💚',
|
||||
reason: '前后端统一,生态庞大,适合大多数 Web 应用和 API 服务。NPM 拥有世界最大的包仓库,几乎任何功能都能找到现成的库。',
|
||||
alternatives: ['Go (更高性能)', 'Python (更简单)']
|
||||
}
|
||||
})
|
||||
|
||||
const selectProjectType = (value) => {
|
||||
answers.value.projectType = value
|
||||
currentStep.value = 2
|
||||
}
|
||||
|
||||
const selectPerformance = (value) => {
|
||||
answers.value.performance = value
|
||||
currentStep.value = 3
|
||||
}
|
||||
|
||||
const selectTeam = (value) => {
|
||||
answers.value.team = value
|
||||
currentStep.value = 4
|
||||
}
|
||||
|
||||
const selectTime = (value) => {
|
||||
answers.value.time = value
|
||||
currentStep.value = 5
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
answers.value = {
|
||||
projectType: null,
|
||||
performance: null,
|
||||
team: null,
|
||||
time: null
|
||||
}
|
||||
currentStep.value = 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-selector-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);
|
||||
}
|
||||
|
||||
.selector-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.selector-header h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.selection-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.step-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
box-shadow: 0 8px 30px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.step-card.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.step-card h5 {
|
||||
margin: 0 0 1.5rem 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.options-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.option-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1.5rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.option-btn:hover:not(.selected) {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
|
||||
}
|
||||
|
||||
.option-btn.selected {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.option-icon {
|
||||
font-size: 2rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.option-label {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.option-desc {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.recommendation-panel {
|
||||
margin-top: 3rem;
|
||||
animation: slide-up 0.5s ease;
|
||||
}
|
||||
|
||||
.recommendation-panel h5 {
|
||||
text-align: center;
|
||||
margin: 0 0 2rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.recommendation-card {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem;
|
||||
color: white;
|
||||
box-shadow: 0 10px 40px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.rec-language {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.rec-icon {
|
||||
font-size: 4rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.rec-name {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rec-reason,
|
||||
.rec-alternatives {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.rec-reason h6,
|
||||
.rec-alternatives h6 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.rec-reason p {
|
||||
margin: 0;
|
||||
line-height: 1.7;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.alt-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.alt-tag {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
background: white;
|
||||
color: #667eea;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.reset-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.result-enter-active,
|
||||
.result-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.result-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
.result-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.options-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.rec-name {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.rec-icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
<template>
|
||||
<div class="performance-benchmark-demo">
|
||||
<div class="benchmark-controls">
|
||||
<h4>性能基准测试</h4>
|
||||
<div class="control-group">
|
||||
<label>测试场景:</label>
|
||||
<select v-model="selectedScenario" @change="runBenchmark">
|
||||
<option value="hello">Hello World (简单 HTTP)</option>
|
||||
<option value="json">JSON 序列化</option>
|
||||
<option value="db">数据库查询</option>
|
||||
<option value="compute">CPU 密集计算</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="run-btn" @click="runBenchmark" :disabled="isRunning">
|
||||
{{ isRunning ? '测试中...' : '▶ 运行测试' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="results-panel">
|
||||
<div class="results-header">
|
||||
<h5>测试结果(Requests/Second)</h5>
|
||||
<div class="legend">
|
||||
<span class="legend-item"
|
||||
><span class="dot" style="background: #f59e0b"></span> 高性能</span
|
||||
>
|
||||
<span class="legend-item"
|
||||
><span class="dot" style="background: #10b981"></span> 中等</span
|
||||
>
|
||||
<span class="legend-item"
|
||||
><span class="dot" style="background: #6366f1"></span> 较低</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition-group name="bar" tag="div" class="chart-container">
|
||||
<div
|
||||
v-for="result in sortedResults"
|
||||
:key="result.language"
|
||||
class="chart-bar-wrapper"
|
||||
>
|
||||
<div class="bar-label">{{ result.language }}</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar-fill"
|
||||
:class="getBarClass(result.rps)"
|
||||
:style="{ width: getBarWidth(result.rps) + '%' }"
|
||||
>
|
||||
<span class="bar-value">{{ formatRPS(result.rps) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
|
||||
<div class="explanation-panel">
|
||||
<h5>💡 性能解释</h5>
|
||||
<div class="explanation-content">
|
||||
<p>{{ getCurrentExplanation() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const selectedScenario = ref('hello')
|
||||
const isRunning = ref(false)
|
||||
|
||||
const benchmarkData = {
|
||||
hello: {
|
||||
'C++': { rps: 1500000, time: 0.5 },
|
||||
Rust: { rps: 1200000, time: 0.6 },
|
||||
Go: { rps: 1000000, time: 0.7 },
|
||||
Java: { rps: 700000, time: 1.0 },
|
||||
'Node.js': { rps: 800000, time: 0.9 },
|
||||
Python: { rps: 200000, time: 2.5 },
|
||||
Ruby: { rps: 150000, time: 3.0 },
|
||||
PHP: { rps: 250000, time: 2.0 }
|
||||
},
|
||||
json: {
|
||||
'C++': { rps: 800000, time: 1.0 },
|
||||
Rust: { rps: 700000, time: 1.1 },
|
||||
Go: { rps: 600000, time: 1.2 },
|
||||
Java: { rps: 500000, time: 1.5 },
|
||||
'Node.js': { rps: 450000, time: 1.6 },
|
||||
Python: { rps: 150000, time: 4.0 },
|
||||
Ruby: { rps: 120000, time: 5.0 },
|
||||
PHP: { rps: 180000, time: 3.5 }
|
||||
},
|
||||
db: {
|
||||
'C++': { rps: 300000, time: 2.5 },
|
||||
Rust: { rps: 280000, time: 2.6 },
|
||||
Go: { rps: 250000, time: 3.0 },
|
||||
Java: { rps: 200000, time: 3.5 },
|
||||
'Node.js': { rps: 220000, time: 3.2 },
|
||||
Python: { rps: 80000, time: 8.0 },
|
||||
Ruby: { rps: 70000, time: 9.0 },
|
||||
PHP: { rps: 90000, time: 7.5 }
|
||||
},
|
||||
compute: {
|
||||
'C++': { rps: 500000, time: 1.5 },
|
||||
Rust: { rps: 480000, time: 1.6 },
|
||||
Go: { rps: 400000, time: 2.0 },
|
||||
Java: { rps: 350000, time: 2.3 },
|
||||
'Node.js': { rps: 50000, time: 15.0 },
|
||||
Python: { rps: 30000, time: 25.0 },
|
||||
Ruby: { rps: 25000, time: 30.0 },
|
||||
PHP: { rps: 35000, time: 20.0 }
|
||||
}
|
||||
}
|
||||
|
||||
const explanations = {
|
||||
hello: '简单的 Hello World HTTP 响应测试。C++ 和 Rust 在这个测试中展现出接近硬件的性能优势。Go 和 Node.js 表现也很优秀,因为它们的 HTTP 栈经过高度优化。Python 和 Ruby 由于解释器开销,性能相对较低。',
|
||||
json: 'JSON 序列化/反序列化测试。这个测试考验语言的 JSON 处理能力。C++ 和 Rust 依然领先,但 Node.js 的表现也不错(V8 引擎优化)。Python 的标准库 json 模块性能尚可,但比编译型语言慢很多。',
|
||||
db: '模拟数据库查询(连接池 + 查询)。这个测试更接近真实应用。性能差距缩小了,因为瓶颈主要在数据库 I/O 而非语言本身。但依然能看到编译型语言(C++、Rust、Go)的优势。',
|
||||
compute: 'CPU 密集型计算(斐波那契数列)。这个测试充分暴露了 Node.js 的短板:单线程 + V8 编译优化不如静态语言。Python 和 Ruby 表现最差,因为它们是解释型语言,且 GIL 限制了多线程性能。C++ 和 Rust 几乎是唯一选择。'
|
||||
}
|
||||
|
||||
const currentResults = ref([])
|
||||
|
||||
const sortedResults = computed(() => {
|
||||
return [...currentResults.value].sort((a, b) => b.rps - a.rps)
|
||||
})
|
||||
|
||||
const runBenchmark = () => {
|
||||
isRunning.value = true
|
||||
currentResults.value = []
|
||||
|
||||
// 模拟测试延迟
|
||||
setTimeout(() => {
|
||||
const data = benchmarkData[selectedScenario.value]
|
||||
currentResults.value = Object.entries(data).map(([language, stats]) => ({
|
||||
language,
|
||||
rps: stats.rps,
|
||||
time: stats.time
|
||||
}))
|
||||
isRunning.value = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const getBarWidth = (rps) => {
|
||||
const max = 1500000
|
||||
return (rps / max) * 100
|
||||
}
|
||||
|
||||
const getBarClass = (rps) => {
|
||||
if (rps >= 800000) return 'bar-high'
|
||||
if (rps >= 300000) return 'bar-medium'
|
||||
return 'bar-low'
|
||||
}
|
||||
|
||||
const formatRPS = (rps) => {
|
||||
if (rps >= 1000000) return (rps / 1000000).toFixed(1) + 'M'
|
||||
if (rps >= 1000) return (rps / 1000).toFixed(0) + 'K'
|
||||
return rps.toString()
|
||||
}
|
||||
|
||||
const getCurrentExplanation = () => {
|
||||
return explanations[selectedScenario.value]
|
||||
}
|
||||
|
||||
// 初始运行一次
|
||||
runBenchmark()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.performance-benchmark-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);
|
||||
}
|
||||
|
||||
.benchmark-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.benchmark-controls h4 {
|
||||
margin: 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.control-group label {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.control-group select {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.run-btn {
|
||||
padding: 0.5rem 1.5rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.run-btn:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
|
||||
.run-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.results-panel {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.results-header h5 {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.chart-bar-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
min-width: 80px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
flex: 1;
|
||||
height: 40px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 1rem;
|
||||
transition: width 0.8s ease-out;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.bar-high {
|
||||
background: linear-gradient(90deg, #f59e0b, #d97706);
|
||||
}
|
||||
|
||||
.bar-medium {
|
||||
background: linear-gradient(90deg, #10b981, #059669);
|
||||
}
|
||||
|
||||
.bar-low {
|
||||
background: linear-gradient(90deg, #6366f1, #4f46e5);
|
||||
}
|
||||
|
||||
.bar-enter-active,
|
||||
.bar-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.bar-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.bar-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
.explanation-panel {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.explanation-panel h5 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.explanation-content p {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.7;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.benchmark-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.run-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
min-width: 60px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.bar-value {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,471 @@
|
||||
<template>
|
||||
<div class="syntax-comparison-demo">
|
||||
<div class="demo-header">
|
||||
<h4>Hello World 语法对比</h4>
|
||||
<p class="subtitle">同样的功能,不同的实现方式</p>
|
||||
</div>
|
||||
|
||||
<div class="language-selector">
|
||||
<button
|
||||
v-for="lang in languages"
|
||||
:key="lang.name"
|
||||
class="lang-btn"
|
||||
:class="{ active: selectedLang === lang.name }"
|
||||
@click="selectedLang = lang.name"
|
||||
>
|
||||
<span class="lang-icon">{{ lang.icon }}</span>
|
||||
<span class="lang-name">{{ lang.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<transition name="fade" mode="out-in">
|
||||
<div :key="selectedLang" class="code-display">
|
||||
<div class="code-header">
|
||||
<div class="window-controls">
|
||||
<span class="control red"></span>
|
||||
<span class="control yellow"></span>
|
||||
<span class="control green"></span>
|
||||
</div>
|
||||
<div class="filename">{{ getFileName(selectedLang) }}</div>
|
||||
</div>
|
||||
|
||||
<pre class="code-content"><code>{{ getCode(selectedLang) }}</code></pre>
|
||||
|
||||
<div class="code-stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">代码行数:</span>
|
||||
<span class="stat-value">{{ getLineCount(selectedLang) }} 行</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">字符数:</span>
|
||||
<span class="stat-value">{{ getCharCount(selectedLang) }} 字符</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">复杂度:</span>
|
||||
<span class="stat-value">{{ getComplexity(selectedLang) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<div class="analysis-panel">
|
||||
<h5>💡 语法分析</h5>
|
||||
<div class="analysis-content">
|
||||
<p>{{ getAnalysis(selectedLang) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="complexity-chart">
|
||||
<h5>代码复杂度对比</h5>
|
||||
<div class="chart-bars">
|
||||
<div
|
||||
v-for="lang in languages"
|
||||
:key="lang.name"
|
||||
class="chart-bar-wrapper"
|
||||
>
|
||||
<div class="chart-label">{{ lang.name }}</div>
|
||||
<div class="chart-bar">
|
||||
<div
|
||||
class="chart-fill"
|
||||
:style="{ width: getComplexityWidth(lang.name) + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="chart-value">{{ getLineCount(lang.name) }} 行</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedLang = ref('Python')
|
||||
|
||||
const languages = [
|
||||
{ name: 'Python', icon: '🐍' },
|
||||
{ name: 'Ruby', icon: '💎' },
|
||||
{ name: 'Go', icon: '🐹' },
|
||||
{ name: 'Node.js', icon: '💚' },
|
||||
{ name: 'Java', icon: '☕' },
|
||||
{ name: 'C#', icon: '💜' },
|
||||
{ name: 'Rust', icon: '🦀' },
|
||||
{ name: 'C++', icon: '⚡' }
|
||||
]
|
||||
|
||||
const codes = {
|
||||
Python: {
|
||||
code: `print("Hello, World!")`,
|
||||
filename: 'hello.py',
|
||||
complexity: '极简'
|
||||
},
|
||||
Ruby: {
|
||||
code: `puts "Hello, World!"`,
|
||||
filename: 'hello.rb',
|
||||
complexity: '极简'
|
||||
},
|
||||
Go: {
|
||||
code: `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, World!")
|
||||
}`,
|
||||
filename: 'hello.go',
|
||||
complexity: '简洁'
|
||||
},
|
||||
'Node.js': {
|
||||
code: `console.log("Hello, World!");`,
|
||||
filename: 'hello.js',
|
||||
complexity: '极简'
|
||||
},
|
||||
Java: {
|
||||
code: `public class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, World!");
|
||||
}
|
||||
}`,
|
||||
filename: 'HelloWorld.java',
|
||||
complexity: '冗长'
|
||||
},
|
||||
"C#": {
|
||||
code: `using System;
|
||||
|
||||
class Program {
|
||||
static void Main() {
|
||||
Console.WriteLine("Hello, World!");
|
||||
}
|
||||
}`,
|
||||
filename: 'Program.cs',
|
||||
complexity: '冗长'
|
||||
},
|
||||
Rust: {
|
||||
code: `fn main() {
|
||||
println!("Hello, World!");
|
||||
}`,
|
||||
filename: 'main.rs',
|
||||
complexity: '简洁'
|
||||
},
|
||||
'C++': {
|
||||
code: `#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!" << std::endl;
|
||||
return 0;
|
||||
}`,
|
||||
filename: 'hello.cpp',
|
||||
complexity: '中等'
|
||||
}
|
||||
}
|
||||
|
||||
const analyses = {
|
||||
Python: 'Python 的语法极其简洁,只有一行代码。这也是为什么它被称为"伪代码语言"——读起来就像英语一样自然。没有任何样板代码,直接表达意图。',
|
||||
Ruby: 'Ruby 受 Perl 影响,语法非常优雅。puts 是 "put string" 的缩写,字符串不需要括号(虽然可以加)。Ruby 哲学是"程序员快乐至上"。',
|
||||
Go: 'Go 的语法虽然比 Python 冗长,但非常清晰。package main、import、func main() 都是必要的显式声明,这让代码更容易理解和维护。',
|
||||
'Node.js': 'Node.js 使用 JavaScript,语法简单直接。console.log() 是浏览器和 Node.js 通用的输出方式。前端开发者零学习成本。',
|
||||
Java: 'Java 是典型的"仪式感"语言。class、public static void main、String[] args 都是必须的样板代码。虽然冗长,但结构清晰,适合大型项目。',
|
||||
'C#': 'C# 和 Java 非常相似,同样需要 class 和 Main 方法。using System 类似 Java 的 import,但更现代一些。.NET Core 后跨平台能力大幅提升。',
|
||||
Rust: 'Rust 的 fn main() 和 println! 宏看起来简洁,但 println! 后面的 ! 表示这是一个宏(不是函数)。Rust 的简洁来自于零成本抽象的设计哲学。',
|
||||
'C++': 'C++ 需要 #include 头文件,std::cout 使用流操作符 <<,return 0 表示程序成功退出。虽然比 C 语言简洁(printf),但依然保留了很多底层细节。'
|
||||
}
|
||||
|
||||
const getCode = (lang) => {
|
||||
return codes[lang].code
|
||||
}
|
||||
|
||||
const getFileName = (lang) => {
|
||||
return codes[lang].filename
|
||||
}
|
||||
|
||||
const getLineCount = (lang) => {
|
||||
return codes[lang].code.split('\n').length
|
||||
}
|
||||
|
||||
const getCharCount = (lang) => {
|
||||
return codes[lang].code.replace(/\s/g, '').length
|
||||
}
|
||||
|
||||
const getComplexity = (lang) => {
|
||||
return codes[lang].complexity
|
||||
}
|
||||
|
||||
const getAnalysis = (lang) => {
|
||||
return analyses[lang]
|
||||
}
|
||||
|
||||
const getComplexityWidth = (lang) => {
|
||||
const max = 10 // Java is the longest
|
||||
const lines = getLineCount(lang)
|
||||
return (lines / max) * 100
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.syntax-comparison-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);
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.demo-header h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.lang-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.lang-btn.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.lang-icon {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.lang-name {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.code-display {
|
||||
background: #1e1e1e;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.code-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.8rem 1rem;
|
||||
background: #2d2d2d;
|
||||
border-bottom: 1px solid #3e3e3e;
|
||||
}
|
||||
|
||||
.window-controls {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.control {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.control.red {
|
||||
background: #ff5f56;
|
||||
}
|
||||
|
||||
.control.yellow {
|
||||
background: #ffbd2e;
|
||||
}
|
||||
|
||||
.control.green {
|
||||
background: #27c93f;
|
||||
}
|
||||
|
||||
.filename {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
color: #858585;
|
||||
font-size: 0.85rem;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.code-content {
|
||||
margin: 0;
|
||||
padding: 1.5rem;
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-content code {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.code-stats {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
padding: 1rem 1.5rem;
|
||||
background: #2d2d2d;
|
||||
border-top: 1px solid #3e3e3e;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: #4ec9b0;
|
||||
font-weight: 600;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.analysis-panel {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.analysis-panel h5 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.analysis-content p {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.7;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.complexity-chart {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.complexity-chart h5 {
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.chart-bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.chart-bar-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.chart-label {
|
||||
min-width: 80px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
flex: 1;
|
||||
height: 30px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--vp-c-brand), #8b5cf6);
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.chart-value {
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.85rem;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.language-selector {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.code-stats {
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.chart-bar-wrapper {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.chart-label {
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.chart-value {
|
||||
min-width: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user