Files
test-repo/docs/.vitepress/theme/components/appendix/backend-languages/LanguageEcosystemDemo.vue
T

981 lines
22 KiB
Vue
Raw Normal View History

<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>