feat: add comprehensive backend topics and fix build issues

## 新增内容

### 附录文档扩展
- 扩展前端项目架构文档 (frontend-project-architecture.md)
- 扩展后端项目架构文档 (backend-project-architecture.md)
- 扩展数据治理文档 (data-governance.md)
- 扩展数据可视化文档 (data-visualization.md)
- 扩展分布式系统文档 (distributed-systems.md)
- 扩展高可用文档 (high-availability.md)
- 扩展单体到微服务文档 (monolith-to-microservices.md)
- 扩展系统设计方法论文档 (system-design-methodology.md)
- 扩展 Docker 容器文档 (docker-containers.md)
- 扩展 Kubernetes 文档 (kubernetes.md)
- 扩展 Linux 基础文档 (linux-basics.md)
- 扩展神经网络文档 (neural-networks.md)

### 新增交互式组件
- 数据治理组件: DataQualityDemo, DataGovernanceFrameworkDemo, DataLineageDemo
- 数据可视化组件: ChartTypeSelectorDemo, DashboardLayoutDemo
- 分布式系统组件: CAPTheoremDemo, ConsistencyModelsDemo, DistributedChallengesDemo
- 高可用组件: AvailabilityCalculatorDemo, FailoverStrategyDemo
- 系统设计组件: SystemDesignStepsDemo, CapacityEstimationDemo
- Docker 容器组件: DockerArchitectureDemo, DockerLifecycleDemo
- Kubernetes 组件: K8sArchitectureDemo, K8sWorkloadsDemo
- Linux 基础组件: LinuxFileSystemDemo, LinuxCommandDemo, LinuxPermissionsDemo
- 神经网络组件: NeuronDemo, NetworkLayersDemo, NetworkArchitectureDemo
- 单体到微服务组件: ArchEvolutionDemo
- 项目架构组件: ProjectArchitectureComparisonDemo
- 附录导航组件: AppendixFlowMap

### 英文版重构
- 将 en-us 目录重命名为 en
- 更新相关配置和组件中的语言代码

## Bug 修复
- 修复 index.js 中重复的组件导入语句导致的 build 失败
- 恢复被注释的 InvertedIndexDemo 和 SearchRelevanceDemo 导入
- 修复 HomeFeatures.vue 中 en-us 与 config.mjs 中 en 不一致导致的语言切换问题

## 其他改进
- 添加构建脚本 (scripts/build.mjs)
- 更新依赖版本
This commit is contained in:
sanbuphy
2026-02-26 04:35:28 +08:00
parent df51f84ab5
commit ef70b1d8e1
84 changed files with 12917 additions and 3477 deletions
+763 -46
View File
@@ -20,7 +20,7 @@ const localeMap = {
lang: 'zh-CN',
hreflang: 'zh-CN'
},
'en-us': {
'en': {
ogLocale: 'en_US',
twitterSite: '@datawhale',
lang: 'en-US',
@@ -203,6 +203,709 @@ const commonThemeConfig = {
}
}
const productManagerSidebarEn = [
{
text: 'Getting Started',
collapsed: false,
items: [
{ text: 'Learning Map', link: '/en/stage-0/0.1-learning-map/' },
{
text: 'AI Era: If You Can Speak, You Can Code',
link: '/en/stage-0/0.2-ai-capabilities-through-games/'
}
]
},
{
text: 'Product Prototype Skills',
collapsed: false,
items: [
{
text: 'Master AI Programming Tools',
link: '/en/stage-1/1.1-introduction-to-ai-ide/'
},
{
text: 'Find Great Ideas',
link: '/en/stage-1/1.0-finding-great-idea/'
},
{
text: 'Build Product Prototypes',
link: '/en/stage-1/1.2-building-prototype/'
},
{
text: 'Integrate AI Capabilities',
link: '/en/stage-1/1.3-integrating-ai-capabilities/'
},
{
text: 'Complete Project Practice',
link: '/en/stage-1/1.4-complete-project-practice/'
}
]
},
{
text: 'Appendix: Business Thinking',
collapsed: false,
items: [
{
text: 'Product Thinking and Solution Design',
link: '/en/stage-1/appendix-a-product-thinking/'
},
{
text: 'AI Industry Application Scenarios (B-end)',
link: '/en/stage-1/appendix-industry-scenarios/'
},
{
text: 'AI Consumer Scenarios Inspiration (C-end)',
link: '/en/stage-1/appendix-c-consumer-scenarios/'
}
]
},
{
text: 'Appendix: Technical Solutions',
collapsed: false,
items: [
{
text: 'What to do if you encounter errors',
link: '/en/stage-1/appendix-b-common-errors/'
},
{
text: 'Comparison of Seven AI Programming Tools',
link: '/en/stage-1/appendix-articles/example0-1/vibe-coding-tools-snake-game-tutorial'
},
{
text: 'Design Websites with Agents',
link: '/en/stage-1/appendix-articles/example0-2/vibe-coding-tools-build-website-with-ai-coding-and-design-agents'
}
]
}
]
const stage2SidebarEn = [
{
text: 'Frontend Development',
collapsed: false,
items: [
{
text: 'Frontend 0: Lovart Assets',
link: '/zh-cn/stage-2/frontend/2.0-lovart-assets/'
},
{
text: 'Frontend 1: Figma & MasterGo',
link: '/zh-cn/stage-2/frontend/2.1-figma-mastergo/'
},
{
text: 'Frontend 2: UI Design',
link: '/zh-cn/stage-2/frontend/2.2-ui-design/'
},
{
text: 'Frontend 3: UI Design Specs',
link: '/zh-cn/stage-2/frontend/2.3-multi-product-ui/'
},
{
text: 'Frontend 4: Hogwarts Portraits',
link: '/zh-cn/stage-2/frontend/2.4-hogwarts-portraits/'
}
]
},
{
text: 'Backend & Full-Stack',
collapsed: false,
items: [
{
text: 'Backend 1: What is API',
link: '/zh-cn/stage-2/backend/2.1-what-is-api/extra2/'
},
{
text: 'Backend 2: Database & Supabase',
link: '/zh-cn/stage-2/backend/2.2-database-supabase/chapter5/'
},
{
text: 'Backend 3: AI-Assisted API Code',
link: '/zh-cn/stage-2/backend/2.3-ai-interface-code/'
},
{
text: 'Backend 4: Git Workflow',
link: '/zh-cn/stage-2/backend/2.4-git-workflow/extra1/'
},
{
text: 'Backend 5: Deployment',
link: '/zh-cn/stage-2/backend/2.5-zeabur-deployment/extra6/'
},
{
text: 'Backend 6: Modern CLI Tools',
link: '/zh-cn/stage-2/backend/2.6-modern-cli/extra7/'
},
{
text: 'Backend 7: Stripe Payment',
link: '/zh-cn/stage-2/backend/2.7-stripe-payment/'
}
]
},
{
text: 'Assignments',
collapsed: false,
items: [
{
text: 'Assignment 1: Full-Stack App',
link: '/zh-cn/stage-2/assignments/2.1-fullstack-app/'
},
{
text: 'Assignment 2: UI Library & Trae',
link: '/zh-cn/stage-2/assignments/2.2-modern-frontend-trae/'
}
]
},
{
text: 'AI Capabilities Appendix',
collapsed: false,
items: [
{
text: 'AI 1: Dify & Knowledge Base',
link: '/zh-cn/stage-2/ai-capabilities/2.1-dify-knowledge-base/chapter3/'
},
{
text: 'AI 2: AI Dictionary & Multimodal API',
link: '/zh-cn/stage-2/ai-capabilities/2.2-multimodal-api/extra3/'
}
]
}
]
const stage3SidebarEn = [
{
text: 'Core Skills',
collapsed: false,
items: [
{
text: 'Advanced 1: MCP & Claude Code Skills',
link: '/zh-cn/stage-3/core-skills/3.1-mcp-claude-code-skills/'
},
{
text: 'Advanced 2: Long Running Tasks',
link: '/zh-cn/stage-3/core-skills/3.2-long-running-tasks/'
}
]
},
{
text: 'Multi-Platform',
collapsed: false,
items: [
{
text: 'Advanced 3: WeChat Mini Program',
link: '/zh-cn/stage-3/cross-platform/3.3-wechat-miniprogram/'
},
{
text: 'Advanced 4: WeChat Mini Program (Backend)',
link: '/zh-cn/stage-3/cross-platform/3.4-wechat-miniprogram-backend/'
},
{
text: 'Advanced 5: Android App - Compose',
link: '/zh-cn/stage-3/cross-platform/3.5-android-app/'
},
{
text: 'Advanced 6: iOS App - SwiftUI',
link: '/zh-cn/stage-3/cross-platform/3.6-ios-app/'
}
]
},
{
text: 'Personal Brand',
collapsed: false,
items: [
{
text: 'Advanced 7: Personal Website & Blog',
link: '/zh-cn/stage-3/personal-brand/3.7-personal-website-blog/'
}
]
},
{
text: 'AI Advanced Appendix',
collapsed: false,
items: [
{
text: 'AI Advanced 1: RAG Introduction',
link: '/zh-cn/stage-3/ai-advanced/3.a1-rag-introduction/'
},
{
text: 'AI Advanced 2: LangGraph & Advanced RAG',
link: '/zh-cn/stage-3/ai-advanced/3.a2-langgraph-advanced-rag/'
}
]
}
]
const appendixSidebarEn = [
{
text: 'I. Computer Fundamentals',
collapsed: false,
items: [
{
text: 'Full-Stack in Vibe Coding Era',
link: '/zh-cn/appendix/1-computer-fundamentals/vibe-coding-fullstack'
},
{
text: 'Power On to Web Visit',
link: '/zh-cn/appendix/1-computer-fundamentals/power-on-to-web'
},
{
text: 'Transistor to CPU',
link: '/zh-cn/appendix/1-computer-fundamentals/transistor-to-cpu'
},
{
text: 'Computer Organization',
link: '/zh-cn/appendix/1-computer-fundamentals/computer-organization'
},
{
text: 'Operating Systems',
link: '/zh-cn/appendix/1-computer-fundamentals/operating-systems'
},
{
text: 'Data Encoding & Storage',
link: '/zh-cn/appendix/1-computer-fundamentals/data-encoding-storage'
},
{
text: 'Computer Networks',
link: '/zh-cn/appendix/1-computer-fundamentals/computer-networks'
},
{
text: 'Data Structures',
link: '/zh-cn/appendix/1-computer-fundamentals/data-structures'
},
{
text: 'Algorithm Thinking',
link: '/zh-cn/appendix/1-computer-fundamentals/algorithm-thinking'
},
{
text: 'Programming Languages',
link: '/zh-cn/appendix/1-computer-fundamentals/programming-languages'
},
{
text: 'Compilers Intro',
link: '/zh-cn/appendix/1-computer-fundamentals/compilers'
},
{
text: 'Type Systems Intro',
link: '/zh-cn/appendix/1-computer-fundamentals/type-systems'
}
]
},
{
text: 'II. Tools & Environment',
collapsed: false,
items: [
{
text: 'IDE Basics',
link: '/zh-cn/appendix/2-development-tools/ide-basics'
},
{
text: 'Command Line & Shell',
link: '/zh-cn/appendix/2-development-tools/command-line-shell'
},
{
text: 'Git: Code Time Machine',
link: '/zh-cn/appendix/2-development-tools/git-version-control'
},
{
text: 'Env Vars & PATH',
link: '/zh-cn/appendix/2-development-tools/environment-path'
},
{
text: 'Ports & Localhost',
link: '/zh-cn/appendix/2-development-tools/ports-localhost'
},
{
text: 'SSH & Key Auth',
link: '/zh-cn/appendix/2-development-tools/ssh-authentication'
},
{
text: 'Package Managers',
link: '/zh-cn/appendix/2-development-tools/package-managers'
},
{
text: 'Art of Debugging',
link: '/zh-cn/appendix/2-development-tools/debugging-art/'
},
{
text: 'Regex',
link: '/zh-cn/appendix/2-development-tools/regex'
}
]
},
{
text: 'III. Browser & Frontend',
collapsed: false,
items: [
{
text: 'JavaScript Deep Dive',
link: '/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive'
},
{
text: 'TypeScript Intro',
link: '/zh-cn/appendix/3-browser-and-frontend/typescript'
},
{
text: 'Frontend Frameworks',
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks'
},
{
text: 'Browser as OS',
link: '/zh-cn/appendix/3-browser-and-frontend/browser-as-os'
},
{
text: 'Rendering Pipeline',
link: '/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering'
},
{
text: 'HTML / CSS Layout',
link: '/zh-cn/appendix/3-browser-and-frontend/html-css-layout'
},
{
text: 'JS Runtime',
link: '/zh-cn/appendix/3-browser-and-frontend/javascript-runtime'
},
{
text: 'Nature of Frameworks',
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-framework-nature'
},
{
text: 'State Management',
link: '/zh-cn/appendix/3-browser-and-frontend/state-management'
},
{
text: 'Routing & Navigation',
link: '/zh-cn/appendix/3-browser-and-frontend/routing-navigation'
},
{
text: 'Graphics & Animation',
link: '/zh-cn/appendix/3-browser-and-frontend/graphics-animation'
},
{
text: 'Real-time Comm',
link: '/zh-cn/appendix/3-browser-and-frontend/realtime-communication'
},
{
text: 'Web Performance',
link: '/zh-cn/appendix/3-browser-and-frontend/web-performance'
},
{
text: 'Frontend Engineering',
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-engineering'
},
{
text: 'Project Architecture',
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-project-architecture'
},
{
text: 'A11y & i18n',
link: '/zh-cn/appendix/3-browser-and-frontend/a11n-i18n'
}
]
},
{
text: 'IV. Server & Backend',
collapsed: false,
items: [
{
text: 'Backend Languages',
link: '/zh-cn/appendix/4-server-and-backend/backend-languages'
},
{
text: 'Client Languages',
link: '/zh-cn/appendix/4-server-and-backend/client-languages'
},
{
text: 'Cross-platform Solutions',
link: '/zh-cn/appendix/4-server-and-backend/cross-platform'
},
{
text: 'HTTP Protocol',
link: '/zh-cn/appendix/4-server-and-backend/http-protocol'
},
{
text: 'Request Journey',
link: '/zh-cn/appendix/4-server-and-backend/request-journey'
},
{
text: 'Web Frameworks',
link: '/zh-cn/appendix/4-server-and-backend/web-frameworks'
},
{
text: 'API Intro',
link: '/zh-cn/appendix/4-server-and-backend/api-intro'
},
{
text: 'API Design Philosophy',
link: '/zh-cn/appendix/4-server-and-backend/api-design'
},
{
text: 'Serialization',
link: '/zh-cn/appendix/4-server-and-backend/serialization'
},
{
text: 'Auth & Authorization',
link: '/zh-cn/appendix/4-server-and-backend/auth-authorization'
},
{
text: 'Concurrency & Async',
link: '/zh-cn/appendix/4-server-and-backend/concurrency-async'
},
{
text: 'Caching Strategies',
link: '/zh-cn/appendix/4-server-and-backend/caching'
},
{
text: 'Message Queues',
link: '/zh-cn/appendix/4-server-and-backend/message-queues'
},
{
text: 'Async Task Queues',
link: '/zh-cn/appendix/4-server-and-backend/async-task-queues'
},
{
text: 'Rate Limiting',
link: '/zh-cn/appendix/4-server-and-backend/rate-limiting-backpressure'
},
{
text: 'Search Engine Principles',
link: '/zh-cn/appendix/4-server-and-backend/search-engines'
},
{
text: 'File Storage',
link: '/zh-cn/appendix/4-server-and-backend/file-storage'
},
{
text: 'Backend Architecture',
link: '/zh-cn/appendix/4-server-and-backend/backend-layered-architecture'
},
{
text: 'Project Architecture',
link: '/zh-cn/appendix/4-server-and-backend/backend-project-architecture'
},
{
text: 'DSL Intro',
link: '/zh-cn/appendix/4-server-and-backend/domain-specific-languages'
}
]
},
{
text: 'V. Data',
collapsed: false,
items: [
{
text: 'Database Fundamentals',
link: '/zh-cn/appendix/5-data/database-fundamentals'
},
{
text: 'Data Models Panorama',
link: '/zh-cn/appendix/5-data/data-models'
},
{
text: 'Data Tracking',
link: '/zh-cn/appendix/5-data/data-tracking'
},
{
text: 'Data Analysis',
link: '/zh-cn/appendix/5-data/data-analysis'
},
{
text: 'A/B Testing',
link: '/zh-cn/appendix/5-data/ab-testing'
},
{
text: 'Data Visualization',
link: '/zh-cn/appendix/5-data/data-visualization'
},
{
text: 'Data Governance',
link: '/zh-cn/appendix/5-data/data-governance'
}
]
},
{
text: 'VI. Architecture',
collapsed: false,
items: [
{
text: 'Monolith to Microservices',
link: '/zh-cn/appendix/6-architecture-and-system-design/monolith-to-microservices'
},
{
text: 'Distributed Systems',
link: '/zh-cn/appendix/6-architecture-and-system-design/distributed-systems'
},
{
text: 'HA & Disaster Recovery',
link: '/zh-cn/appendix/6-architecture-and-system-design/high-availability'
},
{
text: 'System Design',
link: '/zh-cn/appendix/6-architecture-and-system-design/system-design-methodology'
}
]
},
{
text: 'VII. Infrastructure',
collapsed: false,
items: [
{
text: 'Linux Basics',
link: '/zh-cn/appendix/7-infrastructure-and-operations/linux-basics'
},
{
text: 'Docker Containers',
link: '/zh-cn/appendix/7-infrastructure-and-operations/docker-containers'
},
{
text: 'Kubernetes',
link: '/zh-cn/appendix/7-infrastructure-and-operations/kubernetes'
},
{
text: 'CI / CD',
link: '/zh-cn/appendix/7-infrastructure-and-operations/ci-cd'
},
{
text: 'Domain, DNS & HTTPS',
link: '/zh-cn/appendix/7-infrastructure-and-operations/dns-https'
},
{
text: 'Load Balancing',
link: '/zh-cn/appendix/7-infrastructure-and-operations/load-balancing-gateway'
},
{
text: 'Gateway & Reverse Proxy',
link: '/zh-cn/appendix/7-infrastructure-and-operations/gateway-proxy'
},
{
text: 'Cloud Platforms',
link: '/zh-cn/appendix/7-infrastructure-and-operations/cloud-platforms'
},
{
text: 'IAM',
link: '/zh-cn/appendix/7-infrastructure-and-operations/cloud-iam'
},
{
text: 'Storage & CDN',
link: '/zh-cn/appendix/7-infrastructure-and-operations/cloud-storage-cdn'
},
{
text: 'IaC',
link: '/zh-cn/appendix/7-infrastructure-and-operations/infrastructure-as-code'
},
{
text: 'Monitoring & Logging',
link: '/zh-cn/appendix/7-infrastructure-and-operations/monitoring-logging'
},
{
text: 'Incident Response',
link: '/zh-cn/appendix/7-infrastructure-and-operations/incident-response'
}
]
},
{
text: 'VIII. Artificial Intelligence',
collapsed: false,
items: [
{
text: 'AI History & Concepts',
link: '/zh-cn/appendix/8-artificial-intelligence/ai-history'
},
{
text: 'Neural Networks',
link: '/zh-cn/appendix/8-artificial-intelligence/neural-networks'
},
{
text: 'Transformer & Attention',
link: '/zh-cn/appendix/8-artificial-intelligence/transformer-attention'
},
{
text: 'LLM Principles',
link: '/zh-cn/appendix/8-artificial-intelligence/llm-principles'
},
{
text: 'Prompt Engineering',
link: '/zh-cn/appendix/8-artificial-intelligence/prompt-engineering'
},
{
text: 'Context Engineering',
link: '/zh-cn/appendix/8-artificial-intelligence/context-engineering'
},
{
text: 'Multimodal Models',
link: '/zh-cn/appendix/8-artificial-intelligence/multimodal-models'
},
{
text: 'Image Generation',
link: '/zh-cn/appendix/8-artificial-intelligence/image-generation'
},
{
text: 'Speech Synthesis',
link: '/zh-cn/appendix/8-artificial-intelligence/speech-synthesis-recognition'
},
{
text: 'Embedding & Vector Search',
link: '/zh-cn/appendix/8-artificial-intelligence/embedding-vector-retrieval'
},
{
text: 'RAG Architecture',
link: '/zh-cn/appendix/8-artificial-intelligence/rag'
},
{
text: 'AI Agent & Tools',
link: '/zh-cn/appendix/8-artificial-intelligence/ai-agents'
},
{
text: 'AI Protocols (MCP)',
link: '/zh-cn/appendix/8-artificial-intelligence/ai-protocols'
},
{
text: 'Fine-tuning & Deployment',
link: '/zh-cn/appendix/8-artificial-intelligence/model-finetuning-deployment'
},
{
text: 'AI Native Design',
link: '/zh-cn/appendix/8-artificial-intelligence/ai-native-app-design'
},
{
text: 'AI Dictionary',
link: '/zh-cn/appendix/8-artificial-intelligence/ai-capability-dictionary'
}
]
},
{
text: 'IX. Engineering Excellence',
collapsed: false,
items: [
{
text: 'Code Quality',
link: '/zh-cn/appendix/9-engineering-excellence/code-quality-refactoring'
},
{
text: 'Testing Strategies',
link: '/zh-cn/appendix/9-engineering-excellence/testing-strategies'
},
{
text: 'Design Patterns',
link: '/zh-cn/appendix/9-engineering-excellence/design-patterns'
},
{
text: 'Security Thinking',
link: '/zh-cn/appendix/9-engineering-excellence/security-thinking'
},
{
text: 'Technical Writing',
link: '/zh-cn/appendix/9-engineering-excellence/technical-writing'
},
{
text: 'Open Source',
link: '/zh-cn/appendix/9-engineering-excellence/open-source-collaboration'
},
{
text: 'Tech Selection',
link: '/zh-cn/appendix/9-engineering-excellence/technology-selection'
}
]
}
]
const productManagerSidebar = [
{
text: '新手入门',
@@ -288,6 +991,15 @@ export default defineConfig({
base: base,
ignoreDeadLinks: true,
// Vite 配置
vite: {
server: {
watch: {
ignored: ['**/docs/.vitepress/dist/**']
}
}
},
// Sitemap 配置
sitemap: {
hostname: 'https://datawhalechina.github.io/easy-vibe',
@@ -726,7 +1438,7 @@ export default defineConfig({
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-engineering'
},
{
text: '前端项目架构设计',
text: '前端项目文件目录设计',
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-project-architecture'
},
{
@@ -812,7 +1524,7 @@ export default defineConfig({
link: '/zh-cn/appendix/4-server-and-backend/backend-layered-architecture'
},
{
text: '后端项目架构设计',
text: '后端项目文件目录设计',
link: '/zh-cn/appendix/4-server-and-backend/backend-project-architecture'
},
{
@@ -1045,15 +1757,15 @@ export default defineConfig({
},
// 英文
'en-us': {
label: 'English (US)',
'en': {
label: 'English',
lang: 'en-US',
link: '/en-us/',
link: '/en/',
title: 'Easy-Vibe Tutorial',
description:
'Learn Vibe Coding from Zero to Advanced - Master AI programming with Claude Code, Cursor, and other AI IDE tools',
head: getSeoHead(
'en-us',
'en',
'Easy-Vibe Tutorial',
'Learn Vibe Coding from Zero to Advanced - Master AI programming with Claude Code, Cursor, and other AI IDE tools'
),
@@ -1064,23 +1776,28 @@ export default defineConfig({
label: 'On this page'
},
nav: [
{ text: 'Home', link: '/en-us/' },
{ text: 'Home', link: '/en/' },
{
text: 'Novice & PM',
link: '/en-us/stage-0/'
link: '/en/stage-0/'
},
{
text: 'Full-Stack Development',
link: '/en-us/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: 'Advanced Development',
link: '/en-us/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: 'Appendix', link: '/en-us/appendix/' }
{ text: 'Appendix', link: '/zh-cn/appendix/index' }
],
// TODO: Add English sidebar when content is ready
sidebar: {}
sidebar: {
'/en/stage-0/': productManagerSidebarEn,
'/en/stage-1/': productManagerSidebarEn,
'/en/stage-2/': stage2SidebarEn,
'/en/stage-3/': stage3SidebarEn,
'/en/appendix/': appendixSidebarEn
}
}
},
@@ -1107,17 +1824,17 @@ export default defineConfig({
{ text: 'ホーム', link: '/ja-jp/' },
{
text: '初心者とPM',
link: '/ja-jp/stage-0/'
link: '/zh-cn/stage-0/'
},
{
text: 'フルスタック開発',
link: '/ja-jp/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: '上級開発',
link: '/ja-jp/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: '付録', link: '/ja-jp/appendix/' }
{ text: '付録', link: '/zh-cn/appendix/' }
],
// TODO: Add Japanese sidebar when content is ready
sidebar: {}
@@ -1145,17 +1862,17 @@ export default defineConfig({
{ text: '首頁', link: '/zh-tw/' },
{
text: '新手與產品原型',
link: '/zh-tw/stage-0/'
link: '/zh-cn/stage-0/'
},
{
text: '初中級開發',
link: '/zh-tw/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: '高級開發',
link: '/zh-tw/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: '附錄', link: '/zh-tw/appendix/' }
{ text: '附錄', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -1180,16 +1897,16 @@ export default defineConfig({
},
nav: [
{ text: '홈', link: '/ko-kr/' },
{ text: '초보자 & PM', link: '/ko-kr/stage-0/' },
{ text: '초보자 & PM', link: '/zh-cn/stage-0/' },
{
text: '풀스택 개발',
link: '/ko-kr/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: '고급 개발',
link: '/ko-kr/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: '부록', link: '/ko-kr/appendix/' }
{ text: '부록', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -1216,17 +1933,17 @@ export default defineConfig({
{ text: 'Inicio', link: '/es-es/' },
{
text: 'Principiante y PM',
link: '/es-es/stage-0/'
link: '/zh-cn/stage-0/'
},
{
text: 'Desarrollo Full Stack',
link: '/es-es/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: 'Desarrollo Avanzado',
link: '/es-es/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: 'Apéndice', link: '/es-es/appendix/' }
{ text: 'Apéndice', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -1251,16 +1968,16 @@ export default defineConfig({
},
nav: [
{ text: 'Accueil', link: '/fr-fr/' },
{ text: 'Débutant & PM', link: '/fr-fr/stage-0/' },
{ text: 'Débutant & PM', link: '/zh-cn/stage-0/' },
{
text: 'Développement Full Stack',
link: '/fr-fr/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: 'Développement Avancé',
link: '/fr-fr/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: 'Annexe', link: '/fr-fr/appendix/' }
{ text: 'Annexe', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -1285,16 +2002,16 @@ export default defineConfig({
},
nav: [
{ text: 'Start', link: '/de-de/' },
{ text: 'Anfänger & PM', link: '/de-de/stage-0/' },
{ text: 'Anfänger & PM', link: '/zh-cn/stage-0/' },
{
text: 'Full Stack Entwicklung',
link: '/de-de/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: 'Fortgeschrittene Entwicklung',
link: '/de-de/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: 'Anhang', link: '/de-de/appendix/' }
{ text: 'Anhang', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -1321,17 +2038,17 @@ export default defineConfig({
{ text: 'الرئيسية', link: '/ar-sa/' },
{
text: 'مبتدأ & PM',
link: '/ar-sa/stage-0/'
link: '/zh-cn/stage-0/'
},
{
text: 'تطوير Full Stack',
link: '/ar-sa/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: 'تطوير متقدم',
link: '/ar-sa/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: 'ملحق', link: '/ar-sa/appendix/' }
{ text: 'ملحق', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -1358,17 +2075,17 @@ export default defineConfig({
{ text: 'Trang chủ', link: '/vi-vn/' },
{
text: 'Người mới & PM',
link: '/vi-vn/stage-0/'
link: '/zh-cn/stage-0/'
},
{
text: 'Phát triển Full Stack',
link: '/vi-vn/stage-2/'
link: '/zh-cn/stage-2/'
},
{
text: 'Phát triển Nâng cao',
link: '/vi-vn/stage-3/'
link: '/zh-cn/stage-3/'
},
{ text: 'Phụ lục', link: '/vi-vn/appendix/' }
{ text: 'Phụ lục', link: '/zh-cn/appendix/' }
],
sidebar: {}
}
@@ -0,0 +1,390 @@
<script setup>
import { ref } from 'vue'
import { withBase } from 'vitepress'
const categories = [
{
id: 'computer-fundamentals',
name: '计算机基础',
icon: '💻',
color: '#10b981',
bgGradient: 'linear-gradient(135deg, #10b98115, #10b98108)',
articles: [
{ title: 'Vibe Coding 全栈开发', path: '/zh-cn/appendix/1-computer-fundamentals/vibe-coding-fullstack' },
{ title: '从晶体管到 CPU', path: '/zh-cn/appendix/1-computer-fundamentals/transistor-to-cpu' },
{ title: '操作系统', path: '/zh-cn/appendix/1-computer-fundamentals/operating-systems' },
{ title: '数据结构', path: '/zh-cn/appendix/1-computer-fundamentals/data-structures' },
{ title: '算法思维入门', path: '/zh-cn/appendix/1-computer-fundamentals/algorithm-thinking' },
{ title: '编程语言图谱', path: '/zh-cn/appendix/1-computer-fundamentals/programming-languages' },
{ title: '网络基础', path: '/zh-cn/appendix/1-computer-fundamentals/computer-networks' }
]
},
{
id: 'development-tools',
name: '开发工具',
icon: '🔧',
color: '#3b82f6',
bgGradient: 'linear-gradient(135deg, #3b82f615, #3b82f608)',
articles: [
{ title: 'IDE 基础', path: '/zh-cn/appendix/2-development-tools/ide-basics' },
{ title: '命令行与 Shell', path: '/zh-cn/appendix/2-development-tools/command-line-shell' },
{ title: 'Git 版本控制', path: '/zh-cn/appendix/2-development-tools/git-version-control' },
{ title: '环境变量与 PATH', path: '/zh-cn/appendix/2-development-tools/environment-path' },
{ title: '包管理器', path: '/zh-cn/appendix/2-development-tools/package-managers' },
{ title: '调试的艺术', path: '/zh-cn/appendix/2-development-tools/debugging-art/' }
]
},
{
id: 'browser-frontend',
name: '浏览器与前端',
icon: '🌍',
color: '#f59e0b',
bgGradient: 'linear-gradient(135deg, #f59e0b15, #f59e0b08)',
articles: [
{ title: 'JavaScript 深入', path: '/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive' },
{ title: 'TypeScript', path: '/zh-cn/appendix/3-browser-and-frontend/typescript' },
{ title: '浏览器是一个操作系统', path: '/zh-cn/appendix/3-browser-and-frontend/browser-as-os' },
{ title: '浏览器渲染管道', path: '/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering' },
{ title: '前端框架对比', path: '/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks' },
{ title: '前端工程化', path: '/zh-cn/appendix/3-browser-and-frontend/frontend-engineering' }
]
},
{
id: 'server-backend',
name: '服务端与后端',
icon: '⚙️',
color: '#8b5cf6',
bgGradient: 'linear-gradient(135deg, #8b5cf615, #8b5cf608)',
articles: [
{ title: '后端语言对比', path: '/zh-cn/appendix/4-server-and-backend/backend-languages' },
{ title: 'HTTP 协议', path: '/zh-cn/appendix/4-server-and-backend/http-protocol' },
{ title: 'API 设计哲学', path: '/zh-cn/appendix/4-server-and-backend/api-design' },
{ title: 'Web 框架的本质', path: '/zh-cn/appendix/4-server-and-backend/web-frameworks' },
{ title: '认证与授权', path: '/zh-cn/appendix/4-server-and-backend/auth-authorization' },
{ title: '缓存策略', path: '/zh-cn/appendix/4-server-and-backend/caching' },
{ title: '消息队列', path: '/zh-cn/appendix/4-server-and-backend/message-queues' }
]
},
{
id: 'data',
name: '数据',
icon: '📊',
color: '#ec4899',
bgGradient: 'linear-gradient(135deg, #ec489915, #ec489908)',
articles: [
{ title: 'SQL', path: '/zh-cn/appendix/5-data/sql' },
{ title: '数据库原理', path: '/zh-cn/appendix/5-data/database-fundamentals' },
{ title: '数据模型全景', path: '/zh-cn/appendix/5-data/data-models' },
{ title: '数据分析基础', path: '/zh-cn/appendix/5-data/data-analysis' }
]
},
{
id: 'architecture',
name: '架构设计',
icon: '🏗️',
color: '#14b8a6',
bgGradient: 'linear-gradient(135deg, #14b8a615, #14b8a608)',
articles: [
{ title: '从单体到微服务', path: '/zh-cn/appendix/6-architecture-and-system-design/monolith-to-microservices' },
{ title: '分布式系统', path: '/zh-cn/appendix/6-architecture-and-system-design/distributed-systems' },
{ title: '高可用与容灾', path: '/zh-cn/appendix/6-architecture-and-system-design/high-availability' },
{ title: '系统设计方法论', path: '/zh-cn/appendix/6-architecture-and-system-design/system-design-methodology' }
]
},
{
id: 'infrastructure',
name: '基础设施',
icon: '☁️',
color: '#06b6d4',
bgGradient: 'linear-gradient(135deg, #06b6d415, #06b6d408)',
articles: [
{ title: 'Linux 基础', path: '/zh-cn/appendix/7-infrastructure-and-operations/linux-basics' },
{ title: 'Docker 容器化', path: '/zh-cn/appendix/7-infrastructure-and-operations/docker-containers' },
{ title: 'Kubernetes', path: '/zh-cn/appendix/7-infrastructure-and-operations/kubernetes' },
{ title: 'CI/CD 自动化', path: '/zh-cn/appendix/7-infrastructure-and-operations/ci-cd' }
]
},
{
id: 'ai',
name: '人工智能',
icon: '🤖',
color: '#f97316',
bgGradient: 'linear-gradient(135deg, #f9731615, #f9731608)',
articles: [
{ title: 'AI 简史', path: '/zh-cn/appendix/8-artificial-intelligence/ai-history' },
{ title: '神经网络', path: '/zh-cn/appendix/8-artificial-intelligence/neural-networks' },
{ title: 'Transformer', path: '/zh-cn/appendix/8-artificial-intelligence/transformer-attention' },
{ title: '大语言模型原理', path: '/zh-cn/appendix/8-artificial-intelligence/llm-principles' },
{ title: 'RAG 架构', path: '/zh-cn/appendix/8-artificial-intelligence/rag' },
{ title: 'AI Agent', path: '/zh-cn/appendix/8-artificial-intelligence/ai-agents' }
]
},
{
id: 'engineering',
name: '工程素养',
icon: '✨',
color: '#a855f7',
bgGradient: 'linear-gradient(135deg, #a855f715, #a855f708)',
articles: [
{ title: '设计模式', path: '/zh-cn/appendix/9-engineering-excellence/design-patterns' },
{ title: '代码质量与重构', path: '/zh-cn/appendix/9-engineering-excellence/code-quality-refactoring' },
{ title: '测试策略', path: '/zh-cn/appendix/9-engineering-excellence/testing-strategies' },
{ title: '技术写作', path: '/zh-cn/appendix/9-engineering-excellence/technical-writing' },
{ title: '开源协作', path: '/zh-cn/appendix/9-engineering-excellence/open-source-collaboration' }
]
}
]
const activeCategory = ref(null)
const hoveredArticle = ref(null)
const toggleCategory = (id) => {
activeCategory.value = activeCategory.value === id ? null : id
}
const articleCount = categories.reduce((sum, cat) => sum + cat.articles.length, 0)
</script>
<template>
<div class="appendix-bento">
<div class="bento-header">
<h3 class="bento-title">探索附录</h3>
<p class="bento-subtitle">9 个主题方向 · {{ articleCount }} 篇文章</p>
</div>
<div class="bento-grid">
<div
v-for="category in categories"
:key="category.id"
class="bento-card"
:class="{ active: activeCategory === category.id }"
:style="{
'--card-color': category.color,
'--card-bg': category.bgGradient
}"
@click="toggleCategory(category.id)"
>
<div class="card-icon">{{ category.icon }}</div>
<div class="card-content">
<h4 class="card-title">{{ category.name }}</h4>
<p class="card-count">{{ category.articles.length }} </p>
</div>
<Transition name="pop">
<div v-if="activeCategory === category.id" class="card-articles">
<a
v-for="article in category.articles"
:key="article.path"
:href="withBase(article.path)"
class="article-item"
@mouseenter="hoveredArticle = article.path"
@mouseleave="hoveredArticle = null"
>
<span class="article-dot"></span>
<span class="article-title">{{ article.title }}</span>
<span class="article-arrow"></span>
</a>
</div>
</Transition>
<div class="card-indicator" v-if="activeCategory !== category.id">
<span>{{ category.articles.length }} </span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.appendix-bento {
padding: 1rem 0;
}
.bento-header {
text-align: center;
margin-bottom: 1.5rem;
}
.bento-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 0.25rem;
letter-spacing: -0.02em;
}
.bento-subtitle {
font-size: 0.9rem;
color: var(--vp-c-text-2);
margin: 0;
}
.bento-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
}
@media (max-width: 900px) {
.bento-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.bento-grid {
grid-template-columns: 1fr;
}
}
.bento-card {
position: relative;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 16px;
padding: 1.25rem;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
}
.bento-card::before {
content: '';
position: absolute;
inset: 0;
background: var(--card-bg);
opacity: 0;
transition: opacity 0.3s ease;
}
.bento-card:hover::before {
opacity: 1;
}
.bento-card:hover {
border-color: var(--card-color);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
}
.bento-card.active {
border-color: var(--card-color);
background: var(--vp-c-bg);
}
.bento-card.active::before {
opacity: 1;
}
.card-icon {
font-size: 2rem;
margin-bottom: 0.75rem;
position: relative;
}
.card-content {
position: relative;
}
.card-title {
font-size: 1.1rem;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 0.25rem;
}
.card-count {
font-size: 0.85rem;
color: var(--vp-c-text-2);
margin: 0;
}
.card-indicator {
position: absolute;
bottom: 1.25rem;
right: 1.25rem;
font-size: 0.8rem;
color: var(--vp-c-text-3);
transition: all 0.2s ease;
}
.bento-card:hover .card-indicator {
color: var(--card-color);
}
.card-articles {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--vp-c-bg);
border-radius: 15px;
padding: 1rem;
overflow-y: auto;
z-index: 2;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.article-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 0.75rem;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s ease;
color: var(--vp-c-text-1);
}
.article-item:hover {
background: var(--vp-c-bg-soft);
}
.article-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--card-color);
flex-shrink: 0;
}
.article-title {
flex: 1;
font-size: 0.85rem;
font-weight: 500;
}
.article-arrow {
opacity: 0;
color: var(--card-color);
transition: all 0.2s ease;
font-size: 0.9rem;
}
.article-item:hover .article-arrow {
opacity: 1;
transform: translateX(3px);
}
/* Animations */
.pop-enter-active {
transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pop-leave-active {
transition: all 0.2s ease;
}
.pop-enter-from {
opacity: 0;
transform: scale(0.95);
}
.pop-leave-to {
opacity: 0;
transform: scale(0.95);
}
</style>
@@ -220,7 +220,7 @@ const i18n = {
btn: '>_ Start'
}
},
'en-us': {
'en': {
nav: {
title: 'Easy-Vibe Tutorial',
home: 'Home',
@@ -231,27 +231,27 @@ const i18n = {
start: 'Start Learning'
},
stage1: {
cat: 'Stage 1 · Novice & PM',
cat: 'Stage 1 · Getting Started',
title: 'Zero to Hero, <br><span class="highlight">Be Your Own PM.</span>',
sub: 'No CS background needed. Just speak your idea, and AI will turn it into high-fidelity web prototypes.',
sub: 'No CS background needed. Just speak your ideas—AI will turn them into high-fidelity web prototypes.',
cards: [
{
title: 'AI Product Manager',
desc: 'From idea to prototype, just by speaking.',
sub: 'Non-tech friendly',
link: '/en-us/stage-0/'
title: 'Learning Map',
desc: 'Understand the complete learning path from zero to full-stack development.',
sub: 'All Ages Friendly',
link: '/en/stage-0/0.1-learning-map/'
},
{
title: 'Gamified Intro',
desc: 'Build Snake, Tetris, and break the fear of code.',
sub: 'Learn by playing',
link: '/en-us/stage-0/'
desc: 'Experience the magic of AI programming by building games like Snake.',
sub: 'Learn by Playing',
link: '/en/stage-0/0.2-ai-capabilities-through-games/'
},
{
title: 'Vibe Coding',
desc: 'Master the core of AI coding: Prompt Engineering & Context.',
desc: 'Master the core of AI coding: From product ideas to interactive prototypes.',
sub: 'Core Mindset',
link: '/en-us/stage-0/'
link: '/en/stage-0/'
}
]
},
@@ -259,25 +259,25 @@ const i18n = {
cat: 'Stage 2 · Junior/Mid Dev',
title:
'Go Full Stack, <br><span class="highlight">Build Real Apps.</span>',
sub: 'Master frontend-backend separation. Build commercial-grade projects with DB, API, and complex interactions.',
sub: 'Master frontend-backend separation and build commercial-grade projects with DB and API.',
cards: [
{
title: 'Full Stack',
headline: 'Frontend & Backend.',
desc: 'From DB design to API and components, build a modern web app completely.',
link: '/en-us/stage-2/'
link: '/zh-cn/stage-2/assignments/2.1-fullstack-app/'
},
{
title: 'Real Projects',
headline: 'No Toy Code.',
desc: 'Deep dive into Auth, Storage, File Uploads and core business logic.',
link: '/en-us/stage-2/'
link: '/zh-cn/stage-2/backend/2.2-database-supabase/chapter5/'
},
{
title: 'Deployment',
headline: 'Show the World.',
desc: 'Server config, DNS, CI/CD. The last mile of product delivery.',
link: '/en-us/stage-2/'
link: '/zh-cn/stage-2/backend/2.5-zeabur-deployment/extra6/'
}
]
},
@@ -290,22 +290,22 @@ const i18n = {
{
title: 'WeChat Mini-app',
desc: 'Cross-platform dev, reaching millions of users.',
link: '/en-us/stage-3/'
link: '/zh-cn/stage-3/cross-platform/3.3-wechat-miniprogram/'
},
{
title: 'AI Native Apps',
desc: 'RAG, Agent. Explore the limits of LLMs.',
link: '/en-us/stage-3/'
link: '/zh-cn/stage-3/ai-advanced/3.a1-rag-introduction/'
},
{
title: 'Complex Arch',
desc: 'High concurrency, High availability architecture design.',
link: '/en-us/stage-3/'
desc: 'Architecture design for high concurrency and stable AI tasks.',
link: '/zh-cn/stage-3/core-skills/3.2-long-running-tasks/'
},
{
title: 'Personal Brand',
desc: 'Build your own website and academic blog.',
link: '/en-us/stage-3/'
link: '/zh-cn/stage-3/personal-brand/3.7-personal-website-blog/'
}
]
},
@@ -318,62 +318,62 @@ const i18n = {
{
title: 'AI History',
desc: 'Milestones in AI evolution.',
link: '/en-us/appendix/ai-evolution'
link: '/zh-cn/appendix/8-artificial-intelligence/ai-history'
},
{
title: 'Prompt Eng',
desc: 'Master AI communication skills.',
link: '/en-us/appendix/prompt-engineering'
link: '/zh-cn/appendix/8-artificial-intelligence/prompt-engineering'
},
{
title: 'LLM Intro',
desc: 'Understanding Large Language Models.',
link: '/en-us/appendix/llm-intro'
link: '/zh-cn/appendix/8-artificial-intelligence/llm-principles'
},
{
title: 'AI Agents',
desc: 'Autonomous decision-making AI.',
link: '/en-us/appendix/agent-intro'
link: '/zh-cn/appendix/8-artificial-intelligence/ai-agents'
},
{
title: 'Web Basics',
desc: 'HTML/CSS/JS fundamentals.',
link: '/en-us/appendix/web-basics'
link: '/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive'
},
{
title: 'Frontend Evo',
desc: 'Evolution of frontend tech stack.',
link: '/en-us/appendix/frontend-evolution'
link: '/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks'
},
{
title: 'Backend Arch',
desc: 'From monolith to microservices.',
link: '/en-us/appendix/backend-evolution'
link: '/zh-cn/appendix/4-server-and-backend/backend-layered-architecture'
},
{
title: 'Backend Lang',
desc: 'Choosing the right tech stack.',
link: '/en-us/appendix/backend-languages'
link: '/zh-cn/appendix/4-server-and-backend/backend-languages'
},
{
title: 'Database',
desc: 'Core principles of data storage.',
link: '/en-us/appendix/database-intro'
link: '/zh-cn/appendix/5-data/database-fundamentals'
},
{
title: 'API Design',
desc: 'Designing robust interfaces.',
link: '/en-us/appendix/api-intro'
link: '/zh-cn/appendix/4-server-and-backend/api-intro'
},
{
title: 'Git',
desc: 'Version control mastery.',
link: '/en-us/appendix/git-intro'
link: '/zh-cn/appendix/2-development-tools/git-version-control'
},
{
title: 'Networks',
desc: 'Protocols and communication.',
link: '/en-us/appendix/computer-networks'
link: '/zh-cn/appendix/1-computer-fundamentals/computer-networks'
}
]
},
@@ -1374,12 +1374,12 @@ const i18n = {
import { computed } from 'vue'
const t = computed(() => {
const code = lang.value ? lang.value.toLowerCase() : 'zh-cn'
return i18n[code] || i18n['en-us']
return i18n[code] || i18n['en']
})
const locales = [
{ code: 'zh-cn', text: '简体中文' },
{ code: 'en-us', text: 'English' },
{ code: 'en', text: 'English' },
{ code: 'ja-jp', text: '日本語' },
{ code: 'zh-tw', text: '繁體中文' },
{ code: 'ko-kr', text: '한국어' },
@@ -1699,7 +1699,7 @@ const appendixCards = [
</div>
<a
class="buy-btn"
:href="withBase('/zh-cn/stage-0/0.1-learning-map/')"
:href="withBase(t.stage1.cards[0].link)"
>{{ t.footer.btn }}</a>
</div>
</div>
@@ -155,7 +155,7 @@
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { ref, computed, watch, onUnmounted } from 'vue'
const stage = ref(0)
const expandedOp = ref(-1)
@@ -313,15 +313,26 @@ function getDeviceStatus(i) {
return '等待'
}
const postTimer = ref(null)
const hwTimer = ref(null)
const bootTimer = ref(null)
onUnmounted(() => {
if (postTimer.value) clearInterval(postTimer.value)
if (hwTimer.value) clearInterval(hwTimer.value)
if (bootTimer.value) clearInterval(bootTimer.value)
})
// POST 自检动画
watch(() => stage.value, (newStage) => {
if (postTimer.value) clearInterval(postTimer.value)
if (newStage === 1) {
currentCheck.value = 0
const interval = setInterval(() => {
postTimer.value = setInterval(() => {
if (currentCheck.value < postItems.length) {
currentCheck.value++
} else {
clearInterval(interval)
if (postTimer.value) clearInterval(postTimer.value)
}
}, 600)
}
@@ -329,15 +340,16 @@ watch(() => stage.value, (newStage) => {
// 硬件初始化动画
watch(() => stage.value, (newStage) => {
if (hwTimer.value) clearInterval(hwTimer.value)
if (newStage === 2) {
activeHw.value = 0
hwProgress.value = 0
const interval = setInterval(() => {
hwTimer.value = setInterval(() => {
if (hwProgress.value < 100) {
hwProgress.value += 5
activeHw.value = Math.floor(hwProgress.value / 20) % hardwareItems.length
} else {
clearInterval(interval)
if (hwTimer.value) clearInterval(hwTimer.value)
}
}, 100)
}
@@ -345,11 +357,12 @@ watch(() => stage.value, (newStage) => {
// 启动设备搜索动画
watch(() => stage.value, (newStage) => {
if (bootTimer.value) clearInterval(bootTimer.value)
if (newStage === 3) {
currentDevice.value = 0
foundDevice.value = -1
let device = 0
const interval = setInterval(() => {
bootTimer.value = setInterval(() => {
if (device < bootDevices.length) {
currentDevice.value = device
// 假设第一个设备(硬盘)可启动
@@ -357,11 +370,11 @@ watch(() => stage.value, (newStage) => {
setTimeout(() => {
foundDevice.value = device
}, 400)
clearInterval(interval)
if (bootTimer.value) clearInterval(bootTimer.value)
}
device++
} else {
clearInterval(interval)
if (bootTimer.value) clearInterval(bootTimer.value)
}
}, 800)
}
@@ -387,6 +400,9 @@ function reset() {
hwProgress.value = 0
currentDevice.value = 0
foundDevice.value = -1
if (postTimer.value) clearInterval(postTimer.value)
if (hwTimer.value) clearInterval(hwTimer.value)
if (bootTimer.value) clearInterval(bootTimer.value)
}
</script>
@@ -47,12 +47,16 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, onUnmounted } from 'vue'
const selected = ref(0)
const visibleSteps = ref(0)
let timer = null
onUnmounted(() => {
if (timer) clearInterval(timer)
})
function selectMode(i) {
selected.value = i
visibleSteps.value = 0
@@ -451,7 +451,10 @@ function toggleAuto() {
} else {
autoRunning.value = true
autoTimer = setInterval(() => {
if (done.value) { stopAuto(); return }
if (done.value) {
stopAuto()
return
}
advance()
}, 900)
}
@@ -459,7 +462,10 @@ function toggleAuto() {
function stopAuto() {
autoRunning.value = false
if (autoTimer) { clearInterval(autoTimer); autoTimer = null }
if (autoTimer) {
clearInterval(autoTimer)
autoTimer = null
}
}
function reset() {
@@ -476,7 +482,9 @@ function reset() {
currentPhase.value = -1
}
onUnmounted(stopAuto)
onUnmounted(() => {
stopAuto()
})
</script>
<style scoped>
@@ -61,12 +61,17 @@ const updateClock = () => {
}
const runSequence = () => {
if (phaseTimer) clearTimeout(phaseTimer)
phase.value = 0
const delays = [1500, 1500, 1800]
let i = 0
const next = () => {
if (i < delays.length) {
phaseTimer = setTimeout(() => { phase.value = i + 1; i++; next() }, delays[i])
phaseTimer = setTimeout(() => {
phase.value = i + 1
i++
next()
}, delays[i])
}
}
next()
@@ -77,6 +82,7 @@ onMounted(() => {
timer = setInterval(updateClock, 30000)
runSequence()
})
onUnmounted(() => {
if (timer) clearInterval(timer)
if (phaseTimer) clearTimeout(phaseTimer)
@@ -165,7 +165,7 @@ onMounted(() => {
})
onUnmounted(() => {
clearInterval(timer)
if (timer) clearInterval(timer)
})
</script>
@@ -145,7 +145,7 @@ onMounted(() => {
})
onUnmounted(() => {
clearInterval(timer)
if (timer) clearInterval(timer)
})
</script>
@@ -90,7 +90,7 @@ onMounted(() => {
})
onUnmounted(() => {
clearInterval(timer)
if (timer) clearInterval(timer)
})
</script>
@@ -418,23 +418,31 @@ onMounted(() => {
}, 1000)
})
const blTimer = ref(null)
const kernelTimer = ref(null)
const svcTimer = ref(null)
onUnmounted(() => {
if (timeInterval) clearInterval(timeInterval)
if (blTimer.value) clearInterval(blTimer.value)
if (kernelTimer.value) clearInterval(kernelTimer.value)
if (svcTimer.value) clearInterval(svcTimer.value)
})
//
watch(() => stage.value, (newStage) => {
if (blTimer.value) clearInterval(blTimer.value)
if (newStage === 1) {
blStep.value = -1
blCodeLine.value = -1
let step = 0
const interval = setInterval(() => {
blTimer.value = setInterval(() => {
if (step < blSteps.length) {
blStep.value = step
blCodeLine.value = step + 1
step++
} else {
clearInterval(interval)
if (blTimer.value) clearInterval(blTimer.value)
}
}, 600)
}
@@ -442,14 +450,15 @@ watch(() => stage.value, (newStage) => {
//
watch(() => stage.value, (newStage) => {
if (kernelTimer.value) clearInterval(kernelTimer.value)
if (newStage === 2) {
kernelProgress.value = 0
kernelName.value = Math.random() > 0.5 ? 'ntoskrnl.exe' : 'vmlinuz'
const interval = setInterval(() => {
kernelTimer.value = setInterval(() => {
if (kernelProgress.value < 100) {
kernelProgress.value += 4
} else {
clearInterval(interval)
if (kernelTimer.value) clearInterval(kernelTimer.value)
}
}, 80)
}
@@ -457,13 +466,14 @@ watch(() => stage.value, (newStage) => {
//
watch(() => stage.value, (newStage) => {
if (svcTimer.value) clearInterval(svcTimer.value)
if (newStage === 3) {
svcProgress.value = 0
const interval = setInterval(() => {
svcTimer.value = setInterval(() => {
if (svcProgress.value < 100) {
svcProgress.value += 3
} else {
clearInterval(interval)
if (svcTimer.value) clearInterval(svcTimer.value)
}
}, 100)
}
@@ -99,8 +99,8 @@ onMounted(() => {
})
onUnmounted(() => {
clearInterval(timer)
clearTimeout(switchTimer)
if (timer) clearInterval(timer)
if (switchTimer) clearTimeout(switchTimer)
})
const currentTask = computed(() => processes.value[currentIdx.value])
@@ -98,7 +98,7 @@ onMounted(() => {
})
onUnmounted(() => {
clearInterval(timer)
if (timer) clearInterval(timer)
})
</script>
@@ -123,13 +123,14 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, onUnmounted } from 'vue'
const activeAlgo = ref('linear')
const targetNumber = ref(7)
const foundIndex = ref(-1)
const searchStep = ref(-1)
const searching = ref(false)
const searchTimer = ref(null)
const numbers = ref([3, 7, 2, 9, 5, 1, 8, 4, 6, 10])
@@ -140,24 +141,29 @@ const binaryRight = ref(9)
const binaryMid = ref(4)
const binaryFoundIndex = ref(-1)
onUnmounted(() => {
if (searchTimer.value) clearInterval(searchTimer.value)
})
const startLinearSearch = () => {
if (searchTimer.value) clearInterval(searchTimer.value)
searching.value = true
searchStep.value = -1
foundIndex.value = -1
let step = 0
const interval = setInterval(() => {
searchTimer.value = setInterval(() => {
if (step < numbers.value.length) {
searchStep.value = step
if (numbers.value[step] === targetNumber.value) {
foundIndex.value = step
searching.value = false
clearInterval(interval)
if (searchTimer.value) clearInterval(searchTimer.value)
}
step++
} else {
searching.value = false
clearInterval(interval)
if (searchTimer.value) clearInterval(searchTimer.value)
}
}, 500)
}
@@ -123,24 +123,30 @@
</template>
<script setup>
import { ref, computed } from 'vue'
import { ref, onUnmounted } from 'vue'
const activeType = ref('serial')
const dataBits = ref(['1', '0', '1', '1', '0', '0', '1', '0'])
const dataBits = ref([1, 0, 1, 1, 0, 0, 1, 0])
const receivedBits = ref(['-', '-', '-', '-', '-', '-', '-', '-'])
const sendingBit = ref(null)
const timer = ref(null)
onUnmounted(() => {
if (timer.value) clearInterval(timer.value)
})
const startTransmission = () => {
if (timer.value) clearInterval(timer.value)
if (activeType.value === 'serial') {
receivedBits.value = ['-', '-', '-', '-', '-', '-', '-', '-']
let i = 0
const interval = setInterval(() => {
timer.value = setInterval(() => {
if (i < dataBits.value.length) {
sendingBit.value = i
receivedBits.value[i] = dataBits.value[i]
i++
} else {
clearInterval(interval)
if (timer.value) clearInterval(timer.value)
sendingBit.value = null
}
}, 300)
@@ -61,6 +61,7 @@ const steps = [
]
const autoPlay = () => {
if (timer) clearInterval(timer)
current.value = -1
playing.value = true
let i = 0
@@ -68,7 +69,7 @@ const autoPlay = () => {
current.value = i
i++
if (i >= steps.length) {
clearInterval(timer)
if (timer) clearInterval(timer)
playing.value = false
}
}, 800)
@@ -0,0 +1,130 @@
<!--
DataGovernanceFrameworkDemo.vue
数据治理框架演示展示数据治理的核心流程
-->
<template>
<div class="governance-demo">
<div class="header">
<div class="title">数据治理框架</div>
<div class="subtitle">点击各阶段查看详情</div>
</div>
<div class="pipeline">
<div
v-for="(stage, i) in stages"
:key="stage.key"
:class="['stage', { active: activeStage === stage.key }]"
@click="activeStage = stage.key"
>
<div class="stage-num">{{ i + 1 }}</div>
<div class="stage-name">{{ stage.name }}</div>
<div v-if="i < stages.length - 1" class="arrow"></div>
</div>
</div>
<div v-if="current" class="stage-detail">
<div class="detail-title">{{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="activities">
<div v-for="(act, i) in current.activities" :key="i" class="activity">
<span class="act-icon">{{ act.icon }}</span>
<div>
<div class="act-name">{{ act.name }}</div>
<div class="act-desc">{{ act.desc }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeStage = ref('define')
const stages = [
{
key: 'define',
name: '定义标准',
desc: '制定数据标准、命名规范、数据字典',
activities: [
{ icon: '📖', name: '数据字典', desc: '定义每个字段的含义、类型、取值范围' },
{ icon: '📏', name: '命名规范', desc: '统一字段命名:snake_case、驼峰、前缀约定' },
{ icon: '🏷️', name: '分类分级', desc: '按敏感度分级:公开、内部、机密、绝密' }
]
},
{
key: 'collect',
name: '采集接入',
desc: '规范数据采集流程,确保源头质量',
activities: [
{ icon: '🔌', name: '接入规范', desc: '定义数据接入的格式、协议、频率要求' },
{ icon: '✅', name: '入库校验', desc: '数据写入前进行格式、完整性、合规性校验' },
{ icon: '📝', name: '血缘记录', desc: '记录数据来源、加工链路、依赖关系' }
]
},
{
key: 'store',
name: '存储管理',
desc: '合理存储数据,控制成本和访问权限',
activities: [
{ icon: '🗄️', name: '分层存储', desc: 'ODS → DWD → DWS → ADS 数仓分层' },
{ icon: '🔒', name: '权限控制', desc: '按角色和数据分级控制读写权限' },
{ icon: '♻️', name: '生命周期', desc: '热数据 → 温数据 → 冷数据 → 归档/删除' }
]
},
{
key: 'use',
name: '使用消费',
desc: '让数据安全、高效地被业务使用',
activities: [
{ icon: '🔍', name: '数据目录', desc: '提供可搜索的数据资产目录,降低找数成本' },
{ icon: '🎭', name: '脱敏处理', desc: '对敏感字段进行掩码、加密、泛化处理' },
{ icon: '📊', name: '质量监控', desc: '持续监控数据质量指标,异常时告警' }
]
},
{
key: 'retire',
name: '归档销毁',
desc: '按合规要求归档或安全销毁数据',
activities: [
{ icon: '📦', name: '归档策略', desc: '超过保留期的数据迁移到低成本存储' },
{ icon: '🗑️', name: '安全删除', desc: '按 GDPR/个保法要求彻底删除用户数据' },
{ icon: '📋', name: '审计日志', desc: '记录数据删除操作,满足合规审计要求' }
]
}
]
const current = computed(() => stages.find(s => s.key === activeStage.value))
</script>
<style scoped>
.governance-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.pipeline { display: flex; align-items: center; gap: 0.25rem; margin-bottom: 1rem; flex-wrap: wrap; }
.stage {
display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem;
border-radius: 8px; cursor: pointer; background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider); transition: all 0.2s; font-size: 0.85rem;
}
.stage:hover { border-color: var(--vp-c-brand); }
.stage.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); }
.stage-num { width: 20px; height: 20px; border-radius: 50%; background: var(--vp-c-brand); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; font-weight: 700; }
.stage-name { font-weight: 600; }
.arrow { color: var(--vp-c-text-3); margin-left: 0.25rem; }
.stage-detail { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.detail-title { font-weight: 700; font-size: 1rem; margin-bottom: 0.25rem; }
.detail-desc { color: var(--vp-c-text-2); font-size: 0.85rem; margin-bottom: 0.75rem; }
.activities { display: flex; flex-direction: column; gap: 0.5rem; }
.activity { display: flex; gap: 0.5rem; padding: 0.5rem; border-radius: 6px; background: var(--vp-c-bg-soft); }
.act-icon { font-size: 1.2rem; }
.act-name { font-weight: 600; font-size: 0.85rem; }
.act-desc { font-size: 0.78rem; color: var(--vp-c-text-2); }
@media (max-width: 640px) { .pipeline { flex-direction: column; align-items: stretch; } .arrow { display: none; } }
</style>
@@ -0,0 +1,138 @@
<!--
DataLineageDemo.vue
数据血缘追踪演示展示数据从源头到消费的流转路径
-->
<template>
<div class="lineage-demo">
<div class="header">
<div class="title">数据血缘追踪</div>
<div class="subtitle">点击任意节点查看上下游依赖关系</div>
</div>
<div class="lineage-graph">
<div v-for="(layer, li) in layers" :key="li" class="layer">
<div class="layer-label">{{ layer.label }}</div>
<div class="layer-nodes">
<div
v-for="node in layer.nodes"
:key="node.id"
:class="['node', { active: activeNode === node.id, upstream: upstreamIds.includes(node.id), downstream: downstreamIds.includes(node.id) }]"
@click="selectNode(node.id)"
>
<div class="node-icon">{{ node.icon }}</div>
<div class="node-name">{{ node.name }}</div>
</div>
</div>
</div>
</div>
<div v-if="activeNode && activeInfo" class="info-panel">
<div class="info-title">{{ activeInfo.name }}</div>
<div class="info-row"><span class="info-label">上游依赖</span>{{ activeInfo.upstreamNames || '无(数据源头)' }}</div>
<div class="info-row"><span class="info-label">下游消费</span>{{ activeInfo.downstreamNames || '无(最终消费)' }}</div>
<div class="info-row"><span class="info-label">负责人</span>{{ activeInfo.owner }}</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeNode = ref(null)
const nodes = {
mysql_user: { name: 'MySQL 用户表', icon: '🗄️', upstream: [], downstream: ['ods_user'], owner: '业务开发组' },
mysql_order: { name: 'MySQL 订单表', icon: '🗄️', upstream: [], downstream: ['ods_order'], owner: '业务开发组' },
log_click: { name: '点击日志', icon: '📝', upstream: [], downstream: ['ods_click'], owner: '前端团队' },
ods_user: { name: 'ODS 用户', icon: '📥', upstream: ['mysql_user'], downstream: ['dwd_user'], owner: '数据工程师' },
ods_order: { name: 'ODS 订单', icon: '📥', upstream: ['mysql_order'], downstream: ['dwd_order'], owner: '数据工程师' },
ods_click: { name: 'ODS 点击', icon: '📥', upstream: ['log_click'], downstream: ['dwd_click'], owner: '数据工程师' },
dwd_user: { name: 'DWD 用户明细', icon: '🔧', upstream: ['ods_user'], downstream: ['dws_user_profile'], owner: '数据开发' },
dwd_order: { name: 'DWD 订单明细', icon: '🔧', upstream: ['ods_order'], downstream: ['dws_gmv'], owner: '数据开发' },
dwd_click: { name: 'DWD 点击明细', icon: '🔧', upstream: ['ods_click'], downstream: ['dws_user_profile'], owner: '数据开发' },
dws_user_profile: { name: 'DWS 用户画像', icon: '📊', upstream: ['dwd_user', 'dwd_click'], downstream: ['ads_report'], owner: '数据分析师' },
dws_gmv: { name: 'DWS GMV 汇总', icon: '📊', upstream: ['dwd_order'], downstream: ['ads_report'], owner: '数据分析师' },
ads_report: { name: 'ADS 经营报表', icon: '📈', upstream: ['dws_user_profile', 'dws_gmv'], downstream: [], owner: '数据产品' }
}
const layers = [
{ label: '数据源', nodes: [{ id: 'mysql_user', ...nodes.mysql_user }, { id: 'mysql_order', ...nodes.mysql_order }, { id: 'log_click', ...nodes.log_click }] },
{ label: 'ODS 层', nodes: [{ id: 'ods_user', ...nodes.ods_user }, { id: 'ods_order', ...nodes.ods_order }, { id: 'ods_click', ...nodes.ods_click }] },
{ label: 'DWD 层', nodes: [{ id: 'dwd_user', ...nodes.dwd_user }, { id: 'dwd_order', ...nodes.dwd_order }, { id: 'dwd_click', ...nodes.dwd_click }] },
{ label: 'DWS 层', nodes: [{ id: 'dws_user_profile', ...nodes.dws_user_profile }, { id: 'dws_gmv', ...nodes.dws_gmv }] },
{ label: 'ADS 层', nodes: [{ id: 'ads_report', ...nodes.ads_report }] }
]
function getAllUpstream(id, visited = new Set()) {
if (visited.has(id)) return []
visited.add(id)
const node = nodes[id]
if (!node) return []
let result = [...node.upstream]
node.upstream.forEach(uid => { result = result.concat(getAllUpstream(uid, visited)) })
return result
}
function getAllDownstream(id, visited = new Set()) {
if (visited.has(id)) return []
visited.add(id)
const node = nodes[id]
if (!node) return []
let result = [...node.downstream]
node.downstream.forEach(did => { result = result.concat(getAllDownstream(did, visited)) })
return result
}
const upstreamIds = computed(() => activeNode.value ? getAllUpstream(activeNode.value) : [])
const downstreamIds = computed(() => activeNode.value ? getAllDownstream(activeNode.value) : [])
const activeInfo = computed(() => {
if (!activeNode.value || !nodes[activeNode.value]) return null
const n = nodes[activeNode.value]
return {
...n,
upstreamNames: n.upstream.map(id => nodes[id]?.name).join('、'),
downstreamNames: n.downstream.map(id => nodes[id]?.name).join('、')
}
})
function selectNode(id) {
activeNode.value = activeNode.value === id ? null : id
}
</script>
<style scoped>
.lineage-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.lineage-graph { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
.layer { display: flex; align-items: center; gap: 0.75rem; }
.layer-label {
min-width: 60px; font-size: 0.75rem; font-weight: 700;
color: var(--vp-c-text-3); text-align: right;
}
.layer-nodes { display: flex; gap: 0.5rem; flex-wrap: wrap; flex: 1; }
.node {
display: flex; align-items: center; gap: 0.3rem; padding: 0.4rem 0.6rem;
border-radius: 6px; cursor: pointer; font-size: 0.78rem;
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider); transition: all 0.2s;
}
.node:hover { border-color: var(--vp-c-brand); }
.node.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.1); font-weight: 700; }
.node.upstream { border-color: #f59e0b; background: rgba(245,158,11,0.08); }
.node.downstream { border-color: #22c55e; background: rgba(34,197,94,0.08); }
.node-icon { font-size: 1rem; }
.node-name { white-space: nowrap; }
.info-panel {
background: var(--vp-c-bg); border-radius: 8px; padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
.info-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.5rem; }
.info-row { font-size: 0.82rem; margin-bottom: 0.25rem; }
.info-label { font-weight: 600; color: var(--vp-c-text-2); }
@media (max-width: 640px) { .layer { flex-direction: column; align-items: flex-start; } .layer-label { text-align: left; } }
</style>
@@ -0,0 +1,266 @@
<!--
DataQualityDemo.vue
数据质量维度演示展示数据质量的六个核心维度
-->
<template>
<div class="data-quality-demo">
<div class="header">
<div class="title">数据质量检测器</div>
<div class="subtitle">点击不同维度查看数据质量问题示例</div>
</div>
<div class="dimensions">
<div
v-for="dim in dimensions"
:key="dim.key"
:class="['dim-card', { active: activeDim === dim.key }]"
@click="activeDim = dim.key"
>
<div class="dim-icon">{{ dim.icon }}</div>
<div class="dim-name">{{ dim.name }}</div>
</div>
</div>
<div v-if="currentDim" class="detail-panel">
<div class="detail-header">
<span class="detail-icon">{{ currentDim.icon }}</span>
<span class="detail-title">{{ currentDim.name }}</span>
<span class="detail-desc">{{ currentDim.desc }}</span>
</div>
<div class="example-section">
<div class="example bad">
<div class="example-label bad-label">问题数据</div>
<table class="data-table">
<thead>
<tr>
<th v-for="col in currentDim.badData.cols" :key="col">{{ col }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in currentDim.badData.rows" :key="i">
<td
v-for="(cell, j) in row"
:key="j"
:class="{ 'cell-error': cell.error }"
>{{ cell.value }}</td>
</tr>
</tbody>
</table>
</div>
<div class="example good">
<div class="example-label good-label">治理后</div>
<table class="data-table">
<thead>
<tr>
<th v-for="col in currentDim.goodData.cols" :key="col">{{ col }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in currentDim.goodData.rows" :key="i">
<td v-for="(cell, j) in row" :key="j">{{ cell.value }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="quality-score">
<div class="score-label">质量评分</div>
<div class="score-bar-bg">
<div
class="score-bar-fill"
:style="{ width: currentDim.score + '%', background: scoreColor(currentDim.score) }"
></div>
</div>
<div class="score-value">{{ currentDim.score }}%</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeDim = ref('completeness')
const dimensions = [
{
key: 'completeness', name: '完整性', icon: '📋',
desc: '数据是否存在缺失值',
score: 72,
badData: {
cols: ['用户ID', '姓名', '邮箱', '手机号'],
rows: [
[{ value: '001' }, { value: '张三' }, { value: 'zhang@mail.com' }, { value: '138xxxx1234' }],
[{ value: '002' }, { value: '李四' }, { value: '', error: true }, { value: '', error: true }],
[{ value: '003' }, { value: '', error: true }, { value: 'wang@mail.com' }, { value: '139xxxx5678' }]
]
},
goodData: {
cols: ['用户ID', '姓名', '邮箱', '手机号'],
rows: [
[{ value: '001' }, { value: '张三' }, { value: 'zhang@mail.com' }, { value: '138xxxx1234' }],
[{ value: '002' }, { value: '李四' }, { value: 'li@mail.com' }, { value: '137xxxx9012' }],
[{ value: '003' }, { value: '王五' }, { value: 'wang@mail.com' }, { value: '139xxxx5678' }]
]
}
},
{
key: 'accuracy', name: '准确性', icon: '🎯',
desc: '数据值是否正确反映真实情况',
score: 65,
badData: {
cols: ['订单ID', '金额', '日期', '状态'],
rows: [
[{ value: 'ORD-101' }, { value: '-50.00', error: true }, { value: '2025-01-15' }, { value: '已完成' }],
[{ value: 'ORD-102' }, { value: '299.00' }, { value: '2025-13-01', error: true }, { value: '已发货' }],
[{ value: 'ORD-103' }, { value: '1500.00' }, { value: '2025-02-28' }, { value: '已退款', error: true }]
]
},
goodData: {
cols: ['订单ID', '金额', '日期', '状态'],
rows: [
[{ value: 'ORD-101' }, { value: '50.00' }, { value: '2025-01-15' }, { value: '已完成' }],
[{ value: 'ORD-102' }, { value: '299.00' }, { value: '2025-01-13' }, { value: '已发货' }],
[{ value: 'ORD-103' }, { value: '1500.00' }, { value: '2025-02-28' }, { value: '已完成' }]
]
}
},
{
key: 'consistency', name: '一致性', icon: '🔗',
desc: '同一数据在不同系统中是否一致',
score: 58,
badData: {
cols: ['来源', '用户名', '手机号', '地址'],
rows: [
[{ value: 'CRM' }, { value: '张三' }, { value: '13812341234' }, { value: '北京市朝阳区' }],
[{ value: '订单系统' }, { value: '张三丰', error: true }, { value: '13812341234' }, { value: '北京朝阳', error: true }],
[{ value: '客服系统' }, { value: '张三' }, { value: '13899999999', error: true }, { value: '北京市朝阳区' }]
]
},
goodData: {
cols: ['来源', '用户名', '手机号', '地址'],
rows: [
[{ value: 'CRM' }, { value: '张三' }, { value: '13812341234' }, { value: '北京市朝阳区' }],
[{ value: '订单系统' }, { value: '张三' }, { value: '13812341234' }, { value: '北京市朝阳区' }],
[{ value: '客服系统' }, { value: '张三' }, { value: '13812341234' }, { value: '北京市朝阳区' }]
]
}
},
{
key: 'timeliness', name: '时效性', icon: '⏰',
desc: '数据是否及时更新',
score: 80,
badData: {
cols: ['商品ID', '价格', '库存', '更新时间'],
rows: [
[{ value: 'SKU-001' }, { value: '¥299' }, { value: '50' }, { value: '2024-06-01', error: true }],
[{ value: 'SKU-002' }, { value: '¥599' }, { value: '0', error: true }, { value: '2024-03-15', error: true }],
[{ value: 'SKU-003' }, { value: '¥199' }, { value: '200' }, { value: '2025-02-20' }]
]
},
goodData: {
cols: ['商品ID', '价格', '库存', '更新时间'],
rows: [
[{ value: 'SKU-001' }, { value: '¥259' }, { value: '35' }, { value: '2025-02-25' }],
[{ value: 'SKU-002' }, { value: '¥549' }, { value: '12' }, { value: '2025-02-25' }],
[{ value: 'SKU-003' }, { value: '¥199' }, { value: '180' }, { value: '2025-02-25' }]
]
}
},
{
key: 'uniqueness', name: '唯一性', icon: '🔑',
desc: '数据是否存在重复记录',
score: 70,
badData: {
cols: ['用户ID', '姓名', '邮箱', '注册时间'],
rows: [
[{ value: '001' }, { value: '张三' }, { value: 'zhang@mail.com' }, { value: '2025-01-01' }],
[{ value: '005' }, { value: '张三', error: true }, { value: 'zhang@mail.com', error: true }, { value: '2025-01-15', error: true }],
[{ value: '002' }, { value: '李四' }, { value: 'li@mail.com' }, { value: '2025-01-10' }]
]
},
goodData: {
cols: ['用户ID', '姓名', '邮箱', '注册时间'],
rows: [
[{ value: '001' }, { value: '张三' }, { value: 'zhang@mail.com' }, { value: '2025-01-01' }],
[{ value: '002' }, { value: '李四' }, { value: 'li@mail.com' }, { value: '2025-01-10' }]
]
}
},
{
key: 'validity', name: '有效性', icon: '✅',
desc: '数据是否符合预定义的格式和规则',
score: 75,
badData: {
cols: ['字段', '值', '规则'],
rows: [
[{ value: '邮箱' }, { value: 'not-an-email', error: true }, { value: '需包含@' }],
[{ value: '年龄' }, { value: '-5', error: true }, { value: '0~150' }],
[{ value: '手机号' }, { value: '1234', error: true }, { value: '11位数字' }]
]
},
goodData: {
cols: ['字段', '值', '规则'],
rows: [
[{ value: '邮箱' }, { value: 'user@mail.com' }, { value: '需包含@' }],
[{ value: '年龄' }, { value: '28' }, { value: '0~150' }],
[{ value: '手机号' }, { value: '13812345678' }, { value: '11位数字' }]
]
}
}
]
const currentDim = computed(() => dimensions.find(d => d.key === activeDim.value))
function scoreColor(score) {
if (score >= 80) return '#22c55e'
if (score >= 60) return '#f59e0b'
return '#ef4444'
}
</script>
<style scoped>
.data-quality-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.dimensions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1rem; }
.dim-card {
display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem;
border-radius: 8px; background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
cursor: pointer; font-size: 0.85rem; transition: all 0.2s;
}
.dim-card:hover { border-color: var(--vp-c-brand); }
.dim-card.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); }
.dim-icon { font-size: 1.1rem; }
.dim-name { font-weight: 600; }
.detail-panel {
padding: 1rem; border-radius: 8px; background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider); margin-bottom: 1rem;
}
.detail-header { margin-bottom: 0.75rem; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.detail-icon { font-size: 1.2rem; }
.detail-title { font-weight: 700; font-size: 1rem; }
.detail-desc { color: var(--vp-c-text-2); font-size: 0.85rem; }
.example-section { display: flex; gap: 1rem; margin-bottom: 1rem; }
.example { flex: 1; }
.example-label { font-weight: 600; font-size: 0.8rem; margin-bottom: 0.4rem; padding: 0.2rem 0.5rem; border-radius: 4px; display: inline-block; }
.bad-label { background: rgba(239,68,68,0.1); color: #ef4444; }
.good-label { background: rgba(34,197,94,0.1); color: #22c55e; }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.75rem; }
.data-table th { background: var(--vp-c-bg-soft); padding: 0.3rem 0.4rem; text-align: left; font-weight: 600; border-bottom: 1px solid var(--vp-c-divider); }
.data-table td { padding: 0.3rem 0.4rem; border-bottom: 1px solid var(--vp-c-divider); }
.cell-error { background: rgba(239,68,68,0.1); color: #ef4444; font-weight: 600; }
.quality-score { display: flex; align-items: center; gap: 0.75rem; }
.score-label { font-weight: 600; font-size: 0.85rem; white-space: nowrap; }
.score-bar-bg { flex: 1; height: 10px; background: var(--vp-c-bg-soft); border-radius: 5px; overflow: hidden; }
.score-bar-fill { height: 100%; border-radius: 5px; transition: width 0.4s; }
.score-value { font-weight: 700; font-size: 0.9rem; font-family: var(--vp-font-family-mono); min-width: 40px; }
@media (max-width: 640px) { .example-section { flex-direction: column; } }
</style>
@@ -0,0 +1,131 @@
<!--
ChartTypeSelectorDemo.vue
图表类型选择器根据数据特征推荐合适的图表类型
-->
<template>
<div class="chart-selector-demo">
<div class="header">
<div class="title">图表类型选择器</div>
<div class="subtitle">选择你的数据目的查看推荐的图表类型</div>
</div>
<div class="purposes">
<div
v-for="p in purposes"
:key="p.key"
:class="['purpose-card', { active: activePurpose === p.key }]"
@click="activePurpose = p.key"
>
<div class="purpose-icon">{{ p.icon }}</div>
<div class="purpose-name">{{ p.name }}</div>
</div>
</div>
<div v-if="currentPurpose" class="charts-panel">
<div class="panel-title">{{ currentPurpose.name }}推荐图表</div>
<div class="chart-list">
<div
v-for="chart in currentPurpose.charts"
:key="chart.name"
class="chart-item"
>
<div class="chart-visual">{{ chart.visual }}</div>
<div class="chart-info">
<div class="chart-name">{{ chart.name }}</div>
<div class="chart-desc">{{ chart.desc }}</div>
<div class="chart-example">示例{{ chart.example }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activePurpose = ref('comparison')
const purposes = [
{
key: 'comparison',
name: '比较',
icon: '📊',
charts: [
{ name: '柱状图', visual: '▐▐▐', desc: '比较不同类别的数值大小', example: '各部门销售额对比' },
{ name: '分组柱状图', visual: '▐▐ ▐▐', desc: '多维度分组比较', example: '各季度各产品线收入' },
{ name: '雷达图', visual: '◇', desc: '多维度综合对比', example: '候选人能力评估' }
]
},
{
key: 'trend',
name: '趋势',
icon: '📈',
charts: [
{ name: '折线图', visual: '╱╲╱', desc: '展示数据随时间的变化趋势', example: '月度用户增长曲线' },
{ name: '面积图', visual: '▓▓▓', desc: '强调趋势下的累积量', example: '各渠道流量占比变化' },
{ name: '阶梯图', visual: '┐└┐', desc: '展示离散时间点的变化', example: '价格调整历史' }
]
},
{
key: 'proportion',
name: '占比',
icon: '🍩',
charts: [
{ name: '饼图', visual: '◔', desc: '展示各部分占整体的比例', example: '市场份额分布' },
{ name: '环形图', visual: '◎', desc: '饼图的变体,中间可放数字', example: '预算使用率' },
{ name: '堆叠柱状图', visual: '▐▐▐', desc: '展示各部分的组成和总量', example: '各地区各品类销售构成' }
]
},
{
key: 'distribution',
name: '分布',
icon: '🔔',
charts: [
{ name: '直方图', visual: '▁▃▇▃▁', desc: '展示数据的频率分布', example: '用户年龄分布' },
{ name: '散点图', visual: '· ·· ·', desc: '展示两个变量的关系', example: '广告投入 vs 销售额' },
{ name: '箱线图', visual: '├─┤', desc: '展示数据的中位数、四分位数和异常值', example: '各城市房价分布' }
]
},
{
key: 'relation',
name: '关系',
icon: '🕸️',
charts: [
{ name: '桑基图', visual: '≋≋≋', desc: '展示流量或能量的流向', example: '用户转化漏斗' },
{ name: '网络图', visual: '⊙─⊙', desc: '展示节点之间的关联关系', example: '社交关系网络' },
{ name: '热力图', visual: '▓▒░', desc: '用颜色深浅表示数值大小', example: '各时段各页面访问量' }
]
}
]
const currentPurpose = computed(() => purposes.find(p => p.key === activePurpose.value))
</script>
<style scoped>
.chart-selector-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.purposes { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 0.5rem; margin-bottom: 1rem; }
.purpose-card {
display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
padding: 0.6rem; border-radius: 8px; cursor: pointer;
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider); transition: all 0.2s;
}
.purpose-card:hover { border-color: var(--vp-c-brand); }
.purpose-card.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); }
.purpose-icon { font-size: 1.3rem; }
.purpose-name { font-size: 0.8rem; font-weight: 600; }
.panel-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.75rem; }
.charts-panel { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.chart-list { display: flex; flex-direction: column; gap: 0.5rem; }
.chart-item { display: flex; gap: 0.75rem; padding: 0.5rem; border-radius: 6px; background: var(--vp-c-bg-soft); }
.chart-visual { font-size: 1.2rem; min-width: 50px; display: flex; align-items: center; justify-content: center; font-family: var(--vp-font-family-mono); color: var(--vp-c-brand); }
.chart-name { font-weight: 600; font-size: 0.85rem; }
.chart-desc { font-size: 0.78rem; color: var(--vp-c-text-2); }
.chart-example { font-size: 0.75rem; color: var(--vp-c-text-3); font-style: italic; }
</style>
@@ -0,0 +1,134 @@
<!--
DashboardLayoutDemo.vue
仪表盘布局演示展示仪表盘的常见布局模式
-->
<template>
<div class="dashboard-demo">
<div class="header">
<div class="title">仪表盘布局模式</div>
<div class="subtitle">点击查看不同类型的仪表盘布局</div>
</div>
<div class="layout-tabs">
<div
v-for="layout in layouts"
:key="layout.key"
:class="['tab', { active: activeLayout === layout.key }]"
@click="activeLayout = layout.key"
>
{{ layout.name }}
</div>
</div>
<div v-if="current" class="layout-preview">
<div class="preview-title">{{ current.name }}</div>
<div class="preview-desc">{{ current.desc }}</div>
<div :class="['mock-dashboard', current.key]">
<div
v-for="(widget, i) in current.widgets"
:key="i"
:class="['widget', widget.type]"
>
<div class="widget-label">{{ widget.label }}</div>
</div>
</div>
<div class="use-case">适用场景{{ current.useCase }}</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeLayout = ref('overview')
const layouts = [
{
key: 'overview',
name: '全局概览型',
desc: '顶部核心指标卡片 + 中间趋势图 + 底部明细表',
useCase: '管理层日报、运营大盘',
widgets: [
{ type: 'kpi', label: 'DAU 12.5万' },
{ type: 'kpi', label: '收入 ¥85万' },
{ type: 'kpi', label: '转化率 3.2%' },
{ type: 'kpi', label: '客单价 ¥268' },
{ type: 'chart-wide', label: '趋势折线图' },
{ type: 'table', label: '明细数据表' }
]
},
{
key: 'comparison',
name: '对比分析型',
desc: '左右对比布局,适合 A/B 测试或同环比分析',
useCase: 'A/B 测试报告、竞品分析',
widgets: [
{ type: 'half', label: '实验组指标' },
{ type: 'half', label: '对照组指标' },
{ type: 'chart-wide', label: '差异对比图' },
{ type: 'table', label: '统计显著性检验' }
]
},
{
key: 'drill',
name: '下钻分析型',
desc: '从汇总到明细逐层下钻,支持交互式探索',
useCase: '销售分析、用户行为分析',
widgets: [
{ type: 'chart-wide', label: '全国销售地图(点击省份下钻)' },
{ type: 'half', label: '省份排名柱状图' },
{ type: 'half', label: '城市明细饼图' },
{ type: 'table', label: '门店级明细表' }
]
},
{
key: 'realtime',
name: '实时监控型',
desc: '大屏展示,数据自动刷新,适合投屏',
useCase: '双十一大屏、服务器监控',
widgets: [
{ type: 'big-number', label: '实时 GMV ¥1.2亿' },
{ type: 'half', label: '订单量实时曲线' },
{ type: 'half', label: '地域热力图' },
{ type: 'kpi', label: '支付成功率' },
{ type: 'kpi', label: '平均响应时间' },
{ type: 'kpi', label: '在线用户数' }
]
}
]
const current = computed(() => layouts.find(l => l.key === activeLayout.value))
</script>
<style scoped>
.dashboard-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.layout-tabs { display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; }
.tab {
padding: 0.4rem 0.75rem; border-radius: 6px; cursor: pointer;
font-size: 0.85rem; background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.tab:hover { border-color: var(--vp-c-brand); }
.tab.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); font-weight: 600; }
.layout-preview { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.preview-title { font-weight: 700; font-size: 0.95rem; }
.preview-desc { color: var(--vp-c-text-2); font-size: 0.82rem; margin-bottom: 0.75rem; }
.mock-dashboard { display: grid; gap: 0.4rem; margin-bottom: 0.75rem; grid-template-columns: repeat(4, 1fr); }
.widget {
padding: 0.5rem; border-radius: 6px; text-align: center;
font-size: 0.75rem; font-weight: 600; border: 1px dashed var(--vp-c-divider);
}
.widget.kpi { background: rgba(var(--vp-c-brand-rgb), 0.06); grid-column: span 1; }
.widget.chart-wide { background: rgba(34,197,94,0.06); grid-column: span 4; min-height: 50px; }
.widget.table { background: rgba(245,158,11,0.06); grid-column: span 4; }
.widget.half { background: rgba(99,102,241,0.06); grid-column: span 2; min-height: 40px; }
.widget.big-number { background: rgba(239,68,68,0.06); grid-column: span 4; min-height: 40px; font-size: 0.9rem; }
.widget-label { color: var(--vp-c-text-2); }
.use-case { font-size: 0.82rem; color: var(--vp-c-text-3); }
</style>
@@ -0,0 +1,120 @@
<!--
CAPTheoremDemo.vue
CAP 定理交互演示展示一致性可用性分区容错性的权衡
-->
<template>
<div class="cap-demo">
<div class="header">
<div class="title">CAP 定理交互演示</div>
<div class="subtitle">点击选择两个属性查看对应的系统类型</div>
</div>
<div class="triangle">
<div
v-for="item in capItems"
:key="item.key"
:class="['cap-node', { active: selected.includes(item.key) }]"
@click="toggle(item.key)"
>
<div class="cap-letter">{{ item.letter }}</div>
<div class="cap-name">{{ item.name }}</div>
<div class="cap-desc">{{ item.desc }}</div>
</div>
</div>
<div v-if="result" class="result-panel">
<div class="result-title">{{ result.type }}</div>
<div class="result-desc">{{ result.desc }}</div>
<div class="result-examples">
<span class="label">典型系统</span>{{ result.examples }}
</div>
<div class="result-tradeoff">
<span class="label">放弃了</span>{{ result.sacrifice }}
</div>
</div>
<div v-else class="hint">请选择两个属性查看结果</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const selected = ref(['C', 'A'])
const capItems = [
{ key: 'C', letter: 'C', name: '一致性', desc: '所有节点看到相同的数据' },
{ key: 'A', letter: 'A', name: '可用性', desc: '每个请求都能得到响应' },
{ key: 'P', letter: 'P', name: '分区容错', desc: '网络分区时系统仍能运行' }
]
const combinations = {
'CA': {
type: 'CA 系统(放弃分区容错)',
desc: '在没有网络分区的情况下,同时保证一致性和可用性。但在分布式环境中,网络分区是不可避免的,所以纯 CA 系统在实际分布式场景中很少见。',
examples: '单机 MySQL、PostgreSQL(单节点)',
sacrifice: '分区容错性(P)— 网络故障时系统不可用'
},
'CP': {
type: 'CP 系统(放弃可用性)',
desc: '网络分区时优先保证数据一致性,可能拒绝部分请求。适合对数据正确性要求极高的场景。',
examples: 'ZooKeeper、etcd、HBase、MongoDB(强一致模式)',
sacrifice: '可用性(A)— 分区时部分请求会被拒绝或超时'
},
'AP': {
type: 'AP 系统(放弃强一致性)',
desc: '网络分区时优先保证可用性,允许数据暂时不一致(最终一致性)。适合对可用性要求高、能容忍短暂不一致的场景。',
examples: 'Cassandra、DynamoDB、DNS、CDN',
sacrifice: '强一致性(C)— 不同节点可能短暂返回不同数据'
}
}
function toggle(key) {
const idx = selected.value.indexOf(key)
if (idx >= 0) {
selected.value = selected.value.filter(k => k !== key)
} else {
if (selected.value.length >= 2) {
selected.value = [selected.value[1], key]
} else {
selected.value = [...selected.value, key]
}
}
}
const result = computed(() => {
if (selected.value.length !== 2) return null
const combo = [...selected.value].sort().join('')
return combinations[combo] || null
})
</script>
<style scoped>
.cap-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.triangle { display: flex; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap; justify-content: center; }
.cap-node {
flex: 1; min-width: 120px; max-width: 200px; padding: 0.75rem; border-radius: 8px;
cursor: pointer; text-align: center; background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider); transition: all 0.2s;
}
.cap-node:hover { border-color: var(--vp-c-brand); }
.cap-node.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.08); }
.cap-letter { font-size: 1.5rem; font-weight: 800; color: var(--vp-c-brand); }
.cap-name { font-weight: 700; font-size: 0.9rem; margin: 0.2rem 0; }
.cap-desc { font-size: 0.75rem; color: var(--vp-c-text-2); }
.result-panel {
background: var(--vp-c-bg); border-radius: 8px; padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.result-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.5rem; }
.result-desc { font-size: 0.82rem; color: var(--vp-c-text-2); margin-bottom: 0.5rem; }
.result-examples, .result-tradeoff { font-size: 0.82rem; margin-bottom: 0.25rem; }
.label { font-weight: 600; color: var(--vp-c-text-2); }
.hint { text-align: center; color: var(--vp-c-text-3); font-size: 0.85rem; padding: 1rem; }
</style>
@@ -0,0 +1,132 @@
<!--
ConsistencyModelsDemo.vue
一致性模型演示展示强一致性最终一致性因果一致性的区别
-->
<template>
<div class="consistency-demo">
<div class="header">
<div class="title">一致性模型对比</div>
<div class="subtitle">点击查看不同一致性模型的行为差异</div>
</div>
<div class="model-tabs">
<div
v-for="m in models"
:key="m.key"
:class="['tab', { active: activeModel === m.key }]"
@click="activeModel = m.key"
>
{{ m.name }}
</div>
</div>
<div v-if="current" class="model-detail">
<div class="model-name">{{ current.name }}</div>
<div class="model-desc">{{ current.desc }}</div>
<div class="timeline">
<div v-for="(step, i) in current.steps" :key="i" class="step">
<div class="step-time">T{{ i + 1 }}</div>
<div class="step-nodes">
<div
v-for="(node, ni) in step.nodes"
:key="ni"
:class="['node', node.status]"
>
<div class="node-label">{{ node.name }}</div>
<div class="node-value">{{ node.value }}</div>
</div>
</div>
<div class="step-desc">{{ step.desc }}</div>
</div>
</div>
<div class="model-tradeoff">
<span class="label">权衡</span>{{ current.tradeoff }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeModel = ref('strong')
const models = [
{
key: 'strong',
name: '强一致性',
desc: '写入成功后,所有节点立即返回最新值。像单机数据库一样的体验。',
tradeoff: '延迟高(需要等所有节点确认),可用性低(节点故障时可能阻塞)',
steps: [
{ nodes: [{ name: '节点A', value: 'v1', status: 'ok' }, { name: '节点B', value: 'v1', status: 'ok' }, { name: '节点C', value: 'v1', status: 'ok' }], desc: '初始状态,所有节点数据一致' },
{ nodes: [{ name: '节点A', value: 'v2 ✍️', status: 'writing' }, { name: '节点B', value: '同步中...', status: 'syncing' }, { name: '节点C', value: '同步中...', status: 'syncing' }], desc: '客户端写入 v2,等待所有节点确认' },
{ nodes: [{ name: '节点A', value: 'v2', status: 'ok' }, { name: '节点B', value: 'v2', status: 'ok' }, { name: '节点C', value: 'v2', status: 'ok' }], desc: '所有节点确认后才返回成功,读任意节点都是 v2' }
]
},
{
key: 'eventual',
name: '最终一致性',
desc: '写入后不等所有节点同步,数据最终会一致,但中间可能读到旧值。',
tradeoff: '延迟低、可用性高,但可能短暂读到旧数据',
steps: [
{ nodes: [{ name: '节点A', value: 'v1', status: 'ok' }, { name: '节点B', value: 'v1', status: 'ok' }, { name: '节点C', value: 'v1', status: 'ok' }], desc: '初始状态' },
{ nodes: [{ name: '节点A', value: 'v2 ✍️', status: 'writing' }, { name: '节点B', value: 'v1', status: 'stale' }, { name: '节点C', value: 'v1', status: 'stale' }], desc: '写入 A 后立即返回成功,B/C 还是旧值' },
{ nodes: [{ name: '节点A', value: 'v2', status: 'ok' }, { name: '节点B', value: 'v2', status: 'ok' }, { name: '节点C', value: 'v1→v2', status: 'syncing' }], desc: '后台异步同步,逐渐达到一致' }
]
},
{
key: 'causal',
name: '因果一致性',
desc: '有因果关系的操作保证顺序,无因果关系的操作可以乱序。介于强一致和最终一致之间。',
tradeoff: '比强一致性延迟低,比最终一致性更可预测',
steps: [
{ nodes: [{ name: '用户A', value: '发帖: "你好"', status: 'ok' }, { name: '用户B', value: '看到帖子', status: 'ok' }, { name: '用户C', value: '看到帖子', status: 'ok' }], desc: '用户 A 发帖' },
{ nodes: [{ name: '用户A', value: '发帖: "你好"', status: 'ok' }, { name: '用户B', value: '回复: "嗨!"', status: 'writing' }, { name: '用户C', value: '看到帖子', status: 'ok' }], desc: '用户 B 回复(因果依赖于 A 的帖子)' },
{ nodes: [{ name: '用户A', value: '看到回复', status: 'ok' }, { name: '用户B', value: '回复: "嗨!"', status: 'ok' }, { name: '用户C', value: '先看到帖子再看到回复', status: 'ok' }], desc: '所有人都先看到帖子再看到回复(因果顺序保证)' }
]
}
]
const current = computed(() => models.find(m => m.key === activeModel.value))
</script>
<style scoped>
.consistency-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.model-tabs { display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; }
.tab {
padding: 0.4rem 0.75rem; border-radius: 6px; cursor: pointer;
font-size: 0.85rem; background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.tab:hover { border-color: var(--vp-c-brand); }
.tab.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); font-weight: 600; }
.model-detail { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.model-name { font-weight: 700; font-size: 0.95rem; }
.model-desc { color: var(--vp-c-text-2); font-size: 0.82rem; margin-bottom: 0.75rem; }
.timeline { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.75rem; }
.step { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.step-time { font-weight: 700; font-size: 0.8rem; color: var(--vp-c-brand); min-width: 28px; }
.step-nodes { display: flex; gap: 0.4rem; flex: 1; }
.node {
padding: 0.3rem 0.5rem; border-radius: 4px; font-size: 0.72rem;
border: 1px solid var(--vp-c-divider); flex: 1; text-align: center;
}
.node.ok { background: rgba(34,197,94,0.08); border-color: #22c55e; }
.node.writing { background: rgba(var(--vp-c-brand-rgb),0.08); border-color: var(--vp-c-brand); }
.node.syncing { background: rgba(245,158,11,0.08); border-color: #f59e0b; }
.node.stale { background: rgba(239,68,68,0.08); border-color: #ef4444; }
.node-label { font-weight: 600; }
.node-value { color: var(--vp-c-text-2); }
.step-desc { font-size: 0.75rem; color: var(--vp-c-text-3); width: 100%; margin-top: 0.15rem; }
.model-tradeoff { font-size: 0.82rem; }
.label { font-weight: 600; color: var(--vp-c-text-2); }
@media (max-width: 640px) { .step { flex-direction: column; } .step-nodes { width: 100%; } }
</style>
@@ -0,0 +1,215 @@
<!--
DistributedChallengesDemo.vue
分布式系统常见挑战交互演示
-->
<template>
<div class="challenges-demo">
<div class="header">
<div class="title">分布式系统八大挑战</div>
<div class="subtitle">点击查看每个挑战的详情和应对策略</div>
</div>
<div class="challenge-grid">
<div
v-for="c in challenges"
:key="c.key"
:class="['challenge-card', { active: activeChallenge === c.key }]"
@click="activeChallenge = activeChallenge === c.key ? null : c.key"
>
<div class="challenge-icon">{{ c.icon }}</div>
<div class="challenge-name">{{ c.name }}</div>
</div>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-title">{{ current.icon }} {{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="detail-scenario">
<span class="label">场景举例</span>{{ current.scenario }}
</div>
<div class="detail-solution">
<span class="label">应对策略</span>
<ul class="solution-list">
<li v-for="(s, i) in current.solutions" :key="i">{{ s }}</li>
</ul>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeChallenge = ref('network')
const challenges = [
{
key: 'network',
name: '网络不可靠',
icon: '🔌',
desc: '分布式系统的节点通过网络通信,而网络随时可能丢包、延迟、断开。这是分布式系统最根本的挑战——你永远不能假设网络是可靠的。',
scenario: '服务 A 调用服务 B,请求发出后 3 秒没收到响应。是 B 没收到?还是 B 处理了但响应丢了?A 无法区分。',
solutions: [
'超时 + 重试:设置合理超时,失败后重试(需保证幂等性)',
'心跳检测:定期发送心跳包检测连接是否存活',
'断路器模式:连续失败后暂停调用,避免雪崩'
]
},
{
key: 'clock',
name: '时钟不同步',
icon: '⏰',
desc: '每台机器的物理时钟都有微小偏差(时钟漂移),即使用 NTP 同步也只能精确到毫秒级。在分布式系统中,你不能依赖物理时钟来判断事件的先后顺序。',
scenario: '节点 A 在 10:00:00.001 写入数据,节点 B 在 10:00:00.002 写入数据。但 B 的时钟快了 5ms,实际上 B 先写的。',
solutions: [
'逻辑时钟(Lamport Clock):用递增计数器代替物理时钟',
'向量时钟(Vector Clock):每个节点维护一个向量,追踪因果关系',
'TrueTimeGoogle Spanner):用 GPS + 原子钟提供有界误差的时间'
]
},
{
key: 'partition',
name: '网络分区',
icon: '✂️',
desc: '网络分区是指部分节点之间无法通信,但各自仍在运行。这时系统必须在一致性和可用性之间做选择(CAP 定理)。',
scenario: '数据中心 A 和 B 之间的光纤被挖断,两边的服务各自运行,但数据开始分叉。',
solutions: [
'CP 策略:分区时拒绝写入,保证一致性(如 ZooKeeper',
'AP 策略:分区时允许写入,事后合并冲突(如 DynamoDB',
'多数派写入:只要多数节点确认就算成功'
]
},
{
key: 'consistency',
name: '数据一致性',
icon: '🔄',
desc: '多个副本之间如何保持数据一致?强一致性性能差,最终一致性可能读到旧数据。没有银弹,只有权衡。',
scenario: '用户在节点 A 修改了头像,但刷新页面时请求被路由到节点 B,看到的还是旧头像。',
solutions: [
'读写同一节点:写入后的读请求路由到同一节点',
'读修复(Read Repair):读取时检测不一致并修复',
'反熵协议:后台定期比对副本,修复差异'
]
},
{
key: 'failure',
name: '部分失败',
icon: '💥',
desc: '分布式系统中,部分节点可能失败而其他节点正常运行。系统需要在部分失败的情况下继续提供服务。',
scenario: '5 个节点的集群中有 2 个节点宕机,系统需要判断:是继续服务还是停止?剩余节点的数据是否完整?',
solutions: [
'冗余副本:数据存多份,单点故障不影响可用性',
'故障检测:通过心跳和超时机制快速发现故障节点',
'自动故障转移:检测到主节点故障后自动切换到备节点'
]
},
{
key: 'split-brain',
name: '脑裂问题',
icon: '🧠',
desc: '当网络分区导致集群分成两部分时,两边都认为自己是"主",各自接受写入,导致数据冲突。这就是脑裂。',
scenario: '主从架构中,主节点和从节点之间网络断开,从节点以为主节点挂了,自己升级为主。现在有两个主节点同时写入。',
solutions: [
'多数派选举:只有获得多数票的节点才能成为主节点',
'Fencing Token:旧主节点的写入请求会被存储层拒绝',
'仲裁节点:引入第三方节点来裁决谁是真正的主'
]
},
{
key: 'ordering',
name: '事件排序',
icon: '📋',
desc: '在分布式系统中,不同节点上发生的事件没有全局统一的顺序。如何确定"谁先谁后"是一个根本性难题。',
scenario: '两个用户同时编辑同一个文档,节点 A 收到"删除第 3 行",节点 B 收到"修改第 3 行"。最终结果取决于执行顺序。',
solutions: [
'全序广播(Total Order Broadcast):所有节点以相同顺序处理消息',
'CRDT(无冲突复制数据类型):数据结构本身保证合并无冲突',
'OT(操作转换):Google Docs 使用的协作编辑算法'
]
},
{
key: 'transaction',
name: '分布式事务',
icon: '🔐',
desc: '跨多个节点的操作如何保证原子性?要么全部成功,要么全部回滚。这比单机事务复杂得多。',
scenario: '电商下单:扣库存在服务 A,扣余额在服务 B,创建订单在服务 C。如果扣余额失败,库存需要回滚。',
solutions: [
'2PC(两阶段提交):协调者先问所有参与者能否提交,再统一提交',
'Saga 模式:每个步骤有对应的补偿操作,失败时逐步回滚',
'TCCTry-Confirm-Cancel):预留资源 → 确认 → 取消'
]
}
]
const current = computed(() =>
challenges.find(c => c.key === activeChallenge.value)
)
</script>
<style scoped>
.challenges-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.challenge-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 0.5rem;
margin-bottom: 1rem;
}
.challenge-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.2rem;
padding: 0.6rem 0.4rem;
border-radius: 8px;
cursor: pointer;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.challenge-card:hover { border-color: var(--vp-c-brand); }
.challenge-card.active {
border-color: var(--vp-c-brand);
background: rgba(var(--vp-c-brand-rgb), 0.05);
}
.challenge-icon { font-size: 1.3rem; }
.challenge-name { font-size: 0.75rem; font-weight: 600; text-align: center; }
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.4rem; }
.detail-desc {
font-size: 0.82rem;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
}
.detail-scenario {
font-size: 0.82rem;
margin-bottom: 0.5rem;
padding: 0.5rem;
background: rgba(245, 158, 11, 0.06);
border-radius: 6px;
}
.detail-solution { font-size: 0.82rem; }
.solution-list {
margin: 0.3rem 0 0 1.2rem;
padding: 0;
}
.solution-list li {
margin-bottom: 0.2rem;
font-size: 0.8rem;
color: var(--vp-c-text-2);
}
.label { font-weight: 600; color: var(--vp-c-text-2); }
</style>
@@ -0,0 +1,184 @@
<!--
DockerArchitectureDemo.vue
Docker 架构对比演示虚拟机 vs 容器
-->
<template>
<div class="docker-arch-demo">
<div class="header">
<div class="title">虚拟机 vs 容器</div>
<div class="subtitle">点击切换查看两种虚拟化方式的架构差异</div>
</div>
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.key"
:class="['tab-btn', { active: activeTab === tab.key }]"
@click="activeTab = tab.key"
>
{{ tab.label }}
</button>
</div>
<div class="arch-view">
<div class="layers">
<div
v-for="(layer, i) in currentLayers"
:key="i"
:class="['layer', layer.type]"
>
<div class="layer-label">{{ layer.label }}</div>
<div v-if="layer.items" class="layer-items">
<div v-for="(item, j) in layer.items" :key="j" class="layer-item">
{{ item }}
</div>
</div>
</div>
</div>
<div class="comparison">
<div v-for="(item, i) in currentInfo" :key="i" class="info-row">
<span class="info-label">{{ item.label }}</span>
<span :class="['info-value', item.highlight ? 'highlight' : '']">{{ item.value }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeTab = ref('container')
const tabs = [
{ key: 'vm', label: '虚拟机' },
{ key: 'container', label: '容器' }
]
const vmLayers = [
{ label: '应用 A / 应用 B / 应用 C', type: 'app', items: ['App A + Bins/Libs', 'App B + Bins/Libs', 'App C + Bins/Libs'] },
{ label: '客户操作系统(Guest OS', type: 'os', items: ['Ubuntu', 'CentOS', 'Debian'] },
{ label: 'HypervisorVMware / KVM', type: 'hypervisor' },
{ label: '宿主操作系统(Host OS', type: 'host' },
{ label: '物理硬件', type: 'hardware' }
]
const containerLayers = [
{ label: '应用 A / 应用 B / 应用 C', type: 'app', items: ['App A + Bins/Libs', 'App B + Bins/Libs', 'App C + Bins/Libs'] },
{ label: 'Docker Engine', type: 'docker' },
{ label: '宿主操作系统(Host OS', type: 'host' },
{ label: '物理硬件', type: 'hardware' }
]
const vmInfo = [
{ label: '启动速度', value: '分钟级', highlight: false },
{ label: '资源占用', value: '每个 VM 需要完整 OSGB 级)', highlight: false },
{ label: '隔离性', value: '强(硬件级隔离)', highlight: true },
{ label: '密度', value: '单机通常 10-20 个 VM', highlight: false },
{ label: '镜像大小', value: 'GB 级', highlight: false }
]
const containerInfo = [
{ label: '启动速度', value: '秒级', highlight: true },
{ label: '资源占用', value: '共享宿主 OS 内核(MB 级)', highlight: true },
{ label: '隔离性', value: '较强(进程级隔离)', highlight: false },
{ label: '密度', value: '单机可运行数百个容器', highlight: true },
{ label: '镜像大小', value: 'MB 级', highlight: true }
]
const currentLayers = computed(() => activeTab.value === 'vm' ? vmLayers : containerLayers)
const currentInfo = computed(() => activeTab.value === 'vm' ? vmInfo : containerInfo)
</script>
<style scoped>
.docker-arch-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
.tab-btn {
padding: 0.4rem 1rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
font-size: 0.85rem;
font-weight: 600;
color: var(--vp-c-text-2);
transition: all 0.2s;
}
.tab-btn:hover { border-color: var(--vp-c-brand); }
.tab-btn.active {
background: var(--vp-c-brand);
color: #fff;
border-color: var(--vp-c-brand);
}
.arch-view {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
@media (max-width: 640px) {
.arch-view { grid-template-columns: 1fr; }
}
.layers {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.layer {
padding: 0.5rem;
border-radius: 6px;
text-align: center;
font-size: 0.78rem;
font-weight: 600;
}
.layer.app { background: rgba(59, 130, 246, 0.12); color: var(--vp-c-text-1); }
.layer.os { background: rgba(245, 158, 11, 0.12); color: var(--vp-c-text-1); }
.layer.hypervisor { background: rgba(239, 68, 68, 0.12); color: var(--vp-c-text-1); }
.layer.docker { background: rgba(6, 182, 212, 0.15); color: var(--vp-c-text-1); }
.layer.host { background: rgba(34, 197, 94, 0.12); color: var(--vp-c-text-1); }
.layer.hardware { background: rgba(107, 114, 128, 0.12); color: var(--vp-c-text-2); }
.layer-label { margin-bottom: 0.25rem; }
.layer-items {
display: flex;
gap: 0.3rem;
justify-content: center;
flex-wrap: wrap;
}
.layer-item {
padding: 0.2rem 0.5rem;
background: var(--vp-c-bg);
border-radius: 4px;
font-size: 0.72rem;
font-weight: 500;
}
.comparison {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.4rem 0.6rem;
background: var(--vp-c-bg);
border-radius: 6px;
font-size: 0.8rem;
}
.info-label { color: var(--vp-c-text-2); font-weight: 500; }
.info-value { font-weight: 600; }
.info-value.highlight { color: var(--vp-c-brand); }
</style>
@@ -0,0 +1,178 @@
<!--
DockerLifecycleDemo.vue
Docker 生命周期演示镜像构建到容器运行的流程
-->
<template>
<div class="docker-lifecycle-demo">
<div class="header">
<div class="title">Docker 生命周期</div>
<div class="subtitle">点击每个阶段查看详细说明</div>
</div>
<div class="stages">
<div
v-for="(stage, i) in stages"
:key="stage.key"
:class="['stage-card', { active: activeStage === stage.key }]"
@click="activeStage = stage.key"
>
<div class="stage-icon">{{ stage.icon }}</div>
<div class="stage-name">{{ stage.name }}</div>
<div v-if="i < stages.length - 1" class="arrow"></div>
</div>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-title">{{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="command-block">
<div class="cmd-label">常用命令</div>
<div v-for="(cmd, i) in current.commands" :key="i" class="cmd-item">
<code>{{ cmd.cmd }}</code>
<span class="cmd-desc">{{ cmd.desc }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeStage = ref('write')
const stages = [
{
key: 'write',
name: '编写 Dockerfile',
icon: '📝',
desc: 'Dockerfile 是构建镜像的"配方",定义了从基础镜像开始,如何一步步构建出你的应用环境。每条指令创建一个镜像层(Layer),Docker 会缓存这些层以加速后续构建。',
commands: [
{ cmd: 'FROM node:18-alpine', desc: '指定基础镜像' },
{ cmd: 'WORKDIR /app', desc: '设置工作目录' },
{ cmd: 'COPY package*.json ./', desc: '复制依赖文件(利用缓存)' },
{ cmd: 'RUN npm install', desc: '安装依赖' },
{ cmd: 'COPY . .', desc: '复制应用代码' },
{ cmd: 'EXPOSE 3000', desc: '声明端口' },
{ cmd: 'CMD ["node", "server.js"]', desc: '启动命令' }
]
},
{
key: 'build',
name: '构建镜像',
icon: '🔨',
desc: 'docker build 命令读取 Dockerfile,逐层执行指令,最终生成一个不可变的镜像(Image)。镜像是只读的模板,包含运行应用所需的一切:代码、运行时、库、环境变量。',
commands: [
{ cmd: 'docker build -t myapp:1.0 .', desc: '构建并打标签' },
{ cmd: 'docker images', desc: '查看本地镜像列表' },
{ cmd: 'docker image prune', desc: '清理无用镜像' }
]
},
{
key: 'push',
name: '推送仓库',
icon: '☁️',
desc: '将构建好的镜像推送到镜像仓库(Registry),如 Docker Hub、阿里云 ACR、AWS ECR。团队成员和部署环境可以从仓库拉取镜像,实现"一次构建,到处运行"。',
commands: [
{ cmd: 'docker tag myapp:1.0 registry/myapp:1.0', desc: '给镜像打远程标签' },
{ cmd: 'docker push registry/myapp:1.0', desc: '推送到仓库' },
{ cmd: 'docker pull registry/myapp:1.0', desc: '从仓库拉取' }
]
},
{
key: 'run',
name: '运行容器',
icon: '▶️',
desc: '容器是镜像的运行实例。一个镜像可以启动多个容器,每个容器有独立的文件系统、网络和进程空间。容器是轻量级的,启动只需秒级。',
commands: [
{ cmd: 'docker run -d -p 3000:3000 myapp:1.0', desc: '后台运行并映射端口' },
{ cmd: 'docker ps', desc: '查看运行中的容器' },
{ cmd: 'docker logs <container>', desc: '查看容器日志' },
{ cmd: 'docker exec -it <container> sh', desc: '进入容器终端' }
]
},
{
key: 'manage',
name: '管理容器',
icon: '⚙️',
desc: '容器运行后需要监控、停止、重启或删除。Docker Compose 可以管理多个容器的编排,定义服务间的依赖关系和网络。',
commands: [
{ cmd: 'docker stop <container>', desc: '停止容器' },
{ cmd: 'docker restart <container>', desc: '重启容器' },
{ cmd: 'docker rm <container>', desc: '删除容器' },
{ cmd: 'docker compose up -d', desc: '用 Compose 启动多服务' }
]
}
]
const current = computed(() => stages.find(s => s.key === activeStage.value))
</script>
<style scoped>
.docker-lifecycle-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.stages {
display: flex;
align-items: center;
gap: 0.25rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.stage-card {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 0.7rem;
border-radius: 8px;
cursor: pointer;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.stage-card:hover { border-color: var(--vp-c-brand); }
.stage-card.active {
border-color: var(--vp-c-brand);
background: rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.05);
}
.stage-icon { font-size: 1.1rem; }
.stage-name { font-size: 0.8rem; font-weight: 600; }
.arrow { color: var(--vp-c-text-3); font-size: 0.9rem; margin: 0 0.1rem; }
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.4rem; }
.detail-desc { font-size: 0.82rem; color: var(--vp-c-text-2); margin-bottom: 0.75rem; line-height: 1.6; }
.command-block {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.6rem;
}
.cmd-label { font-weight: 600; font-size: 0.78rem; margin-bottom: 0.4rem; color: var(--vp-c-text-2); }
.cmd-item {
display: flex;
align-items: baseline;
gap: 0.5rem;
padding: 0.25rem 0;
font-size: 0.78rem;
}
.cmd-item code {
background: var(--vp-c-bg);
padding: 0.15rem 0.4rem;
border-radius: 4px;
font-size: 0.75rem;
white-space: nowrap;
color: var(--vp-c-brand);
}
.cmd-desc { color: var(--vp-c-text-3); }
</style>
@@ -0,0 +1,87 @@
<!--
AvailabilityCalculatorDemo.vue
可用性计算器展示不同 SLA 级别对应的停机时间
-->
<template>
<div class="availability-demo">
<div class="header">
<div class="title">可用性等级计算器</div>
<div class="subtitle">点击查看不同"几个 9"对应的停机时间</div>
</div>
<div class="sla-cards">
<div
v-for="sla in slaLevels"
:key="sla.nines"
:class="['sla-card', { active: activeSla === sla.nines }]"
@click="activeSla = sla.nines"
>
<div class="sla-nines">{{ sla.label }}</div>
<div class="sla-percent">{{ sla.percent }}</div>
</div>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-title">{{ current.label }}{{ current.percent }}</div>
<div class="downtime-grid">
<div class="downtime-item">
<div class="dt-label">每年停机</div>
<div class="dt-value">{{ current.yearly }}</div>
</div>
<div class="downtime-item">
<div class="dt-label">每月停机</div>
<div class="dt-value">{{ current.monthly }}</div>
</div>
<div class="downtime-item">
<div class="dt-label">每周停机</div>
<div class="dt-value">{{ current.weekly }}</div>
</div>
</div>
<div class="detail-examples">
<span class="label">典型场景</span>{{ current.examples }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeSla = ref('3')
const slaLevels = [
{ nines: '2', label: '2 个 9', percent: '99%', yearly: '3.65 天', monthly: '7.3 小时', weekly: '1.68 小时', examples: '内部工具、非关键系统' },
{ nines: '3', label: '3 个 9', percent: '99.9%', yearly: '8.76 小时', monthly: '43.8 分钟', weekly: '10.1 分钟', examples: '普通 Web 应用、企业系统' },
{ nines: '4', label: '4 个 9', percent: '99.99%', yearly: '52.6 分钟', monthly: '4.38 分钟', weekly: '1.01 分钟', examples: '电商平台、SaaS 服务' },
{ nines: '5', label: '5 个 9', percent: '99.999%', yearly: '5.26 分钟', monthly: '26.3 秒', weekly: '6.05 秒', examples: '金融交易、电信核心网' }
]
const current = computed(() => slaLevels.find(s => s.nines === activeSla.value))
</script>
<style scoped>
.availability-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.sla-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 0.5rem; margin-bottom: 1rem; }
.sla-card {
padding: 0.6rem; border-radius: 8px; cursor: pointer; text-align: center;
background: var(--vp-c-bg); border: 2px solid var(--vp-c-divider); transition: all 0.2s;
}
.sla-card:hover { border-color: var(--vp-c-brand); }
.sla-card.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.08); }
.sla-nines { font-weight: 800; font-size: 1.1rem; color: var(--vp-c-brand); }
.sla-percent { font-size: 0.8rem; color: var(--vp-c-text-2); }
.detail-panel { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.detail-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.75rem; }
.downtime-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-bottom: 0.75rem; }
.downtime-item { text-align: center; padding: 0.5rem; border-radius: 6px; background: var(--vp-c-bg-soft); }
.dt-label { font-size: 0.75rem; color: var(--vp-c-text-3); }
.dt-value { font-weight: 700; font-size: 0.9rem; color: var(--vp-c-brand); }
.detail-examples { font-size: 0.82rem; }
.label { font-weight: 600; color: var(--vp-c-text-2); }
</style>
@@ -0,0 +1,143 @@
<!--
FailoverStrategyDemo.vue
故障转移策略演示展示主备主主多活等高可用架构
-->
<template>
<div class="failover-demo">
<div class="header">
<div class="title">故障转移策略对比</div>
<div class="subtitle">点击查看不同高可用架构的工作方式</div>
</div>
<div class="strategy-tabs">
<div
v-for="s in strategies"
:key="s.key"
:class="['tab', { active: activeStrategy === s.key }]"
@click="activeStrategy = s.key"
>
{{ s.name }}
</div>
</div>
<div v-if="current" class="strategy-detail">
<div class="strategy-name">{{ current.name }}</div>
<div class="strategy-desc">{{ current.desc }}</div>
<div class="arch-visual">
<div
v-for="(node, i) in current.nodes"
:key="i"
:class="['arch-node', node.role]"
>
<div class="node-role">{{ node.label }}</div>
<div class="node-status">{{ node.status }}</div>
</div>
</div>
<div class="pros-cons">
<div class="pros">
<div class="pc-title">优点</div>
<div v-for="p in current.pros" :key="p" class="pc-item good">{{ p }}</div>
</div>
<div class="cons">
<div class="pc-title">缺点</div>
<div v-for="c in current.cons" :key="c" class="pc-item bad">{{ c }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeStrategy = ref('active-standby')
const strategies = [
{
key: 'active-standby',
name: '主备模式',
desc: '一个主节点处理所有请求,备节点待命。主节点故障时,备节点接管。',
nodes: [
{ label: '主节点', status: '处理请求', role: 'primary' },
{ label: '备节点', status: '待命同步', role: 'standby' }
],
pros: ['架构简单,易于理解', '数据一致性好保证'],
cons: ['备节点资源浪费', '切换有短暂中断(秒级)']
},
{
key: 'active-active',
name: '主主模式',
desc: '两个节点都处理请求,互相同步数据。任一节点故障,另一个继续服务。',
nodes: [
{ label: '节点 A', status: '处理请求', role: 'primary' },
{ label: '节点 B', status: '处理请求', role: 'primary' }
],
pros: ['资源利用率高', '无切换中断'],
cons: ['数据冲突处理复杂', '需要解决写冲突']
},
{
key: 'multi-az',
name: '多可用区',
desc: '在同一地域的不同数据中心部署,防止单个机房故障。',
nodes: [
{ label: 'AZ-1 主', status: '读写', role: 'primary' },
{ label: 'AZ-2 从', status: '只读', role: 'secondary' },
{ label: 'AZ-3 从', status: '只读', role: 'secondary' }
],
pros: ['机房级容灾', '读性能可扩展'],
cons: ['跨 AZ 延迟(1-2ms', '成本增加']
},
{
key: 'multi-region',
name: '异地多活',
desc: '在不同地域部署完整的服务,每个地域独立处理本地流量。',
nodes: [
{ label: '北京', status: '独立服务', role: 'primary' },
{ label: '上海', status: '独立服务', role: 'primary' },
{ label: '广州', status: '独立服务', role: 'primary' }
],
pros: ['地域级容灾', '就近访问延迟低'],
cons: ['架构极其复杂', '数据同步挑战大']
}
]
const current = computed(() => strategies.find(s => s.key === activeStrategy.value))
</script>
<style scoped>
.failover-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.strategy-tabs { display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; }
.tab {
padding: 0.4rem 0.75rem; border-radius: 6px; cursor: pointer;
font-size: 0.85rem; background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.tab:hover { border-color: var(--vp-c-brand); }
.tab.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); font-weight: 600; }
.strategy-detail { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.strategy-name { font-weight: 700; font-size: 0.95rem; }
.strategy-desc { color: var(--vp-c-text-2); font-size: 0.82rem; margin-bottom: 0.75rem; }
.arch-visual { display: flex; gap: 0.5rem; margin-bottom: 0.75rem; flex-wrap: wrap; justify-content: center; }
.arch-node {
padding: 0.5rem 0.75rem; border-radius: 6px; text-align: center;
border: 1px dashed var(--vp-c-divider); min-width: 90px;
}
.arch-node.primary { background: rgba(var(--vp-c-brand-rgb), 0.08); border-color: var(--vp-c-brand); }
.arch-node.standby { background: rgba(245,158,11,0.08); border-color: #f59e0b; }
.arch-node.secondary { background: rgba(99,102,241,0.08); border-color: #6366f1; }
.node-role { font-weight: 700; font-size: 0.82rem; }
.node-status { font-size: 0.72rem; color: var(--vp-c-text-2); }
.pros-cons { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.pc-title { font-weight: 700; font-size: 0.82rem; margin-bottom: 0.3rem; }
.pc-item { font-size: 0.78rem; padding: 0.2rem 0; }
.pc-item.good::before { content: '+ '; color: #22c55e; font-weight: 700; }
.pc-item.bad::before { content: '- '; color: #ef4444; font-weight: 700; }
</style>
@@ -0,0 +1,183 @@
<!--
K8sArchitectureDemo.vue
Kubernetes 架构演示控制平面与工作节点
-->
<template>
<div class="k8s-arch-demo">
<div class="header">
<div class="title">Kubernetes 架构</div>
<div class="subtitle">点击组件查看详细说明</div>
</div>
<div class="arch-layout">
<div class="plane control-plane">
<div class="plane-title">控制平面Control Plane</div>
<div class="components">
<div
v-for="c in controlPlane"
:key="c.key"
:class="['comp-card', { active: active === c.key }]"
@click="active = c.key"
>
<div class="comp-name">{{ c.name }}</div>
</div>
</div>
</div>
<div class="plane worker-plane">
<div class="plane-title">工作节点Worker Node× N</div>
<div class="components">
<div
v-for="c in workerNode"
:key="c.key"
:class="['comp-card', { active: active === c.key }]"
@click="active = c.key"
>
<div class="comp-name">{{ c.name }}</div>
</div>
</div>
</div>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-title">{{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="detail-analogy">
<span class="label">类比</span>{{ current.analogy }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const active = ref('api-server')
const controlPlane = [
{
key: 'api-server',
name: 'API Server',
desc: 'Kubernetes 的"前门",所有操作(kubectl、Dashboard、内部组件)都通过 API Server 进行。它负责认证、授权、准入控制,是集群的唯一入口。',
analogy: '公司前台,所有访客和快递都要经过前台登记'
},
{
key: 'etcd',
name: 'etcd',
desc: '分布式键值存储,保存集群的所有状态数据:Pod 信息、Service 配置、Secret 等。它是集群的"记忆",丢失 etcd 数据等于丢失整个集群。',
analogy: '公司的档案室,记录所有员工信息和规章制度'
},
{
key: 'scheduler',
name: 'Scheduler',
desc: '负责将新创建的 Pod 分配到合适的节点上。它会考虑资源需求、亲和性规则、污点容忍等因素,做出最优调度决策。',
analogy: 'HR 部门,根据岗位需求把新员工分配到合适的部门'
},
{
key: 'controller',
name: 'Controller Manager',
desc: '运行各种控制器(Deployment、ReplicaSet、Job 等),持续监控集群状态,确保实际状态与期望状态一致。如果 Pod 挂了,控制器会自动重建。',
analogy: '各部门经理,确保每个部门的人员配置符合编制要求'
}
]
const workerNode = [
{
key: 'kubelet',
name: 'kubelet',
desc: '每个节点上的"代理人",负责管理本节点上的 Pod 生命周期。它接收 API Server 的指令,调用容器运行时创建/销毁容器,并上报节点状态。',
analogy: '每个工位上的组长,负责管理组员的日常工作'
},
{
key: 'kube-proxy',
name: 'kube-proxy',
desc: '负责实现 Service 的网络规则,将访问 Service 的流量转发到对应的 Pod。它维护节点上的 iptables/IPVS 规则,实现负载均衡。',
analogy: '公司的电话总机,把外部来电转接到正确的分机'
},
{
key: 'runtime',
name: '容器运行时',
desc: '实际运行容器的组件,如 containerd、CRI-O。kubelet 通过 CRI(容器运行时接口)与它交互,它负责拉取镜像、创建和管理容器。',
analogy: '实际干活的工人,按照指令完成具体的生产任务'
}
]
const allComponents = [...controlPlane, ...workerNode]
const current = computed(() => allComponents.find(c => c.key === active.value))
</script>
<style scoped>
.k8s-arch-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.arch-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
margin-bottom: 1rem;
}
@media (max-width: 640px) {
.arch-layout { grid-template-columns: 1fr; }
}
.plane {
border-radius: 8px;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
.control-plane { background: rgba(59, 130, 246, 0.06); }
.worker-plane { background: rgba(34, 197, 94, 0.06); }
.plane-title {
font-weight: 700;
font-size: 0.8rem;
margin-bottom: 0.5rem;
color: var(--vp-c-text-2);
}
.components {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.comp-card {
padding: 0.35rem 0.6rem;
border-radius: 6px;
cursor: pointer;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
font-size: 0.78rem;
font-weight: 600;
transition: all 0.2s;
}
.comp-card:hover { border-color: var(--vp-c-brand); }
.comp-card.active {
border-color: var(--vp-c-brand);
background: rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.08);
color: var(--vp-c-brand);
}
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.4rem; }
.detail-desc {
font-size: 0.82rem;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
line-height: 1.6;
}
.detail-analogy {
font-size: 0.8rem;
padding: 0.4rem 0.6rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.label { font-weight: 600; color: var(--vp-c-text-2); }
</style>
@@ -0,0 +1,235 @@
<!--
K8sWorkloadsDemo.vue
Kubernetes 工作负载演示PodDeploymentService 等核心资源
-->
<template>
<div class="k8s-workloads-demo">
<div class="header">
<div class="title">K8s 核心资源</div>
<div class="subtitle">点击资源类型查看说明和 YAML 示例</div>
</div>
<div class="resource-tabs">
<button
v-for="r in resources"
:key="r.key"
:class="['res-btn', { active: activeRes === r.key }]"
@click="activeRes = r.key"
>
{{ r.name }}
</button>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-header">
<div class="detail-title">{{ current.name }}</div>
<div class="detail-badge">{{ current.category }}</div>
</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="yaml-block">
<div class="yaml-label">YAML 示例</div>
<pre class="yaml-code"><code>{{ current.yaml }}</code></pre>
</div>
<div v-if="current.tips" class="tips">
<span class="tip-label">要点</span>{{ current.tips }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeRes = ref('pod')
const resources = [
{
key: 'pod',
name: 'Pod',
category: '最小调度单元',
desc: 'Pod 是 K8s 中最小的部署单元,包含一个或多个紧密关联的容器。同一 Pod 内的容器共享网络和存储,可以通过 localhost 互相通信。',
yaml: `apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: my-app:1.0
ports:
- containerPort: 3000`,
tips: '生产环境中很少直接创建 Pod,通常通过 Deployment 管理。'
},
{
key: 'deployment',
name: 'Deployment',
category: '工作负载',
desc: 'Deployment 管理 Pod 的副本数、滚动更新和回滚。你声明"我要 3 个副本运行 v1.0"Deployment 控制器会确保始终有 3 个健康的 Pod 在运行。',
yaml: `apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:1.0`,
tips: '更新镜像版本后,Deployment 会自动执行滚动更新,逐步替换旧 Pod。'
},
{
key: 'service',
name: 'Service',
category: '网络',
desc: 'Service 为一组 Pod 提供稳定的访问入口。Pod 的 IP 会变,但 Service 的 ClusterIP 和 DNS 名称不变。它通过 label selector 找到对应的 Pod,并做负载均衡。',
yaml: `apiVersion: v1
kind: Service
metadata:
name: my-app-svc
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 3000
type: ClusterIP`,
tips: 'ClusterIP(集群内访问)、NodePort(节点端口)、LoadBalancer(云负载均衡器)是三种常用类型。'
},
{
key: 'configmap',
name: 'ConfigMap',
category: '配置',
desc: 'ConfigMap 存储非敏感的配置数据(如数据库地址、功能开关),可以作为环境变量或文件挂载到 Pod 中。修改 ConfigMap 后可以不重建镜像就更新配置。',
yaml: `apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: "db.example.com"
LOG_LEVEL: "info"`,
tips: '敏感数据(密码、密钥)应该用 Secret 而不是 ConfigMap。'
},
{
key: 'ingress',
name: 'Ingress',
category: '网络',
desc: 'Ingress 管理集群的外部 HTTP/HTTPS 访问入口,支持基于域名和路径的路由规则。它是集群的"反向代理",通常配合 Nginx Ingress Controller 使用。',
yaml: `apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-svc
port:
number: 80`,
tips: 'Ingress 需要 Ingress Controller 才能工作,它本身只是路由规则的声明。'
}
]
const current = computed(() => resources.find(r => r.key === activeRes.value))
</script>
<style scoped>
.k8s-workloads-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.resource-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-bottom: 1rem;
}
.res-btn {
padding: 0.35rem 0.7rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
color: var(--vp-c-text-2);
transition: all 0.2s;
}
.res-btn:hover { border-color: var(--vp-c-brand); }
.res-btn.active {
background: var(--vp-c-brand);
color: #fff;
border-color: var(--vp-c-brand);
}
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.4rem;
}
.detail-title { font-weight: 700; font-size: 0.95rem; }
.detail-badge {
font-size: 0.68rem;
padding: 0.15rem 0.4rem;
border-radius: 4px;
background: rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.1);
color: var(--vp-c-brand);
font-weight: 600;
}
.detail-desc {
font-size: 0.82rem;
color: var(--vp-c-text-2);
margin-bottom: 0.75rem;
line-height: 1.6;
}
.yaml-block {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.6rem;
margin-bottom: 0.5rem;
}
.yaml-label {
font-weight: 600;
font-size: 0.72rem;
color: var(--vp-c-text-3);
margin-bottom: 0.3rem;
}
.yaml-code {
font-size: 0.75rem;
line-height: 1.5;
margin: 0;
overflow-x: auto;
color: var(--vp-c-text-1);
}
.tips {
font-size: 0.78rem;
color: var(--vp-c-text-2);
padding: 0.4rem 0.6rem;
background: rgba(245, 158, 11, 0.08);
border-radius: 6px;
}
.tip-label { font-weight: 600; }
</style>
@@ -0,0 +1,168 @@
<!--
LinuxCommandDemo.vue
Linux 常用命令分类演示
-->
<template>
<div class="linux-cmd-demo">
<div class="header">
<div class="title">Linux 命令速查</div>
<div class="subtitle">按分类查看常用命令及示例</div>
</div>
<div class="categories">
<button
v-for="cat in categories"
:key="cat.key"
:class="['cat-btn', { active: activeCat === cat.key }]"
@click="activeCat = cat.key"
>
{{ cat.label }}
</button>
</div>
<div v-if="current" class="cmd-list">
<div v-for="(cmd, i) in current.commands" :key="i" class="cmd-card">
<div class="cmd-header">
<code class="cmd-name">{{ cmd.name }}</code>
<span class="cmd-brief">{{ cmd.brief }}</span>
</div>
<div class="cmd-example">
<code>{{ cmd.example }}</code>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeCat = ref('file')
const categories = [
{
key: 'file',
label: '文件操作',
commands: [
{ name: 'ls', brief: '列出文件和目录', example: 'ls -la /home' },
{ name: 'cd', brief: '切换目录', example: 'cd /var/log' },
{ name: 'cp', brief: '复制文件', example: 'cp -r src/ backup/' },
{ name: 'mv', brief: '移动/重命名', example: 'mv old.txt new.txt' },
{ name: 'rm', brief: '删除文件', example: 'rm -rf dist/' },
{ name: 'mkdir', brief: '创建目录', example: 'mkdir -p src/components' },
{ name: 'find', brief: '查找文件', example: 'find . -name "*.js" -type f' }
]
},
{
key: 'text',
label: '文本处理',
commands: [
{ name: 'cat', brief: '查看文件内容', example: 'cat config.json' },
{ name: 'grep', brief: '搜索文本', example: 'grep -rn "ERROR" /var/log/' },
{ name: 'head/tail', brief: '查看文件头/尾', example: 'tail -f app.log' },
{ name: 'awk', brief: '文本列处理', example: "awk '{print $1, $3}' data.txt" },
{ name: 'sed', brief: '流式文本替换', example: "sed -i 's/old/new/g' file.txt" },
{ name: 'wc', brief: '统计行/词/字符数', example: 'wc -l *.js' },
{ name: 'sort | uniq', brief: '排序去重', example: 'sort data.txt | uniq -c' }
]
},
{
key: 'process',
label: '进程管理',
commands: [
{ name: 'ps', brief: '查看进程', example: 'ps aux | grep node' },
{ name: 'top/htop', brief: '实时监控', example: 'top -o %CPU' },
{ name: 'kill', brief: '终止进程', example: 'kill -9 12345' },
{ name: 'nohup', brief: '后台运行', example: 'nohup node app.js &' },
{ name: 'lsof', brief: '查看打开的文件', example: 'lsof -i :3000' },
{ name: 'systemctl', brief: '管理系统服务', example: 'systemctl restart nginx' }
]
},
{
key: 'network',
label: '网络工具',
commands: [
{ name: 'curl', brief: '发送 HTTP 请求', example: 'curl -X POST -d "data" url' },
{ name: 'ping', brief: '测试连通性', example: 'ping -c 4 google.com' },
{ name: 'ss/netstat', brief: '查看网络连接', example: 'ss -tlnp' },
{ name: 'dig', brief: 'DNS 查询', example: 'dig example.com' },
{ name: 'ssh', brief: '远程登录', example: 'ssh user@server -p 22' },
{ name: 'scp', brief: '远程复制文件', example: 'scp file.txt user@server:/tmp/' }
]
}
]
const current = computed(() => categories.find(c => c.key === activeCat.value))
</script>
<style scoped>
.linux-cmd-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.categories {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-bottom: 1rem;
}
.cat-btn {
padding: 0.35rem 0.7rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
color: var(--vp-c-text-2);
transition: all 0.2s;
}
.cat-btn:hover { border-color: var(--vp-c-brand); }
.cat-btn.active {
background: var(--vp-c-brand);
color: #fff;
border-color: var(--vp-c-brand);
}
.cmd-list {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.cmd-card {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 0.5rem 0.7rem;
border: 1px solid var(--vp-c-divider);
}
.cmd-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.25rem;
}
.cmd-name {
font-weight: 700;
font-size: 0.82rem;
color: var(--vp-c-brand);
background: rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.08);
padding: 0.1rem 0.35rem;
border-radius: 4px;
}
.cmd-brief {
font-size: 0.78rem;
color: var(--vp-c-text-2);
}
.cmd-example code {
font-size: 0.73rem;
color: var(--vp-c-text-3);
background: var(--vp-c-bg-soft);
padding: 0.1rem 0.3rem;
border-radius: 3px;
}
</style>
@@ -0,0 +1,119 @@
<!--
LinuxFileSystemDemo.vue
Linux 文件系统层级演示
-->
<template>
<div class="linux-fs-demo">
<div class="header">
<div class="title">Linux 文件系统层级</div>
<div class="subtitle">点击目录查看用途说明</div>
</div>
<div class="tree">
<div
v-for="dir in dirs"
:key="dir.path"
:class="['dir-item', { active: activeDir === dir.path }]"
@click="activeDir = dir.path"
>
<span class="dir-icon">{{ dir.icon }}</span>
<span class="dir-path">{{ dir.path }}</span>
<span class="dir-brief">{{ dir.brief }}</span>
</div>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-title">{{ current.path }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div v-if="current.examples.length" class="examples">
<div class="ex-label">常见内容</div>
<div class="ex-list">
<span v-for="(ex, i) in current.examples" :key="i" class="ex-tag">{{ ex }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeDir = ref('/')
const dirs = [
{ path: '/', icon: '📁', brief: '根目录', desc: '整个文件系统的起点,所有目录和文件都从这里开始。Linux 中一切皆文件,所有设备、进程信息都以文件形式存在于这棵目录树中。', examples: [] },
{ path: '/bin', icon: '⚙️', brief: '基础命令', desc: '存放系统启动和单用户模式下必需的基础命令二进制文件。这些命令所有用户都可以使用。', examples: ['ls', 'cp', 'mv', 'cat', 'grep', 'chmod'] },
{ path: '/etc', icon: '📋', brief: '配置文件', desc: '存放系统和应用的配置文件。几乎所有软件的配置都在这里,修改配置是 Linux 运维的日常。', examples: ['nginx.conf', 'hosts', 'passwd', 'ssh/sshd_config', 'crontab'] },
{ path: '/home', icon: '🏠', brief: '用户目录', desc: '普通用户的家目录。每个用户在这里有一个以用户名命名的子目录,存放个人文件和配置。', examples: ['/home/alice', '/home/bob', '~/.bashrc', '~/.ssh/'] },
{ path: '/var', icon: '📊', brief: '可变数据', desc: '存放运行时会变化的数据:日志、缓存、邮件、数据库文件等。排查问题时经常需要查看这里的日志。', examples: ['/var/log/', '/var/cache/', '/var/lib/mysql/', '/var/www/'] },
{ path: '/tmp', icon: '🗑️', brief: '临时文件', desc: '存放临时文件,系统重启后通常会被清空。所有用户都有写权限,适合存放不需要持久化的中间文件。', examples: ['编译中间文件', '下载缓存', '会话临时数据'] },
{ path: '/usr', icon: '📦', brief: '用户程序', desc: '存放用户安装的程序、库和文档。可以理解为 "Unix System Resources",是最大的目录之一。', examples: ['/usr/bin/', '/usr/lib/', '/usr/local/', '/usr/share/'] },
{ path: '/proc', icon: '🔍', brief: '进程信息', desc: '虚拟文件系统,不占磁盘空间。内核将进程和系统信息以文件形式暴露在这里,是监控和调试的重要数据源。', examples: ['/proc/cpuinfo', '/proc/meminfo', '/proc/[pid]/status'] },
{ path: '/dev', icon: '🔌', brief: '设备文件', desc: '存放设备文件。Linux 中硬件设备也是文件,通过读写这些文件与硬件交互。', examples: ['/dev/sda', '/dev/null', '/dev/zero', '/dev/tty'] }
]
const current = computed(() => dirs.find(d => d.path === activeDir.value))
</script>
<style scoped>
.linux-fs-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.tree {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-bottom: 1rem;
}
.dir-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.35rem 0.6rem;
border-radius: 6px;
cursor: pointer;
background: var(--vp-c-bg);
border: 1px solid transparent;
transition: all 0.2s;
font-size: 0.82rem;
}
.dir-item:hover { border-color: var(--vp-c-divider); }
.dir-item.active {
border-color: var(--vp-c-brand);
background: rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.05);
}
.dir-icon { font-size: 0.9rem; }
.dir-path { font-weight: 700; font-family: var(--vp-font-family-mono); min-width: 60px; }
.dir-brief { color: var(--vp-c-text-3); font-size: 0.78rem; }
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-title {
font-weight: 700;
font-size: 0.95rem;
font-family: var(--vp-font-family-mono);
margin-bottom: 0.4rem;
}
.detail-desc { font-size: 0.82rem; color: var(--vp-c-text-2); margin-bottom: 0.5rem; line-height: 1.6; }
.examples { margin-top: 0.4rem; }
.ex-label { font-size: 0.75rem; font-weight: 600; color: var(--vp-c-text-3); margin-bottom: 0.3rem; }
.ex-list { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.ex-tag {
font-size: 0.72rem;
padding: 0.15rem 0.4rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
font-family: var(--vp-font-family-mono);
color: var(--vp-c-brand);
}
</style>
@@ -0,0 +1,234 @@
<!--
LinuxPermissionsDemo.vue
Linux 权限系统演示
-->
<template>
<div class="linux-perm-demo">
<div class="header">
<div class="title">Linux 权限解读器</div>
<div class="subtitle">输入权限字符串或数字查看含义</div>
</div>
<div class="input-row">
<div class="input-group">
<label>权限数字 755</label>
<input v-model="permNum" type="text" maxlength="3" placeholder="755" @input="onNumInput" />
</div>
<div class="perm-string">{{ permString }}</div>
</div>
<div class="perm-grid">
<div v-for="(group, gi) in groups" :key="gi" class="perm-group">
<div class="group-label">{{ group.label }}</div>
<div class="bits">
<label v-for="(bit, bi) in group.bits" :key="bi" class="bit-label">
<input type="checkbox" v-model="bit.on" @change="onBitChange" />
<span :class="['bit-char', bit.char]">{{ bit.char }}</span>
<span class="bit-name">{{ bit.name }}</span>
</label>
</div>
</div>
</div>
<div class="examples">
<div class="ex-title">常见权限组合</div>
<div class="ex-grid">
<div v-for="ex in examples" :key="ex.num" class="ex-item" @click="setPermNum(ex.num)">
<code>{{ ex.num }}</code>
<span class="ex-desc">{{ ex.desc }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, reactive } from 'vue'
const groups = reactive([
{
label: '所有者(Owner',
bits: [
{ char: 'r', name: '读', on: true },
{ char: 'w', name: '写', on: true },
{ char: 'x', name: '执行', on: true }
]
},
{
label: '所属组(Group',
bits: [
{ char: 'r', name: '读', on: true },
{ char: 'w', name: '写', on: false },
{ char: 'x', name: '执行', on: true }
]
},
{
label: '其他人(Others',
bits: [
{ char: 'r', name: '读', on: true },
{ char: 'w', name: '写', on: false },
{ char: 'x', name: '执行', on: true }
]
}
])
const permNum = ref('755')
const permString = computed(() => {
return '-' + groups.map(g =>
g.bits.map(b => b.on ? b.char : '-').join('')
).join('')
})
function bitsToNum() {
return groups.map(g => {
let n = 0
if (g.bits[0].on) n += 4
if (g.bits[1].on) n += 2
if (g.bits[2].on) n += 1
return n
}).join('')
}
function onBitChange() {
permNum.value = bitsToNum()
}
function onNumInput() {
const s = permNum.value.replace(/[^0-7]/g, '').slice(0, 3)
permNum.value = s
if (s.length === 3) {
s.split('').forEach((ch, gi) => {
const n = parseInt(ch)
groups[gi].bits[0].on = !!(n & 4)
groups[gi].bits[1].on = !!(n & 2)
groups[gi].bits[2].on = !!(n & 1)
})
}
}
function setPermNum(num) {
permNum.value = num
onNumInput()
}
const examples = [
{ num: '644', desc: '普通文件(owner 读写,其他只读)' },
{ num: '755', desc: '可执行文件/目录(owner 全权限)' },
{ num: '600', desc: '私密文件(仅 owner 读写)' },
{ num: '777', desc: '完全开放(不推荐)' }
]
</script>
<style scoped>
.linux-perm-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.input-row {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.input-group label {
display: block;
font-size: 0.75rem;
font-weight: 600;
color: var(--vp-c-text-2);
margin-bottom: 0.2rem;
}
.input-group input {
padding: 0.4rem 0.6rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
font-size: 1.1rem;
font-family: var(--vp-font-family-mono);
font-weight: 700;
width: 80px;
text-align: center;
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
}
.perm-string {
font-family: var(--vp-font-family-mono);
font-size: 1.1rem;
font-weight: 700;
color: var(--vp-c-brand);
letter-spacing: 1px;
}
.perm-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
margin-bottom: 1rem;
}
@media (max-width: 480px) {
.perm-grid { grid-template-columns: 1fr; }
}
.perm-group {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 0.6rem;
border: 1px solid var(--vp-c-divider);
}
.group-label {
font-size: 0.75rem;
font-weight: 700;
color: var(--vp-c-text-2);
margin-bottom: 0.4rem;
}
.bits { display: flex; flex-direction: column; gap: 0.25rem; }
.bit-label {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.8rem;
cursor: pointer;
}
.bit-char {
font-family: var(--vp-font-family-mono);
font-weight: 700;
width: 16px;
text-align: center;
}
.bit-char.r { color: #22c55e; }
.bit-char.w { color: #f59e0b; }
.bit-char.x { color: #ef4444; }
.bit-name { color: var(--vp-c-text-3); font-size: 0.75rem; }
.examples {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 0.6rem;
border: 1px solid var(--vp-c-divider);
}
.ex-title {
font-weight: 600;
font-size: 0.78rem;
color: var(--vp-c-text-2);
margin-bottom: 0.4rem;
}
.ex-grid { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.ex-item {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.78rem;
transition: background 0.2s;
}
.ex-item:hover { background: var(--vp-c-bg-soft); }
.ex-item code {
font-weight: 700;
color: var(--vp-c-brand);
}
.ex-desc { color: var(--vp-c-text-3); }
</style>
@@ -0,0 +1,158 @@
<!--
ArchEvolutionDemo.vue
架构演进演示展示从单体到微服务的演进过程
-->
<template>
<div class="arch-evolution-demo">
<div class="header">
<div class="title">架构演进路径</div>
<div class="subtitle">点击查看每个阶段的架构特点</div>
</div>
<div class="stages">
<div
v-for="(stage, i) in stages"
:key="stage.key"
:class="['stage-card', { active: activeStage === stage.key }]"
@click="activeStage = stage.key"
>
<div class="stage-num">{{ i + 1 }}</div>
<div class="stage-name">{{ stage.name }}</div>
</div>
</div>
<div v-if="current" class="stage-detail">
<div class="detail-name">{{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="arch-visual">
<div
v-for="(box, i) in current.boxes"
:key="i"
:class="['arch-box', box.type]"
>
{{ box.label }}
</div>
</div>
<div class="detail-row">
<span class="label">适用规模</span>{{ current.scale }}
</div>
<div class="detail-row">
<span class="label">核心挑战</span>{{ current.challenge }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeStage = ref('monolith')
const stages = [
{
key: 'monolith',
name: '单体架构',
desc: '所有功能打包在一个应用中,共享一个数据库。简单直接,适合早期快速迭代。',
scale: '团队 < 10 人,日活 < 10 万',
challenge: '代码耦合严重,一个模块的 Bug 可能拖垮整个系统',
boxes: [
{ label: '用户模块', type: 'module' },
{ label: '订单模块', type: 'module' },
{ label: '支付模块', type: 'module' },
{ label: '商品模块', type: 'module' },
{ label: '单体应用(一个进程)', type: 'container' },
{ label: 'MySQL', type: 'db' }
]
},
{
key: 'modular',
name: '模块化单体',
desc: '在单体内部按业务域划分模块,模块间通过接口通信。是微服务的前置步骤。',
scale: '团队 10-30 人',
challenge: '模块边界容易被打破,需要纪律性',
boxes: [
{ label: '用户域', type: 'domain' },
{ label: '订单域', type: 'domain' },
{ label: '支付域', type: 'domain' },
{ label: '内部 API 边界', type: 'boundary' },
{ label: 'MySQL', type: 'db' }
]
},
{
key: 'soa',
name: '服务化(SOA',
desc: '按业务能力拆分为独立服务,通过 ESB 或 API 网关通信。每个服务可以独立部署。',
scale: '团队 30-100 人',
challenge: '服务间调用链变长,需要服务治理',
boxes: [
{ label: '用户服务', type: 'service' },
{ label: '订单服务', type: 'service' },
{ label: '支付服务', type: 'service' },
{ label: 'API 网关', type: 'gateway' },
{ label: '各自数据库', type: 'db' }
]
},
{
key: 'microservices',
name: '微服务架构',
desc: '更细粒度的服务拆分,每个服务独立开发、部署、扩缩容。配合容器化和 K8s。',
scale: '团队 100+ 人,日活百万+',
challenge: '分布式复杂性、数据一致性、运维成本',
boxes: [
{ label: '用户服务', type: 'service' },
{ label: '认证服务', type: 'service' },
{ label: '订单服务', type: 'service' },
{ label: '库存服务', type: 'service' },
{ label: '支付服务', type: 'service' },
{ label: '通知服务', type: 'service' },
{ label: 'API Gateway + Service Mesh', type: 'gateway' },
{ label: '独立数据库 x N', type: 'db' }
]
}
]
const current = computed(() => stages.find(s => s.key === activeStage.value))
</script>
<style scoped>
.arch-evolution-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.stages { display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; }
.stage-card {
display: flex; align-items: center; gap: 0.4rem;
padding: 0.4rem 0.75rem; border-radius: 6px; cursor: pointer;
font-size: 0.85rem; background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.stage-card:hover { border-color: var(--vp-c-brand); }
.stage-card.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); font-weight: 600; }
.stage-num {
width: 20px; height: 20px; border-radius: 50%; background: var(--vp-c-brand);
color: white; font-size: 0.7rem; font-weight: 700;
display: flex; align-items: center; justify-content: center;
}
.stage-detail { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); }
.detail-name { font-weight: 700; font-size: 0.95rem; }
.detail-desc { color: var(--vp-c-text-2); font-size: 0.82rem; margin-bottom: 0.75rem; }
.arch-visual { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.75rem; }
.arch-box {
padding: 0.35rem 0.6rem; border-radius: 4px; font-size: 0.72rem; font-weight: 600;
border: 1px dashed var(--vp-c-divider);
}
.arch-box.module { background: rgba(var(--vp-c-brand-rgb), 0.06); }
.arch-box.domain { background: rgba(99,102,241,0.06); }
.arch-box.service { background: rgba(34,197,94,0.06); }
.arch-box.gateway { background: rgba(245,158,11,0.06); width: 100%; text-align: center; }
.arch-box.container { background: rgba(239,68,68,0.06); width: 100%; text-align: center; }
.arch-box.boundary { background: rgba(156,163,175,0.06); width: 100%; text-align: center; }
.arch-box.db { background: rgba(139,92,246,0.06); }
.detail-row { font-size: 0.82rem; margin-bottom: 0.25rem; }
.label { font-weight: 600; color: var(--vp-c-text-2); }
</style>
@@ -0,0 +1,222 @@
<!--
NetworkArchitectureDemo.vue
神经网络架构对比演示
-->
<template>
<div class="net-arch-demo">
<div class="header">
<div class="title">常见神经网络架构</div>
<div class="subtitle">点击查看不同网络架构的特点和应用</div>
</div>
<div class="arch-tabs">
<button
v-for="arch in architectures"
:key="arch.key"
:class="['arch-btn', { active: activeArch === arch.key }]"
@click="activeArch = arch.key"
>
{{ arch.name }}
</button>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-header">
<div class="detail-title">{{ current.name }}{{ current.abbr }}</div>
<div class="detail-year">{{ current.year }}</div>
</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="structure">
<div class="struct-label">网络结构</div>
<div class="struct-visual">
<span v-for="(layer, i) in current.layers" :key="i" class="layer-tag">
{{ layer }}
<span v-if="i < current.layers.length - 1" class="layer-arrow"></span>
</span>
</div>
</div>
<div class="apps">
<div class="apps-label">典型应用</div>
<div class="apps-list">
<span v-for="(app, i) in current.applications" :key="i" class="app-tag">{{ app }}</span>
</div>
</div>
<div class="key-idea">
<span class="idea-label">核心思想</span>{{ current.keyIdea }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeArch = ref('ffn')
const architectures = [
{
key: 'ffn',
name: '前馈神经网络',
abbr: 'FNN',
year: '1958',
desc: '最基础的神经网络结构,数据从输入层经过隐藏层到输出层,单向流动,没有循环。每一层的每个神经元与下一层的所有神经元相连(全连接)。',
layers: ['输入层', '隐藏层 ×N', '输出层'],
applications: ['分类', '回归', '函数逼近'],
keyIdea: '通过多层非线性变换,将输入映射到输出。层数越多,能表达的函数越复杂。'
},
{
key: 'cnn',
name: '卷积神经网络',
abbr: 'CNN',
year: '1998',
desc: '专为处理网格状数据(如图像)设计。通过卷积核在输入上滑动提取局部特征,池化层降低维度,最后全连接层做分类。参数共享大幅减少了参数量。',
layers: ['输入', '卷积层', '池化层', '...', '全连接层', '输出'],
applications: ['图像分类', '目标检测', '人脸识别', '医学影像'],
keyIdea: '局部感受野 + 参数共享。卷积核只关注局部区域,同一个卷积核在整张图上共享参数。'
},
{
key: 'rnn',
name: '循环神经网络',
abbr: 'RNN/LSTM',
year: '1997',
desc: '专为处理序列数据设计。隐藏状态会传递到下一个时间步,让网络具有"记忆"能力。LSTM 通过门控机制解决了长序列中的梯度消失问题。',
layers: ['输入序列', '循环层(含记忆)', '...', '输出序列'],
applications: ['机器翻译', '语音识别', '时间序列预测', '文本生成'],
keyIdea: '引入时间维度的循环连接,让网络能处理变长序列并保持上下文记忆。'
},
{
key: 'transformer',
name: 'Transformer',
abbr: 'Transformer',
year: '2017',
desc: '用自注意力机制替代循环结构,可以并行处理整个序列。每个位置都能直接关注序列中的任意其他位置,解决了 RNN 的长距离依赖问题。是 GPT、BERT 等大模型的基础。',
layers: ['输入嵌入', '位置编码', '多头注意力', '前馈网络', '...×N', '输出'],
applications: ['ChatGPT', 'BERT', '机器翻译', '代码生成', '图像生成'],
keyIdea: '自注意力(Self-Attention):让序列中的每个元素都能"看到"其他所有元素,计算相关性权重。'
},
{
key: 'gan',
name: '生成对抗网络',
abbr: 'GAN',
year: '2014',
desc: '由生成器和判别器两个网络对抗训练。生成器试图生成以假乱真的数据,判别器试图区分真假。两者博弈的结果是生成器越来越强。',
layers: ['随机噪声', '生成器', '生成数据', '判别器', '真/假'],
applications: ['图像生成', '风格迁移', '超分辨率', '数据增强'],
keyIdea: '对抗训练:生成器和判别器互相博弈,共同进步,最终生成器能产生逼真的数据。'
}
]
const current = computed(() => architectures.find(a => a.key === activeArch.value))
</script>
<style scoped>
.net-arch-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.arch-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-bottom: 1rem;
}
.arch-btn {
padding: 0.35rem 0.7rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
color: var(--vp-c-text-2);
transition: all 0.2s;
}
.arch-btn:hover { border-color: var(--vp-c-brand); }
.arch-btn.active {
background: var(--vp-c-brand);
color: #fff;
border-color: var(--vp-c-brand);
}
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.4rem;
}
.detail-title { font-weight: 700; font-size: 0.95rem; }
.detail-year {
font-size: 0.72rem;
padding: 0.1rem 0.4rem;
background: rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.1);
color: var(--vp-c-brand);
border-radius: 4px;
font-weight: 600;
}
.detail-desc {
font-size: 0.82rem;
color: var(--vp-c-text-2);
margin-bottom: 0.75rem;
line-height: 1.6;
}
.structure, .apps {
margin-bottom: 0.5rem;
}
.struct-label, .apps-label {
font-size: 0.72rem;
font-weight: 600;
color: var(--vp-c-text-3);
margin-bottom: 0.3rem;
}
.struct-visual {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.2rem;
}
.layer-tag {
font-size: 0.75rem;
padding: 0.2rem 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
font-weight: 600;
}
.layer-arrow {
color: var(--vp-c-text-3);
margin: 0 0.1rem;
}
.apps-list {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
}
.app-tag {
font-size: 0.72rem;
padding: 0.15rem 0.4rem;
background: rgba(34, 197, 94, 0.1);
color: var(--vp-c-text-1);
border-radius: 4px;
}
.key-idea {
font-size: 0.8rem;
padding: 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
line-height: 1.5;
}
.idea-label { font-weight: 600; color: var(--vp-c-text-2); }
</style>
@@ -0,0 +1,132 @@
<!--
NetworkLayersDemo.vue
神经网络层类型交互演示
-->
<template>
<div class="layers-demo">
<div class="header">
<div class="title">神经网络常见层类型</div>
<div class="subtitle">点击查看各层的作用和参数</div>
</div>
<div class="layer-tabs">
<button v-for="l in layers" :key="l.key"
:class="['tab-btn', { active: activeLayer === l.key }]"
@click="activeLayer = activeLayer === l.key ? null : l.key">
{{ l.name }}
</button>
</div>
<div v-if="current" class="layer-detail">
<div class="detail-name">{{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="detail-section">
<span class="section-label">核心参数</span>
<code v-for="(p, i) in current.params" :key="i" class="param-tag">{{ p }}</code>
</div>
<div class="detail-section">
<span class="section-label">典型用途</span>
<span class="usage-text">{{ current.usage }}</span>
</div>
<div class="detail-code">
<code>{{ current.code }}</code>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeLayer = ref('dense')
const layers = [
{
key: 'dense',
name: '全连接层',
desc: '每个神经元与上一层所有神经元相连。最基础的层类型,用于学习输入特征的组合。',
params: ['units(神经元数)', 'activation(激活函数)'],
usage: '分类、回归任务的输出层,以及简单特征提取',
code: 'Dense(128, activation="relu")'
},
{
key: 'conv',
name: '卷积层',
desc: '用滑动窗口(卷积核)扫描输入,提取局部特征。参数共享大幅减少参数量,是图像处理的核心。',
params: ['filters(卷积核数)', 'kernel_size(核大小)', 'stride(步长)'],
usage: '图像分类、目标检测、图像分割',
code: 'Conv2D(64, kernel_size=3, stride=1, padding=1)'
},
{
key: 'rnn',
name: '循环层',
desc: '具有"记忆"能力,能处理序列数据。每个时间步的输出会作为下一步的输入,形成循环。',
params: ['hidden_size(隐藏维度)', 'num_layers(层数)'],
usage: '文本生成、语音识别、时间序列预测',
code: 'LSTM(hidden_size=256, num_layers=2)'
},
{
key: 'attention',
name: '注意力层',
desc: '让模型学会"关注"输入中最重要的部分。Transformer 的核心,彻底改变了 NLP 领域。',
params: ['embed_dim(嵌入维度)', 'num_heads(注意力头数)'],
usage: 'GPT、BERT 等大语言模型,机器翻译',
code: 'MultiHeadAttention(embed_dim=512, num_heads=8)'
},
{
key: 'norm',
name: '归一化层',
desc: '将数据标准化到合理范围,加速训练收敛,缓解梯度消失/爆炸问题。',
params: ['num_features(特征数)'],
usage: '几乎所有深度网络中都会使用,通常跟在卷积或全连接层后面',
code: 'BatchNorm2d(64) / LayerNorm(512)'
},
{
key: 'dropout',
name: 'Dropout 层',
desc: '训练时随机"关闭"一部分神经元,防止网络过度依赖某些特征,是最常用的正则化手段。',
params: ['p(丢弃概率,通常 0.1~0.5'],
usage: '防止过拟合,提升模型泛化能力',
code: 'Dropout(p=0.3)'
}
]
const current = computed(() => layers.find(l => l.key === activeLayer.value))
</script>
<style scoped>
.layers-demo {
border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
border-radius: 12px; padding: 1.5rem; margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.layer-tabs { display: flex; gap: 0.4rem; margin-bottom: 1rem; flex-wrap: wrap; }
.tab-btn {
padding: 0.35rem 0.7rem; border-radius: 6px; cursor: pointer;
font-size: 0.8rem; font-weight: 600; background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider); transition: all 0.2s;
color: var(--vp-c-text-2);
}
.tab-btn:hover { border-color: var(--vp-c-brand); }
.tab-btn.active { border-color: var(--vp-c-brand); background: rgba(var(--vp-c-brand-rgb), 0.05); color: var(--vp-c-text-1); }
.layer-detail {
background: var(--vp-c-bg); border-radius: 8px; padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-name { font-weight: 700; font-size: 0.95rem; color: var(--vp-c-brand); margin-bottom: 0.3rem; }
.detail-desc { font-size: 0.82rem; color: var(--vp-c-text-2); margin-bottom: 0.6rem; line-height: 1.5; }
.detail-section { font-size: 0.8rem; margin-bottom: 0.4rem; display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.section-label { font-weight: 600; color: var(--vp-c-text-2); }
.param-tag {
background: rgba(var(--vp-c-brand-rgb), 0.08); padding: 0.15rem 0.4rem;
border-radius: 4px; font-size: 0.72rem;
}
.usage-text { color: var(--vp-c-text-2); }
.detail-code {
margin-top: 0.5rem; padding: 0.5rem 0.7rem; background: var(--vp-c-bg-soft);
border-radius: 6px; font-family: var(--vp-font-family-mono); font-size: 0.75rem;
color: var(--vp-c-brand);
}
</style>
@@ -0,0 +1,202 @@
<!--
NeuronDemo.vue
神经元结构演示展示单个神经元的工作原理
-->
<template>
<div class="neuron-demo">
<div class="header">
<div class="title">神经元工作原理</div>
<div class="subtitle">调整输入和权重观察神经元的输出变化</div>
</div>
<div class="neuron-layout">
<div class="inputs-col">
<div class="col-label">输入 × 权重</div>
<div v-for="(inp, i) in inputs" :key="i" class="input-row">
<div class="input-pair">
<label>x{{ i + 1 }}</label>
<input v-model.number="inp.value" type="range" min="-1" max="1" step="0.1" />
<span class="val">{{ inp.value.toFixed(1) }}</span>
</div>
<span class="multiply">×</span>
<div class="input-pair">
<label>w{{ i + 1 }}</label>
<input v-model.number="inp.weight" type="range" min="-2" max="2" step="0.1" />
<span class="val">{{ inp.weight.toFixed(1) }}</span>
</div>
<span class="equals">=</span>
<span class="partial">{{ (inp.value * inp.weight).toFixed(2) }}</span>
</div>
</div>
<div class="output-col">
<div class="sum-box">
<div class="sum-label">加权求和 + 偏置({{ bias.toFixed(1) }})</div>
<div class="sum-value">{{ weightedSum.toFixed(2) }}</div>
</div>
<div class="arrow"></div>
<div class="activation-box">
<div class="act-label">激活函数: {{ activationName }}</div>
<div class="act-value">{{ activationOutput.toFixed(4) }}</div>
</div>
<div class="controls">
<div class="control-row">
<label>偏置 b</label>
<input v-model.number="bias" type="range" min="-2" max="2" step="0.1" />
<span class="val">{{ bias.toFixed(1) }}</span>
</div>
<div class="control-row">
<label>激活函数</label>
<select v-model="activation">
<option value="sigmoid">Sigmoid</option>
<option value="relu">ReLU</option>
<option value="tanh">Tanh</option>
</select>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
const inputs = reactive([
{ value: 0.5, weight: 0.8 },
{ value: -0.3, weight: 1.2 },
{ value: 0.7, weight: -0.5 }
])
const bias = ref(0.1)
const activation = ref('sigmoid')
const activationName = computed(() => {
const names = { sigmoid: 'Sigmoid', relu: 'ReLU', tanh: 'Tanh' }
return names[activation.value]
})
const weightedSum = computed(() => {
return inputs.reduce((sum, inp) => sum + inp.value * inp.weight, 0) + bias.value
})
const activationOutput = computed(() => {
const z = weightedSum.value
switch (activation.value) {
case 'sigmoid': return 1 / (1 + Math.exp(-z))
case 'relu': return Math.max(0, z)
case 'tanh': return Math.tanh(z)
default: return z
}
})
</script>
<style scoped>
.neuron-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.neuron-layout {
display: grid;
grid-template-columns: 1.2fr 1fr;
gap: 1rem;
}
@media (max-width: 640px) {
.neuron-layout { grid-template-columns: 1fr; }
}
.col-label {
font-weight: 600;
font-size: 0.8rem;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
}
.input-row {
display: flex;
align-items: center;
gap: 0.3rem;
margin-bottom: 0.4rem;
font-size: 0.78rem;
}
.input-pair {
display: flex;
align-items: center;
gap: 0.2rem;
}
.input-pair label {
font-weight: 600;
font-size: 0.72rem;
color: var(--vp-c-text-3);
min-width: 18px;
}
.input-pair input[type="range"] { width: 60px; }
.val {
font-family: var(--vp-font-family-mono);
font-size: 0.72rem;
min-width: 28px;
text-align: right;
}
.multiply, .equals {
color: var(--vp-c-text-3);
font-weight: 600;
}
.partial {
font-family: var(--vp-font-family-mono);
font-weight: 600;
color: var(--vp-c-brand);
min-width: 40px;
text-align: right;
}
.output-col {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
}
.sum-box, .activation-box {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 0.5rem 0.8rem;
text-align: center;
width: 100%;
}
.sum-label, .act-label {
font-size: 0.72rem;
color: var(--vp-c-text-3);
}
.sum-value, .act-value {
font-size: 1.1rem;
font-weight: 700;
font-family: var(--vp-font-family-mono);
color: var(--vp-c-brand);
}
.arrow { color: var(--vp-c-text-3); font-size: 1.2rem; }
.controls { width: 100%; margin-top: 0.5rem; }
.control-row {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.78rem;
margin-bottom: 0.3rem;
}
.control-row label {
font-weight: 600;
color: var(--vp-c-text-2);
min-width: 55px;
font-size: 0.72rem;
}
.control-row select {
padding: 0.2rem 0.4rem;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
font-size: 0.78rem;
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
}
</style>
@@ -0,0 +1,183 @@
<!--
CapacityEstimationDemo.vue
容量估算计算器信封背面估算交互演示
-->
<template>
<div class="capacity-demo">
<div class="header">
<div class="title">信封背面估算器</div>
<div class="subtitle">输入基础数据自动计算系统容量需求</div>
</div>
<div class="inputs">
<div class="input-group">
<label>日活用户</label>
<input v-model.number="dau" type="number" min="1" max="100000" />
</div>
<div class="input-group">
<label>人均请求数/</label>
<input v-model.number="reqPerUser" type="number" min="1" max="1000" />
</div>
<div class="input-group">
<label>单次响应大小KB</label>
<input v-model.number="responseSize" type="number" min="0.1" max="1000" />
</div>
<div class="input-group">
<label>峰值系数</label>
<input v-model.number="peakFactor" type="number" min="1" max="10" step="0.5" />
</div>
</div>
<div class="results">
<div class="result-card">
<div class="result-label">日请求量</div>
<div class="result-value">{{ formatNumber(dailyRequests) }}</div>
</div>
<div class="result-card">
<div class="result-label">平均 QPS</div>
<div class="result-value">{{ formatNumber(avgQps) }}</div>
</div>
<div class="result-card">
<div class="result-label">峰值 QPS</div>
<div class="result-value">{{ formatNumber(peakQps) }}</div>
</div>
<div class="result-card">
<div class="result-label">日带宽</div>
<div class="result-value">{{ formatBandwidth(dailyBandwidth) }}</div>
</div>
<div class="result-card">
<div class="result-label">峰值带宽</div>
<div class="result-value">{{ formatBandwidth(peakBandwidthPerSec) }}/s</div>
</div>
</div>
<div class="reference">
<div class="ref-title">常用估算参考值</div>
<div class="ref-grid">
<div class="ref-item" v-for="r in references" :key="r.label">
<span class="ref-label">{{ r.label }}</span>
<span class="ref-value">{{ r.value }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const dau = ref(100)
const reqPerUser = ref(20)
const responseSize = ref(5)
const peakFactor = ref(3)
const dailyRequests = computed(() => dau.value * 10000 * reqPerUser.value)
const avgQps = computed(() => Math.round(dailyRequests.value / 86400))
const peakQps = computed(() => Math.round(avgQps.value * peakFactor.value))
const dailyBandwidth = computed(() => dailyRequests.value * responseSize.value * 1024)
const peakBandwidthPerSec = computed(() => peakQps.value * responseSize.value * 1024)
const references = [
{ label: '1 天', value: '86,400 秒' },
{ label: '1 月', value: '≈ 250 万秒' },
{ label: 'QPS 1000', value: '≈ 1 台 8 核服务器' },
{ label: '1 亿/天', value: '≈ 1,200 QPS' },
{ label: 'MySQL 单机', value: '≈ 5,000 QPS' },
{ label: 'Redis 单机', value: '≈ 100,000 QPS' }
]
function formatNumber(n) {
if (n >= 1e8) return (n / 1e8).toFixed(1) + ' 亿'
if (n >= 1e4) return (n / 1e4).toFixed(1) + ' 万'
return n.toLocaleString()
}
function formatBandwidth(bytes) {
if (bytes >= 1e12) return (bytes / 1e12).toFixed(1) + ' TB'
if (bytes >= 1e9) return (bytes / 1e9).toFixed(1) + ' GB'
if (bytes >= 1e6) return (bytes / 1e6).toFixed(1) + ' MB'
if (bytes >= 1e3) return (bytes / 1e3).toFixed(1) + ' KB'
return bytes + ' B'
}
</script>
<style scoped>
.capacity-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.inputs {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 0.75rem;
margin-bottom: 1rem;
}
.input-group label {
display: block;
font-size: 0.78rem;
font-weight: 600;
color: var(--vp-c-text-2);
margin-bottom: 0.25rem;
}
.input-group input {
width: 100%;
padding: 0.4rem 0.5rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
font-size: 0.85rem;
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
}
.results {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
margin-bottom: 1rem;
}
.result-card {
padding: 0.6rem;
border-radius: 8px;
text-align: center;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
}
.result-label {
font-size: 0.72rem;
color: var(--vp-c-text-3);
}
.result-value {
font-size: 0.95rem;
font-weight: 700;
color: var(--vp-c-brand);
}
.reference {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
.ref-title {
font-weight: 600;
font-size: 0.82rem;
margin-bottom: 0.5rem;
}
.ref-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 0.3rem;
}
.ref-item {
font-size: 0.75rem;
display: flex;
justify-content: space-between;
padding: 0.2rem 0;
}
.ref-label { color: var(--vp-c-text-2); }
.ref-value { font-weight: 600; font-family: var(--vp-font-family-mono); }
</style>
@@ -0,0 +1,199 @@
<!--
SystemDesignStepsDemo.vue
系统设计步骤交互演示展示系统设计面试/实战的标准流程
-->
<template>
<div class="design-steps-demo">
<div class="header">
<div class="title">系统设计四步法</div>
<div class="subtitle">点击每个步骤查看详细内容</div>
</div>
<div class="steps">
<div
v-for="(step, i) in steps"
:key="step.key"
:class="['step-card', { active: activeStep === step.key }]"
@click="activeStep = step.key"
>
<div class="step-number">{{ i + 1 }}</div>
<div class="step-name">{{ step.name }}</div>
<div class="step-time">{{ step.time }}</div>
</div>
</div>
<div v-if="current" class="detail-panel">
<div class="detail-title">{{ current.name }}</div>
<div class="detail-desc">{{ current.desc }}</div>
<div class="checklist">
<div v-for="(item, i) in current.checklist" :key="i" class="check-item">
<span class="check-icon"></span>
<span>{{ item }}</span>
</div>
</div>
<div class="detail-example">
<span class="label">示例设计短链服务</span>
<div class="example-text">{{ current.example }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeStep = ref('requirements')
const steps = [
{
key: 'requirements',
name: '需求澄清',
time: '~5 分钟',
desc: '不要急着画架构图。先搞清楚:系统要解决什么问题?用户规模多大?有哪些核心功能?有哪些非功能需求?',
checklist: [
'核心功能有哪些?(MVP 范围)',
'用户规模?DAU / QPS 预估',
'读写比例?读多写少还是写多读少?',
'数据量级?需要存多少数据?',
'可用性要求?几个 9',
'延迟要求?P99 要多少毫秒?'
],
example: '短链服务:生成短链(写)+ 重定向(读),读写比约 100:1,日均 1 亿次重定向,短链永不过期。'
},
{
key: 'estimation',
name: '容量估算',
time: '~5 分钟',
desc: '用"信封背面估算"Back-of-envelope estimation)快速计算系统需要的资源量级,为后续架构决策提供数据支撑。',
checklist: [
'QPS 估算:日请求量 / 86400',
'存储估算:单条数据大小 × 总量',
'带宽估算:QPS × 单次响应大小',
'缓存估算:热点数据量(通常 20% 数据承载 80% 请求)',
'峰值估算:平均 QPS × 峰值系数(通常 2-5 倍)'
],
example: '1 亿次/天 ≈ 1200 QPS,峰值 ≈ 3600 QPS。每条短链 100 字节,5 年 = 1.8 亿条 ≈ 18GB。缓存热点 20% ≈ 3.6GB,一台 Redis 足够。'
},
{
key: 'design',
name: '架构设计',
time: '~15 分钟',
desc: '画出核心组件和数据流。先画最简单的版本(单机),再根据需求逐步演进(加缓存、分库分表、CDN 等)。',
checklist: [
'API 设计:定义核心接口的输入输出',
'数据模型:设计核心表结构',
'核心组件:Web 服务、数据库、缓存、消息队列',
'数据流:请求从用户到数据库的完整路径',
'读写分离:读路径和写路径分开考虑'
],
example: '写路径:客户端 → API → 生成短码(Base62 → 写入 MySQL + Redis。读路径:客户端 → CDN → API → Redis 查询 → 302 重定向。'
},
{
key: 'deep-dive',
name: '深入优化',
time: '~10 分钟',
desc: '针对系统的瓶颈和关键问题进行深入讨论。这是展示技术深度的环节。',
checklist: [
'如何保证短码唯一性?(哈希冲突处理)',
'如何应对热点?(缓存、CDN',
'如何水平扩展?(分库分表策略)',
'如何保证高可用?(主备、多可用区)',
'如何监控和告警?(关键指标)',
'安全考虑?(防刷、恶意链接检测)'
],
example: '短码生成:用分布式 ID 生成器(Snowflake)+ Base62 编码,避免哈希冲突。热点短链用多级缓存(本地缓存 + Redis + CDN)。'
}
]
const current = computed(() => steps.find(s => s.key === activeStep.value))
</script>
<style scoped>
.design-steps-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.steps {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
margin-bottom: 1rem;
}
@media (max-width: 640px) {
.steps { grid-template-columns: repeat(2, 1fr); }
}
.step-card {
padding: 0.6rem;
border-radius: 8px;
cursor: pointer;
text-align: center;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
transition: all 0.2s;
}
.step-card:hover { border-color: var(--vp-c-brand); }
.step-card.active {
border-color: var(--vp-c-brand);
background: rgba(var(--vp-c-brand-rgb), 0.05);
}
.step-number {
font-size: 1.2rem;
font-weight: 800;
color: var(--vp-c-brand);
}
.step-name { font-weight: 600; font-size: 0.85rem; }
.step-time {
font-size: 0.72rem;
color: var(--vp-c-text-3);
}
.detail-panel {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.detail-title {
font-weight: 700;
font-size: 0.95rem;
margin-bottom: 0.4rem;
}
.detail-desc {
font-size: 0.82rem;
color: var(--vp-c-text-2);
margin-bottom: 0.75rem;
}
.checklist {
display: flex;
flex-direction: column;
gap: 0.3rem;
margin-bottom: 0.75rem;
}
.check-item {
font-size: 0.8rem;
display: flex;
gap: 0.4rem;
align-items: flex-start;
}
.check-icon {
color: var(--vp-c-brand);
font-weight: 700;
flex-shrink: 0;
}
.detail-example {
font-size: 0.82rem;
padding: 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.example-text {
color: var(--vp-c-text-2);
margin-top: 0.25rem;
}
.label { font-weight: 600; color: var(--vp-c-text-2); }
</style>
+136 -44
View File
@@ -206,15 +206,15 @@ import LearningStrategyDemo from './components/appendix/computer-fundamentals/Le
import VibeCodingFlowDemo from './components/appendix/computer-fundamentals/VibeCodingFlowDemo.vue'
import PowerOnDemo from './components/appendix/computer-fundamentals/PowerOnDemo.vue'
import BootProcessDemo from './components/appendix/computer-fundamentals/BootProcessDemo.vue'
import BiosUefiDemo from './components/appendix/computer-fundamentals/BiosUefiDemo.vue'
import BiosUefiInteractiveDemo from './components/appendix/computer-fundamentals/BiosUefiInteractiveDemo.vue'
import AppLaunchDemo from './components/appendix/computer-fundamentals/AppLaunchDemo.vue'
import DesktopDemo from './components/appendix/computer-fundamentals/DesktopDemo.vue'
import OSBootInteractiveDemo from './components/appendix/computer-fundamentals/OSBootInteractiveDemo.vue'
import BrowserArchitectureDemo from './components/appendix/computer-fundamentals/BrowserArchitectureDemo.vue'
import URLRequestDemo from './components/appendix/computer-fundamentals/URLRequestDemo.vue'
import RenderingDemo from './components/appendix/computer-fundamentals/RenderingDemo.vue'
import FullProcessDemo from './components/appendix/computer-fundamentals/FullProcessDemo.vue'
// import BiosUefiDemo from './components/appendix/computer-fundamentals/BiosUefiDemo.vue'
// import BiosUefiInteractiveDemo from './components/appendix/computer-fundamentals/BiosUefiInteractiveDemo.vue'
// import AppLaunchDemo from './components/appendix/computer-fundamentals/AppLaunchDemo.vue'
// import DesktopDemo from './components/appendix/computer-fundamentals/DesktopDemo.vue'
// import OSBootInteractiveDemo from './components/appendix/computer-fundamentals/OSBootInteractiveDemo.vue'
// import BrowserArchitectureDemo from './components/appendix/computer-fundamentals/BrowserArchitectureDemo.vue'
// import URLRequestDemo from './components/appendix/computer-fundamentals/URLRequestDemo.vue'
// import RenderingDemo from './components/appendix/computer-fundamentals/RenderingDemo.vue'
// import FullProcessDemo from './components/appendix/computer-fundamentals/FullProcessDemo.vue'
// Data Encoding Components
import GarbledTextDemo from './components/appendix/data-encoding/GarbledTextDemo.vue'
@@ -762,27 +762,73 @@ import IncidentCommandDemo from './components/appendix/incident-response/Inciden
import AlertEscalationDemo from './components/appendix/incident-response/AlertEscalationDemo.vue'
import PostmortemDemo from './components/appendix/incident-response/PostmortemDemo.vue'
// Async Task Queues Components
import AsyncTaskFlowDemo from './components/appendix/async-task-queues/AsyncTaskFlowDemo.vue'
import TaskWorkerDemo from './components/appendix/async-task-queues/TaskWorkerDemo.vue'
import TaskRetryDemo from './components/appendix/async-task-queues/TaskRetryDemo.vue'
import AsyncComparisonDemo from './components/appendix/async-task-queues/AsyncComparisonDemo.vue'
// // Async Task Queues Components
// import AsyncTaskFlowDemo from './components/appendix/async-task-queues/AsyncTaskFlowDemo.vue'
// import TaskWorkerDemo from './components/appendix/async-task-queues/TaskWorkerDemo.vue'
// import TaskRetryDemo from './components/appendix/async-task-queues/TaskRetryDemo.vue'
// import AsyncComparisonDemo from './components/appendix/async-task-queues/AsyncComparisonDemo.vue'
// File Storage Components
import FileStorageTypeDemo from './components/appendix/file-storage/FileStorageTypeDemo.vue'
import FileUploadFlowDemo from './components/appendix/file-storage/FileUploadFlowDemo.vue'
import CDNAccelerationDemo from './components/appendix/file-storage/CDNAccelerationDemo.vue'
// // File Storage Components
// import FileStorageTypeDemo from './components/appendix/file-storage/FileStorageTypeDemo.vue'
// import FileUploadFlowDemo from './components/appendix/file-storage/FileUploadFlowDemo.vue'
// import CDNAccelerationDemo from './components/appendix/file-storage/CDNAccelerationDemo.vue'
// Rate Limiting Components
import RateLimitAlgorithmDemo from './components/appendix/rate-limiting/RateLimitAlgorithmDemo.vue'
import BackpressureDemo from './components/appendix/rate-limiting/BackpressureDemo.vue'
// // Rate Limiting Components
// import RateLimitAlgorithmDemo from './components/appendix/rate-limiting/RateLimitAlgorithmDemo.vue'
// import BackpressureDemo from './components/appendix/rate-limiting/BackpressureDemo.vue'
// Search Engines Components Registration
import InvertedIndexDemo from './components/appendix/search-engines/InvertedIndexDemo.vue'
import SearchRelevanceDemo from './components/appendix/search-engines/SearchRelevanceDemo.vue'
// Monolith to Microservices Components
import ArchEvolutionDemo from './components/appendix/monolith-to-microservices/ArchEvolutionDemo.vue'
// High Availability Components
import AvailabilityCalculatorDemo from './components/appendix/high-availability/AvailabilityCalculatorDemo.vue'
import FailoverStrategyDemo from './components/appendix/high-availability/FailoverStrategyDemo.vue'
// Distributed Systems Components
import CAPTheoremDemo from './components/appendix/distributed-systems/CAPTheoremDemo.vue'
import ConsistencyModelsDemo from './components/appendix/distributed-systems/ConsistencyModelsDemo.vue'
import DistributedChallengesDemo from './components/appendix/distributed-systems/DistributedChallengesDemo.vue'
// System Design Methodology Components
import SystemDesignStepsDemo from './components/appendix/system-design-methodology/SystemDesignStepsDemo.vue'
import CapacityEstimationDemo from './components/appendix/system-design-methodology/CapacityEstimationDemo.vue'
// Data Visualization Components
import ChartTypeSelectorDemo from './components/appendix/data-visualization/ChartTypeSelectorDemo.vue'
import DashboardLayoutDemo from './components/appendix/data-visualization/DashboardLayoutDemo.vue'
// Data Governance Components
import DataQualityDemo from './components/appendix/data-governance/DataQualityDemo.vue'
import DataGovernanceFrameworkDemo from './components/appendix/data-governance/DataGovernanceFrameworkDemo.vue'
import DataLineageDemo from './components/appendix/data-governance/DataLineageDemo.vue'
// Linux Basics Components
import LinuxFileSystemDemo from './components/appendix/linux-basics/LinuxFileSystemDemo.vue'
import LinuxCommandDemo from './components/appendix/linux-basics/LinuxCommandDemo.vue'
import LinuxPermissionsDemo from './components/appendix/linux-basics/LinuxPermissionsDemo.vue'
// Docker Containers Components
import DockerArchitectureDemo from './components/appendix/docker-containers/DockerArchitectureDemo.vue'
import DockerLifecycleDemo from './components/appendix/docker-containers/DockerLifecycleDemo.vue'
// Kubernetes Components
import K8sArchitectureDemo from './components/appendix/kubernetes/K8sArchitectureDemo.vue'
import K8sWorkloadsDemo from './components/appendix/kubernetes/K8sWorkloadsDemo.vue'
// Neural Networks Components
import NeuronDemo from './components/appendix/neural-networks/NeuronDemo.vue'
import NetworkLayersDemo from './components/appendix/neural-networks/NetworkLayersDemo.vue'
import NetworkArchitectureDemo from './components/appendix/neural-networks/NetworkArchitectureDemo.vue'
// Project Architecture Components
import ArchitectureComparisonDemo from './components/appendix/project-architecture/ArchitectureComparisonDemo.vue'
import ProjectArchitectureComparisonDemo from './components/appendix/project-architecture/ArchitectureComparisonDemo.vue'
// Appendix Navigation Component
import AppendixFlowMap from './components/AppendixFlowMap.vue'
export default {
extends: DefaultTheme,
@@ -995,15 +1041,15 @@ export default {
app.component('VibeCodingFlowDemo', VibeCodingFlowDemo)
app.component('PowerOnDemo', PowerOnDemo)
app.component('BootProcessDemo', BootProcessDemo)
app.component('BiosUefiDemo', BiosUefiDemo)
app.component('BiosUefiInteractiveDemo', BiosUefiInteractiveDemo)
app.component('AppLaunchDemo', AppLaunchDemo)
app.component('DesktopDemo', DesktopDemo)
app.component('OSBootInteractiveDemo', OSBootInteractiveDemo)
app.component('BrowserArchitectureDemo', BrowserArchitectureDemo)
app.component('URLRequestDemo', URLRequestDemo)
app.component('RenderingDemo', RenderingDemo)
app.component('FullProcessDemo', FullProcessDemo)
// app.component('BiosUefiDemo', BiosUefiDemo)
// app.component('BiosUefiInteractiveDemo', BiosUefiInteractiveDemo)
// app.component('AppLaunchDemo', AppLaunchDemo)
// app.component('DesktopDemo', DesktopDemo)
// app.component('OSBootInteractiveDemo', OSBootInteractiveDemo)
// app.component('BrowserArchitectureDemo', BrowserArchitectureDemo)
// app.component('URLRequestDemo', URLRequestDemo)
// app.component('RenderingDemo', RenderingDemo)
// app.component('FullProcessDemo', FullProcessDemo)
// Data Encoding Components Registration
app.component('GarbledTextDemo', GarbledTextDemo)
@@ -1564,27 +1610,73 @@ export default {
app.component('AlertEscalationDemo', AlertEscalationDemo)
app.component('PostmortemDemo', PostmortemDemo)
// Async Task Queues Components Registration
app.component('AsyncTaskFlowDemo', AsyncTaskFlowDemo)
app.component('TaskWorkerDemo', TaskWorkerDemo)
app.component('TaskRetryDemo', TaskRetryDemo)
app.component('AsyncComparisonDemo', AsyncComparisonDemo)
// // Async Task Queues Components Registration
// app.component('AsyncTaskFlowDemo', AsyncTaskFlowDemo)
// app.component('TaskWorkerDemo', TaskWorkerDemo)
// app.component('TaskRetryDemo', TaskRetryDemo)
// app.component('AsyncComparisonDemo', AsyncComparisonDemo)
// File Storage Components Registration
app.component('FileStorageTypeDemo', FileStorageTypeDemo)
app.component('FileUploadFlowDemo', FileUploadFlowDemo)
app.component('CDNAccelerationDemo', CDNAccelerationDemo)
// // File Storage Components Registration
// app.component('FileStorageTypeDemo', FileStorageTypeDemo)
// app.component('FileUploadFlowDemo', FileUploadFlowDemo)
// app.component('CDNAccelerationDemo', CDNAccelerationDemo)
// Rate Limiting Components Registration
app.component('RateLimitAlgorithmDemo', RateLimitAlgorithmDemo)
app.component('BackpressureDemo', BackpressureDemo)
// // Rate Limiting Components Registration
// app.component('RateLimitAlgorithmDemo', RateLimitAlgorithmDemo)
// app.component('BackpressureDemo', BackpressureDemo)
// Search Engines Components Registration
app.component('InvertedIndexDemo', InvertedIndexDemo)
app.component('SearchRelevanceDemo', SearchRelevanceDemo)
// Data Visualization Components Registration
app.component('ChartTypeSelectorDemo', ChartTypeSelectorDemo)
app.component('DashboardLayoutDemo', DashboardLayoutDemo)
// Data Governance Components Registration
app.component('DataQualityDemo', DataQualityDemo)
app.component('DataGovernanceFrameworkDemo', DataGovernanceFrameworkDemo)
app.component('DataLineageDemo', DataLineageDemo)
// Distributed Systems Components Registration
app.component('CAPTheoremDemo', CAPTheoremDemo)
app.component('ConsistencyModelsDemo', ConsistencyModelsDemo)
app.component('DistributedChallengesDemo', DistributedChallengesDemo)
// High Availability Components Registration
app.component('AvailabilityCalculatorDemo', AvailabilityCalculatorDemo)
app.component('FailoverStrategyDemo', FailoverStrategyDemo)
// Monolith to Microservices Components Registration
app.component('ArchEvolutionDemo', ArchEvolutionDemo)
// System Design Methodology Components Registration
app.component('SystemDesignStepsDemo', SystemDesignStepsDemo)
app.component('CapacityEstimationDemo', CapacityEstimationDemo)
// Docker Containers Components Registration
app.component('DockerArchitectureDemo', DockerArchitectureDemo)
app.component('DockerLifecycleDemo', DockerLifecycleDemo)
// Linux Basics Components Registration
app.component('LinuxFileSystemDemo', LinuxFileSystemDemo)
app.component('LinuxCommandDemo', LinuxCommandDemo)
app.component('LinuxPermissionsDemo', LinuxPermissionsDemo)
// Kubernetes Components Registration
app.component('K8sArchitectureDemo', K8sArchitectureDemo)
app.component('K8sWorkloadsDemo', K8sWorkloadsDemo)
// Neural Networks Components Registration
app.component('NeuronDemo', NeuronDemo)
app.component('NetworkLayersDemo', NetworkLayersDemo)
app.component('NetworkArchitectureDemo', NetworkArchitectureDemo)
// Project Architecture Components Registration
app.component('ArchitectureComparisonDemo', ArchitectureComparisonDemo)
app.component('ProjectArchitectureComparisonDemo', ProjectArchitectureComparisonDemo)
// Appendix Navigation Component Registration
app.component('AppendixFlowMap', AppendixFlowMap)
},
setup() {
const route = useRoute()
-66
View File
@@ -1,66 +0,0 @@
# Novice & Product Prototype
Welcome to the **AI Product Manager** stage! This is the starting point of the Easy-Vibe tutorial, designed for learners with zero programming background.
## What You Will Learn
In this stage, you will start from scratch and master the Vibe Coding workflow to become a super individual capable of independent product design.
### Getting Started
Suitable for product managers, operations staff, and non-technical backgrounds. Understand AI programming logic through games and build confidence:
<NavGrid>
<NavCard
href="/en-us/stage-0/0.1-learning-map/"
title="Learning Map"
description="Understand the entire learning path and clarify the goals and outcomes of each stage"
/>
<NavCard
href="/en-us/stage-0/0.2-ai-capabilities-through-games/"
title="AI Era: If You Can Speak, You Can Code"
description="Experience the charm of AI programming through games like Snake, breaking the fear of coding"
/>
</NavGrid>
### Product Manager
Master the Vibe Coding workflow. Learn to break down requirements and independently complete high-fidelity web application prototypes:
<NavGrid>
<NavCard
href="/en-us/stage-1/1.1-introduction-to-ai-ide/"
title="Introduction to AI IDE Tools"
description="Learn about current mainstream AI programming tools and choose the best development partner for you"
/>
<NavCard
href="/en-us/stage-1/1.2-building-prototype/"
title="Building a Prototype"
description="Learn how to quickly transform product ideas into visual prototypes for low-cost trial and error"
/>
<NavCard
href="/en-us/stage-1/1.3-integrating-ai-capabilities/"
title="Integrating AI Capabilities"
description="Make your prototype intelligent by integrating simple AI APIs"
/>
<NavCard
href="/en-us/stage-1/1.4-complete-project-practice/"
title="Complete Project Practice"
description="Comprehensively apply what you've learned to complete a full product prototype development from 0 to 1"
/>
</NavGrid>
## Who Is This For
- Product managers and operations staff with zero programming background
- Entrepreneurs who want to quickly validate ideas
- Non-technical people interested in AI programming
- Designers looking to enhance their prototyping skills
## Learning Path
```
Getting Started → Product Manager Basics → AI Capability Integration → Complete Project Practice
```
Ready to start your AI programming journey? Click the left navigation to begin learning!
+2 -2
View File
@@ -17,10 +17,10 @@ hero:
actions:
- theme: brand
text: Start Learning
link: /en-us/stage-0/
link: /en/stage-0/
- theme: alt
text: Course Outline
link: /en-us/stage-0/
link: /en/stage-0/
---
<HomeFeatures />

Before

Width:  |  Height:  |  Size: 374 KiB

After

Width:  |  Height:  |  Size: 374 KiB

+259
View File
@@ -0,0 +1,259 @@
---
title: 'From Idea to AI Product - Easy-Vibe Learning Roadmap'
description: 'Complete roadmap for learning AI programming: from zero basics to full-stack development. Master AI IDE tools like Vibe Coding, Claude Code, and Cursor, and learn product thinking, full-stack development, and AI capability integration.'
---
# From Idea to AI Product
In the past, building software had a high barrier: you had to understand programming and algorithms and have years of project experience.
Now it's different. As long as you have an idea, AI can help you write the code.
This is a huge change: **Programming languages are becoming natural languages**.
The emergence of Large Language Models (LLMs) has turned development from a "technical expert's exclusive" into a tool everyone can use. What used to be the hardest part—"how to write code"—is now replaced by the new hardest part: "**What do you want to do?**"
> **What is Vibe Coding?**
> Simply put, it's "programming by speaking." Vibe coding means you can rely solely on conversing with AI instead of writing code directly to complete a programming project.
Of course, letting AI write code is just the first step. To make a truly usable product, you will still encounter these questions:
- How to let AI write clean, maintainable code?
- How to piece together scattered code into a runnable application?
- How to make the application truly go live and be used by people?
- How to put AI capabilities like text generation and image recognition into your product?
These questions will find answers in this course.
Whether you are a student, teacher, doctor, worker, or any common person who knows nothing about technology—you don't need to learn programming for years first; in two weeks, you can make a runnable, demonstrable product prototype.
| Your Identity | This Course Can Help You |
|---------|-------------|
| Student | Assignments, competitions, entrepreneurship; do projects yourself without asking for help |
| Professional | Automate repetitive work, improve efficiency, and even develop side hustles |
| Product Manager / Designer | Ideas no longer stay on paper; quickly make Demos to show bosses/clients |
| Entrepreneur / SME Owner | Validate ideas at low cost; make an MVP without spending tens of thousands on outsourcing |
| Teacher / Educator | Make teaching tools, courseware, and automated questions to improve teaching efficiency |
| Doctor / Lawyer / Professional | Automate professional processes and build your own efficiency tools |
| Anyone | Use AI to solve specific problems in life/work, making the impossible possible |
In the AI era, execution and ideas are always more important than technology.
## Growth Path: From "Using AI" to "Making AI Products"
<div class="stage-intro">
<div class="stage-card">
<div class="stage-icon">🎮</div>
<h3>Getting Started</h3>
<p class="stage-role">Experience AI Programming</p>
<div class="stage-tags">
<span>Snake Mini-game</span>
<span>Zero Basics to Start</span>
<span>Vibe Coding First Experience</span>
<span>Generate in Minutes</span>
</div>
</div>
</div>
<div class="stage-grid">
<div class="stage-card">
<div class="stage-icon">🛠️</div>
<h3>Stage One</h3>
<p class="stage-role">Product Manager / Operations</p>
<div class="stage-tags">
<span>AI IDE (Cursor/Claude)</span>
<span>Requirement Deconstruction & Prototype</span>
<span>Integrate AI Capabilities</span>
<span>Full Demo Development</span>
</div>
</div>
<div class="stage-card">
<div class="stage-icon">💻</div>
<h3>Stage Two</h3>
<p class="stage-role">Junior-Mid Developer / Indie Dev</p>
<div class="stage-tags">
<span>Figma to Code</span>
<span>Supabase Database</span>
<span>Stripe Payment Integration</span>
<span>Dify Knowledge Base</span>
</div>
</div>
<div class="stage-card">
<div class="stage-icon">🚀</div>
<h3>Stage Three</h3>
<p class="stage-role">Senior Developer / Architect</p>
<div class="stage-tags">
<span>Web/Mini-program/Multi-platform</span>
<span>MCP Advanced Tools</span>
<span>RAG & LangGraph</span>
<span>Senior Engineer Thinking</span>
</div>
</div>
</div>
<style>
.stage-intro {
margin: 20px auto;
max-width: 400px;
}
.stage-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 12px;
margin: 16px 0;
}
.stage-card {
border: 1px solid var(--vp-c-divider);
border-radius: 10px;
padding: 12px;
background-color: var(--vp-c-bg-soft);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
height: 100%;
}
.stage-card:hover {
transform: translateY(-2px);
background-color: var(--vp-c-bg-mute);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
border-color: var(--vp-c-brand);
}
.stage-icon {
font-size: 2rem;
margin-bottom: 8px;
line-height: 1;
}
.stage-card h3 {
margin: 0 0 4px 0 !important;
font-size: 1rem;
font-weight: 600;
line-height: 1.2;
}
.stage-role {
margin: 0 0 8px 0 !important;
font-size: 0.8rem;
color: var(--vp-c-text-2);
font-weight: 500;
}
.stage-tags {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 4px;
}
.stage-tags span {
font-size: 0.7rem;
padding: 1px 6px;
border-radius: 3px;
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-text-2);
border: 1px solid var(--vp-c-divider);
}
.stage-card:hover .stage-tags span {
background-color: var(--vp-c-bg);
border-color: var(--vp-c-brand-dimm);
color: var(--vp-c-brand-dark);
}
</style>
Through this complete learning path, you will gain:
- **Vibe Coding Development Ability:** Effortlessly use vibe coding thinking and AI coding tools to increase development efficiency several times. No longer need to memorize syntax, but learn how to guide AI to generate high-quality code.
- **Full-stack Development Skills:** From UI design to front-end implementation, from database design to API development, and from local development to cloud deployment, master the full technology stack of modern Web applications.
- **AI Capability Integration:** Learn to call various multimodal AI APIs and seamlessly integrate AI capabilities like text, images, and voice into your applications, building intelligent products through technologies like RAG.
- **Product Thinking and Operations Ability:** From user research to demand deconstruction, from MVP design to product iteration, and from payment integration to user management, form a complete product development and operation closed loop.
# What Can You Do After Learning?
## Stage One: Build Your First Product Prototype
This stage is suitable for students with zero programming foundation or those who only know a little but are not confident. You don't need to learn a lot of theoretical knowledge first, but follow the steps directly and learn how to use AI tools to write code in the process.
**After learning, you can**:
- Independently complete a web application using AI programming tools
- Turn product ideas into clickable, interactive prototypes
- Add AI functions to the prototype (e.g., text-to-image, intelligent dialogue)
- Know how to troubleshoot and solve problems when encountering errors
Simply put, you can make something "runnable and demonstrable to others."
We can first experience AI programming through mini-games, then learn how to use AI programming tools to help you write code and fix errors. Then start from simple pages and gradually make interactive multi-page applications, adding AI functions like text-to-image and intelligent dialogue. Finally, independently complete a full project so that your creativity can truly have the possibility of landing.
# Why Use Project-Based Training?
> **Real-world Challenges**
>
> The reason is simple: based on the state of most students now, directly entering the workplace might make it difficult to move an inch under the "social beatings" of real projects and bosses/clients. More common scenarios in the real world are:
> Your mentor / boss: We want to do xxx, the goal is to achieve yyy effect.
>
> Documentation? Ready-made frameworks? Detailed requirement specifications? Often they don't exist.
Many tasks in real work are essentially solving problems never seen before in a highly uncertain environment: requirements are vague, boundaries are changing, no one tells you the standard answer, and you need to look up information, do experiments, build prototypes, iterate continuously, and finally give a "runnable, usable, and launchable" solution.
What this course wants to do is give you a "simulated social beating" in advance in a relatively safe environment:
- Force you to practice deconstructing problems, designing solutions, and finding information yourself through seemingly difficult project tasks.
- Allow you to learn to read, understand, and transform a medium-to-large codebase through scaffolds and code that are not so "idiot-proof."
- Let you experience the complete process of a real product from 0 to 1 through the complete closed loop from idea to launch.
In the short term, this kind of training is indeed torturous; but in the long term, it will greatly improve your competitiveness in job searching and career development: you will be more able to handle things, more able to find breakthroughs in uncertain environments, and more capable of turning AI into real landing products instead of staying in the "playing with demos" stage.
# The Art of Questioning: An Essential Skill in the AI Era
In the AI era, questioning is also a "basic skill." For the same code and the same error, **how you ask almost determines what kind of answer AI can give**: whether it's talking broadly or giving implementable modifications step by step.
**Develop Good Habits**: Treat "asking AI" as part of the daily development flow: ask immediately when you don't understand or get stuck.
## Why is this an Essential Skill?
- **Real life rarely has complete documentation**: Most of the time you face unclear requirements, half-finished code, and scattered error messages.
- **AI is your tutor + colleague by your side**: Those who can ask questions can turn it into "high-quality pair programming."
- **Ability upper limit is determined by communication**: The more you can provide key information and the more you can constrain the output format, the more usable the answer will be.
**Common Misconception**: Asking just "Why error?" usually only gets a bunch of guesses. Only by filling in the context will you get an executable solution.
## How to "Feed" Information to AI: Screenshots vs Copy-Paste
Both methods are fine, but for different purposes:
| Method | Applicable Scenarios | Key Requirements |
| ------------ | ----------------------------------------- | ----------------------------------------- |
| **Copy-Paste** | Error stacks, logs, code, configuration, API returns | Be as complete as possible; don't just take one line of keywords |
| **Screenshot** | UI layout issues, interaction anomalies, can't find buttons in tools | Screenshot full screen + highlight key areas, preferably with a line of text description |
::: danger ⚠️ Important Prerequisite
**Not all AI support image input.** Communication via screenshots requires AI to have multimodal capabilities (i.e., the ability to understand and analyze images). Current AIs that support image input include: Claude (Anthropic), GPT-4V/GPT-4o (OpenAI), Gemini (Google), and some Chinese models like Tongyi Qianwen, Wenxin Yiyan, etc.
**If the AI you are using does not support image input**, screenshots will not be recognized. In this case, please switch to copy-pasting text for communication.
:::
## Prompt Tips to Make AI "Explain Well"
If you don't just want the answer, but want to "learn" the answer. Using instructions like the following can significantly improve the quality of explanation:
> **Learning Question Examples**
>
> - "Please explain this concept clearly in 5 sentences first, and then ask me a few questions to verify if I understood it correctly."
> - "Please explain this error message in detail; I don't understand why the error occurred."
# I've been persistent for a long time but still can't handle it, I want to give up
Maybe your method of persistence is wrong. Don't hold on alone in the dark; you can come and talk to the authors and teaching assistants: frankly state the methods you have tried, the specific stuck points you encountered, and your current state of mind. Many times, just a slight adjustment in direction or adding a key knowledge point can keep you moving forward.
# I feel some designs of the tutorial are unreasonable
You are welcome to contact the author at any time, submit an issue, or give feedback directly in the group/class. We very much hope to work with you to polish this set of tutorials to be better and better: wherever it's unclear, wherever the experience is broad, or wherever it makes you waste effort, you can point it out frankly. The more real and specific the feedback, the more it can help newcomers avoid pitfalls.
# Reference
- [Nanjing University Computer Science and Technology Department Computer System Fundamentals Course Experiment](https://nju-projectn.github.io/ics-pa-gitbook/ics2025/)
@@ -0,0 +1,743 @@
# Primary 1: AI Era, If You Can Speak, You Can Code
This is a **project-based learning** tutorial. We encourage you to follow the steps one by one and try to reproduce the results.
Don't worry about making mistakes or modifying the content. We always believe you can do it. Please always remember:
<div style="text-align: center;">
<div style="display: inline-block; padding: 8px 20px; border-radius: 8px; border: 1px dashed #FFB6C1; background: linear-gradient(135deg, #FFF0F5 0%, #FFE4EC 100%); margin: 12px 0;">
<span style="font-size: 15px; font-weight: 500; color: #666;">Completion is more important than perfection 🐣</span>
</div>
</div>
<script setup>
const duration = 'Approx. <strong>4 hours</strong>, can be completed in multiple sessions'
</script>
## Chapter Outline
<ChapterIntroduction :duration="duration" :tags="['Conversational AI Programming', 'AI-Native Mini-Games', 'Snake Game Practice']" coreOutput="AI-Native Snake + Custom Mini-Game" expectedOutput="1 playable AI-native Snake game + (Optional) 1 custom AI-native mini-game or Demo of your choice">
If you <strong>don't know how to program at all</strong>, or only know the basics, this chapter is for you. We will start from the very beginning: using <strong>conversations</strong> to have AI write code for you, without needing to memorize syntax or set up environments. It will run right in your browser.
You will personally create <strong>your first running program</strong>—a Snake game that can "eat words, write poems, and draw". Through this practical exercise, you will experience what AI programming is really like: AI is not replacing your thinking, but rather, you speak your ideas, and AI helps you implement them.
All creation starts from 0 to 1. We are glad to pass each bit of confidence and professionalism to you. For you, <strong>execution is all you need</strong>.
</ChapterIntroduction>
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="0" :items="[
{ title: 'Dilemmas & Opportunities', description: 'New possibilities for coding' },
{ title: 'Capability Exploration', description: '60-second speed development' },
{ title: 'Native Practice', description: 'Build an AI-native Snake' },
{ title: 'Extended Creation', description: 'Create other games' }
]" />
</ClientOnly>
</div>
## 1. Dilemmas and Opportunities for Ordinary People
Many people have a bunch of product ideas in their heads: a small tool to help manage finances, a webpage to record a child's growth, or even a mini-game. But the thought of having to write code or find a programmer often discourages them directly.
After the emergence of AI, for the first time, ordinary people have a completely new possibility: you don't need to know how to write code, you just need to learn how to clearly tell AI what you want. [Data from GitHub Copilot](https://www.wearetenet.com/blog/github-copilot-usage-data-statistics) shows that over 15 million developers are using AI-assisted programming, with an average of 46% of code being AI-generated! In Java projects, this proportion can reach 61%.
<el-card shadow="hover" style="margin: 20px 0; border-radius: 12px;">
<template #header>
<div style="display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">🚀</span>
<span style="font-weight: bold; font-size: 16px;">Leaps in Efficiency and Adoption</span>
</div>
</template>
<el-row :gutter="20" style="margin-bottom: 24px;">
<el-col :span="6" :xs="12">
<div style="text-align: center; padding: 10px;">
<div style="color: #409EFF; font-size: 24px; font-weight: bold;">55%</div>
<div style="color: #909399; font-size: 12px; margin-top: 4px;">Speed Increase</div>
</div>
</el-col>
<el-col :span="6" :xs="12">
<div style="text-align: center; padding: 10px;">
<div style="color: #67C23A; font-size: 24px; font-weight: bold;">2.4 <span style="font-size: 14px;">Days</span></div>
<div style="color: #909399; font-size: 12px; margin-top: 4px;">Task Time (from 9.6)</div>
</div>
</el-col>
<el-col :span="6" :xs="12">
<div style="text-align: center; padding: 10px;">
<div style="color: #E6A23C; font-size: 24px; font-weight: bold;">81%</div>
<div style="color: #909399; font-size: 12px; margin-top: 4px;">Day-1 Install Rate</div>
</div>
</el-col>
<el-col :span="6" :xs="12">
<div style="text-align: center; padding: 10px;">
<div style="color: #F56C6C; font-size: 24px; font-weight: bold;">96%</div>
<div style="color: #909399; font-size: 12px; margin-top: 4px;">Suggestion Adoption</div>
</div>
</el-col>
</el-row>
<div style="line-height: 1.8; color: #606266;">
What is truly exciting is the leap in efficiency: developers' task completion speed increased by <b>55%</b>. Code that originally took 9.6 days to deliver can now be done in just <b>2.4 days</b>. This visible improvement shows that AI is no longer just an "optional feature" but is becoming an indispensable assistant in the development workflow. The adoption rate data confirms this: on the day they granted access, <b>81%</b> of developers installed and started using it immediately; among them, <b>96%</b> started adopting the AI's code suggestions that same day. In other words, developers almost instantly integrated AI into their daily coding routines.
</div>
</el-card>
For ordinary people, this trend is even more significant: if professional programmers are relying heavily on AI to write code, **why can't those of us who don't know how to program communicate directly with AI to realize our ideas**?
The goal of this course is to help you practice a new skill: building apps through natural language conversations. We will teach you how to communicate with AI using computer language and how to let AI turn the ideas in your head into real, usable products.
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="1" :items="[
{ title: 'Dilemmas & Opportunities', description: 'New possibilities' },
{ title: 'Capability Exploration', description: '60-second speed' },
{ title: 'Native Practice', description: 'Build AI-native Snake' },
{ title: 'Extended Creation', description: 'Create other games' }
]" />
</ClientOnly>
</div>
## 2. To What Extent Can AI Help You?
In this section, we only discuss one question: if you completely don't know how to write code, to what extent can today's AI help you?
Roughly speaking, you can understand current LLM capabilities as: competent in developing **simple internal tools**, **data visualization dashboards**, and some **lightweight mini-games**. These are generally sufficient for making **tools for personal use** or validating requirements from a **product manager's perspective**. But to generate a **commercially mature product** with one click, it still typically requires manual, continuous polishing of **process design** and **details**.
Next, let's take Snake as an example and see exactly what AI programming can achieve.
### 2.1 Build a Snake Game in 60 Seconds
First, please open the experimental site used in the course, [z.ai](https://chat.z.ai/). `z.ai` is an AI platform developed by Zhipu AI (one of China's leading LLM companies), powered by their proprietary GLM models. This platform includes various features, such as slideshow generation, poster design, and full-stack development. In this tutorial, we will focus on its full-stack development module.
::: details 💡 What is the "programming right on the web" paradigm?
In the past, developing a web app required:
- Installing programming environments (Node.js, Python, etc.)
- Configuring code editors
- Learning HTML/CSS/JavaScript
- Dealing with dependencies and errors
Now, with AI coding platforms, you only need to:
- Open your browser and visit the site
- Describe your desired features in natural language
- Have AI instantly generate the code and let you preview the result live
This "conversation as programming" paradigm changes coding from "writing instructions" to "describing requirements". You don't need to care about low-level technical details; just clearly state what you want. This is the new programming paradigm of the AI era—**Vibe Coding**.
:::
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/index-2026-01-07-18-25-03.png)
Input our simple requirement and click the **Full-stack Development** button. You can watch the webpage being built in real time. Usually, it takes just the time to brew a coffee!
```
Help me create a Snake game:
1. Control snake movement with arrow keys
2. When it eats food, it gets longer and the score increases
3. Hitting walls or itself results in Game Over
4. Include Start and Restart buttons
5. The UI should be clean and elegant
```
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/index-2026-01-07-18-34-03.png)
Once generated, you will see a browsable webpage UI on the right. Scroll around or click the 🧭 button at the top to view it in full screen.
> The buttons at the top from left to right are: Arrow button expands chat history, Pencil button to start a new chat, Refresh icon to rebuild the page, Compass icon to toggle fullscreen, Download button to download the project, <> button to view code, and Publish button to publish it.
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/index-2026-01-07-18-35-11.png)
If you'd like to check the webpage's source code, click the code icon in the top right to view the entire codebase.
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image7.png)
::: tip 🌐 Explore More AI Programming Tools
Besides z.ai, we also recommend trying out these excellent AI programming platforms:
| Tool | Link | Features |
|------|------|----------|
| **Google AI Studio** (Recommended)| [aistudio.google.com/apps](https://aistudio.google.com/apps) | Official tool from Google, powered by Gemini, great for rapid prototyping |
| **Figma Make** | [figma.com/make](https://www.figma.com/make) | Deeply integrated with design tools, ideal for interactive prototypes |
| **Coze** | [coze.com](https://www.coze.cn) | AI bot platform by ByteDance, zero-code visual building |
| **v0.dev** | [v0.dev](https://v0.dev) | AI generation for React components from Vercel |
| **Bolt.new** | [bolt.new](https://bolt.new) | AI full-stack development capable of generating deployed apps |
| **Lovable** | [lovable.dev](https://lovable.dev) | High-quality React app generation |
| **Replit Agent**| [replit.com](https://replit.com) | Online IDE integrated with AI |
For more comparisons, view the appendix: [Comparison of 7 AI Programming Tools](../../stage-1/appendix-articles/example0-1/vibe-coding-tools-snake-game-tutorial.md)
:::
### 2.2 What Conversational Programming Can and Cannot Do
This section focuses on a specific question: When relying exclusively on conversational AI and writing no code at all, how far can you push a project?
In terms of experience, a fairly consistent conclusion is: It can help you complete a "small but complete" thing, but determining "how much is enough" still requires your personal decision on every detailed step.
#### Excels at "Small and Clear" Apps
From the Snake game example, you already saw a typical pattern:
As long as you can clearly describe the UI and interaction, AI can often piece together a fully functional, clickable webpage in just a few rounds of conversations.
Such tasks often share a few characteristics:
- Clear scope: one page, a simple internal tool, a small game mechanic.
- Visible results: you immediately see if it works as expected.
- Direct debugging: you can point out errors and ask for corrections easily.
Within these boundaries, you can view the AI as a highly capable "junior assistant".
**AI's success rate in handling small-scale tasks:**
<el-progress :percentage="90" :stroke-width="15" status="success" striped striped-flow />
#### Large Projects Require a "Process Perspective"
Once it exceeds the small and clear scope, relying purely on conversational requests to build complex systems end-to-end will quickly hit ceilings. Large projects deal with backend databases, third-party services, authentication, permissions, edge cases, state management, etc.
In these situations, the logical approach is to define a clear process flowchart and break it into segments to be handled individually.
#### The Difference Between Generating and Validating
Just because AI wrote it doesn't mean it's ready for a commercial launch! Always validate AI-generated code, especially in secure systems.
::: warning ⚠️ Usage Guidelines
- **Prototypes/Tools/Demos**: Highly suitable for early stage builds iterations.
- **Large consumer-facing products**: Usually needs developers for architecture.
- **High-security systems**: Not suitable to deploy immediately. Needs stringent checks.
:::
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="2" :items="[
{ title: 'Dilemmas', description: 'New possibilities' },
{ title: 'Basic Ability', description: '60-second speed' },
{ title: 'Native Practice', description: 'Build AI-native Snake' },
{ title: 'Extended', description: 'Create other games' }
]" />
</ClientOnly>
</div>
## 3. Hands-on: Your First AI Native Application
Let's do some hands-on work. We'll add some native AI integration elements into our game.
### 3.1 AI-Native Snake
You can simply provide these prompts:
> **💡 Example Prompt:** Build me a Snake game.
>
> ![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image12.png)
> **💡 Example Prompt:** Build me a Snake game that supports:
> 1. Eating different words and placing them in a collection box.
>
> ![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image13.png)
> **💡 Example Prompt:** Build a Snake game that supports:
> 1. I can eat distinct words, collected in a box.
> 2. When eating 8 words, the LLM generates a poem using them.
> 3. An image generation API is called right after the poem is composed.
>
> ![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image14.png)
If you face any issues, just screenshot the error or tell the bot what's wrong and it will iterate the changes.
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image15.png)
### 3.2 Add New Features to the Game
After completing the basic functionality, we can try adding some new twists to our program! If you find the process of the snake eating words or characters a bit boring, you can have the snake eat words of different colors and change the snake's color accordingly.
You can also add special effects to the "eating" process, or introduce magic words that trigger special effects—like increasing the snake's speed or size. Another idea is to have the model generate a poem and an image every time the snake eats a word, instead of waiting until it eats eight.
If these feel challenging, you can ask the language model directly for help! It can provide creative suggestions to make your game more fun. Give it a try!
```
1. "Word Unlocks World" Mechanic
Every time the snake eats a word, the LLM performs a poetic association on that word (e.g., "tree" → "forest", "shade"), and the image model instantly generates a small artwork for that word. These images gradually piece together into a unique, player-created panorama, so players are "painting and writing poetry" with every playthrough.
2. "Poetry Puzzle" Gameplay
Each word the snake eats triggers the LLM to generate a short verse, and the image model generates an illustration. These verses and images combine like puzzle pieces, forming an AI-collaborative poem and painting at the end of the round.
3. "Magic Words" & "Story Branches"
Special "magic words" (e.g., "wind", "night", "dream") not only trigger the LLM to generate poetry but also change the mood or theme of the scene—transforming the generated image style to nighttime, stormy, or dreamlike atmospheres.
Branching story: The LLM gives a theme or riddle at the start (e.g., "autumn memories"). The player's word choices directly influence the story and poetry evolution, with the image model updating backgrounds and visuals in real time.
4. "Real-time Interactive Generation"
After each word, the LLM generates a line of dialogue or description; NPCs in the game can "speak" to the player, or the environment can change accordingly.
The snake's appearance or obstacles in the game can visually change based on the words eaten, thanks to the image model.
5. "Create & Share"
Players can save and share their AI-created poems and images at the end of a session, showing off their unique "AI collaboration."
Leaderboards for "Most Beautiful Poem + Art", "Most Creative Word Combination", etc., encourage replaying and creativity.
6. "Sentence Snake" Challenge
Reverse mode: The LLM gives a line of poetry or a riddle, and the player must guide the snake to eat words in order to reconstruct the sentence. Eating the wrong word triggers funny or artistic consequences via the image generation model.
7. "Themed Levels" & "Style Selection"
At the start of the game, the player chooses a theme (e.g., "fairy tale", "sci-fi", "Tang poetry"), and both the LLM and image model adjust word selection, poetry style, and visuals to match, making each run feel fresh.
8. "Live Co-creation"
When a special word is eaten, the LLM can prompt the player to input a phrase or choose a style, then AI generates corresponding verses and illustrations, making it a true human-AI co-creation.
9. "AI Easter Eggs & Achievements"
Certain word combinations are recognized by the LLM as special themes or inside jokes (e.g., "moon", "osmanthus", "riverbank"), triggering rare verses and illustrations that reward exploration.
10. "A Growing Story"
As the snake grows, the LLM generates a continuous story-poem, and the image model creates a seamless scroll or panorama, so the player is simultaneously "writing, painting, and playing."
```
Additionally, we can also ask the LLM to generate project-level prompts for you directly. In the previous section, we only wrote the Snake game prompt ourselves. Now let's try having the LLM generate a prompt with an overall framework and implementation path (you can generate it directly with z.ai).
If you want to learn how to write better prompts, check out the [Prompt Engineering Appendix](/zh-cn/appendix/8-artificial-intelligence/prompt-engineering).
> I want AI to generate a web-based Snake game and need a more complete prompt to make the result more impressive and fun. Please generate the corresponding prompt. The current goal is: generate a Snake game that implements the function of eating different words to generate poetry, and should include an image generation module.
z.ai's response will look like this:
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image56.png)
We can use this prompt to regenerate the project in full-stack development mode:
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image57.png)
![](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/image58.png)
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="3" :items="[
{ title: 'Dilemmas', description: 'New possibilities' },
{ title: 'Basic Ability', description: '60-second speed' },
{ title: 'Native Practice', description: 'Build AI-native Snake' },
{ title: 'Extended', description: 'Create other games' }
]" />
</ClientOnly>
</div>
### 3.3 Try Making Other Mini-Games
Beyond Snake, we can let our imagination run wild.
Create anything we want to create, and even try to mess everything up! Then start over!
```
1. AI Art Gallery Platform
Description: An online gallery showcasing AI-generated artworks where users can upload, share, and comment on AI art.
Features: User account system, artwork upload and display, rating system, category browsing, AI generation tool integration.
Tech highlights: React/Vue frontend, Node.js backend, MongoDB database, AI API integration.
2. Retro Game Archive
Description: A website paying tribute to classic games, featuring game history, gameplay guides, and playable retro games online.
Features: Game database, timeline display, online emulator, user reviews, game collection feature.
Tech highlights: Responsive design, WebGL/Canvas game implementation, RESTful API, user authentication.
3. Sustainable Living Tracker
Description: A website helping users track and reduce their carbon footprint through eco-tips and community challenges.
Features: Personal carbon footprint calculator, goal setting, progress tracking, community challenges, eco knowledge base.
Tech highlights: Data visualization, mobile optimization, social features, push notifications.
4. Virtual Kitchen Assistant
Description: An AI-based cooking guidance platform providing personalized recipe recommendations and step-by-step cooking instructions.
Features: Recipe database, ingredient recognition, personalized recommendations, cooking timer, nutrition analysis.
Tech highlights: Image recognition API, ML recommendation system, voice control, real-time video guidance.
5. Underground Music Discovery Platform
Description: A music streaming platform focused on indie and emerging artists, offering a unique discovery experience.
Features: Music streaming, artist profiles, personalized recommendations, playlist creation, community reviews.
Tech highlights: Audio streaming, recommendation algorithms, social features, music visualization.
6. Minimalist Task Management System
Description: A task management tool with zen aesthetics, focused on simple and efficient task organization.
Features: Task creation and categorization, priority setting, progress tracking, team collaboration, data analytics.
Tech highlights: Minimalist UI design, drag-and-drop, real-time sync, cross-platform compatibility.
7. Sci-Fi Writing Workshop
Description: A platform providing creative tools and inspiration for sci-fi writers, including world-building aids and character development tools.
Features: Story structure tools, character profiles, world-building templates, writing statistics, community feedback.
Tech highlights: Rich text editor, data visualization, collaborative editing, AI-assisted creation.
8. Personal Knowledge Graph
Description: A tool helping users build personal knowledge networks, visualizing and connecting various ideas and information.
Features: Node creation and connection, tagging system, search functionality, import/export tools, visual charts.
Tech highlights: Graph database, data visualization algorithms, Markdown support, cross-device sync.
9. Virtual Botanical Garden
Description: An interactive plant encyclopedia where users can explore the plant world and create virtual gardens.
Features: Plant database, 3D plant models, growth simulation, gardening guides, community showcase.
Tech highlights: 3D rendering, seasonal change simulation, AR integration, plant recognition API.
10. Programming Challenge Arena
Description: An online competition platform for programmers with coding challenges of various difficulty levels.
Features: Challenge problems, code editor, auto-evaluation, leaderboards, learning paths.
Tech highlights: Code sandbox environment, real-time evaluation system, algorithm visualization, social learning features.
```
And... if you enjoy playing games, let's try creating games together!
```
1. 3D Open World RPG
Description: A fantasy RPG with a vast open world, quests, and character progression.
Features: Day-night cycle, dynamic weather, skill trees, multiplayer co-op, crafting system.
Tech highlights: Three.js or Babylon.js for 3D rendering, server-side game logic, character customization, save system.
2. First-Person Shooter (FPS) Arena
Description: A fast-paced multiplayer FPS with various game modes and maps.
Features: Team deathmatch, capture the flag, weapon customization, ranked matches.
Tech highlights: WebGL/Three.js for 3D graphics, multiplayer netcode, hit detection, voice chat.
3. AI Chess and Multiplayer
Description: A full-featured chess platform with AI opponents and online matches.
Features: AI difficulty levels, endgame challenges, tournament mode, replay analysis.
Tech highlights: Chess logic library, WebSocket for real-time matches, ELO ranking system, anti-cheat.
4. Mahjong Online Multiplayer
Description: A traditional Mahjong game with online multiplayer and scoring.
Features: Multiple rule sets, private rooms, ranking system, replay feature.
Tech highlights: Tile matching logic, real-time multiplayer, lobby system, score tracking.
5. Turn-Based Strategy Game
Description: A tactical strategy game with grid-based combat and unit management.
Features: Campaign mode, skirmish, unit upgrades, fog of war, multiplayer battles.
Tech highlights: Grid movement system, AI decision-making, turn synchronization, save/load system.
6. Time Trial Racing Game
Description: A 3D racing game focused on time trials and track records.
Features: Multiple tracks, car customization, ghost replays, leaderboards.
Tech highlights: 3D car physics, track editor, replay system, online leaderboards.
7. Card Battle Game (Deck Building)
Description: A strategic card game where players build decks and battle opponents.
Features: Card collection, deck building, ranked matches, seasonal events.
Tech highlights: Card game logic, matchmaking system, AI opponents, card animations.
8. Battle Royale (Top-Down 2D)
Description: A top-down 2D battle royale with shrinking play zones and loot mechanics.
Features: Solo and squad modes, weapon variety, in-match events, leaderboards.
Tech highlights: Real-time multiplayer, zone shrinking logic, loot generation system, matchmaking.
9. Horror Survival Game (First-Person)
Description: A first-person horror game with resource management and escape mechanics.
Features: Atmospheric environments, puzzles, enemy AI, multiple endings.
Tech highlights: Dynamic lighting, sound design, enemy pathfinding, save system.
10. Music Rhythm Game (3D)
Description: A 3D rhythm game where players hit notes to the beat of the music.
Features: Multiple difficulty levels, track editor, custom song support, leaderboards.
Tech highlights: Audio analysis, beat synchronization, 3D note tracks, input timing detection.
```
## 📚 Assignment
<el-card id="assignment-card" shadow="hover" style="margin: 20px 0; border-radius: 12px;">
<template #header>
<div style="font-weight: bold; font-size: 16px;">🎯 Chapter Assignment: Build Your First AI-Native Mini-Games</div>
</template>
<p>
In this section, you've followed the steps to experience the complete process from "conversational Snake generation" to "understanding AI-native game design thinking." The following assignments will help you turn this understanding into real skills.
</p>
<ol>
<li>
<strong>Fully Reproduce the AI-Native Snake Game</strong>
<ul>
<li>At minimum, implement: the snake can move, eating "food" changes its length and score, and hitting walls or itself ends the game.</li>
<li>During reproduction, practice sending the error description + error message + key code snippets all at once to the AI, asking it to fix things in "beginner mode."</li>
</ul>
</li>
<li>
<strong>(Optional) Create 1 Original AI-Native Mini-Game or Demo</strong>
<ul>
<li>It can be any lightweight gameplay involving text, images, music, rhythm, etc., such as "eat words to write poems," "rhythm clicking," "generative runner," etc.</li>
<li>The focus isn't on flashy graphics, but on being able to clearly articulate: what specifically did AI help with here, and what "hard-to-do-manually or tedious" part did it solve.</li>
</ul>
</li>
</ol>
<p>
That's the complete tutorial! You may need about <strong>4 hours</strong> to finish all the content and build your own Snake game. Don't rush—explore, experiment, and enjoy the process. If you encounter concepts you don't quite understand along the way, we recommend checking the relevant sections in the appendix below.
</p>
</el-card>
## Appendix
<el-card id="appendix-nav" shadow="hover" style="margin-top: 24px; margin-bottom: 24px; border-left: 5px solid #67C23A;">
<div style="font-weight: bold; margin-bottom: 8px;">Appendix Navigation</div>
<div style="color: #606266; font-size: 14px; line-height: 1.6; margin-bottom: 12px;">
Here we've compiled some foundational concepts related to this chapter: if you encounter questions like "what is frontend?" or "what exactly does Vibe Coding mean?" during your learning, you can always come back here to look them up.
</div>
<el-row :gutter="16">
<el-col :span="12">
<a href="#appendix-1" style="text-decoration: none; color: inherit;"><b>Appendix 1: Do We Need Frontend Knowledge?</b></a><br/>
<span style="font-size: 12px; color: #909399">Understand where frontend fits in the overall application, and know which parts are "visible."</span>
</el-col>
<el-col :span="12">
<a href="#appendix-2" style="text-decoration: none; color: inherit;"><b>Appendix 2: What Exactly is Vibe Coding</b></a><br/>
<span style="font-size: 12px; color: #909399">Understand the core idea of "conversational development" and how to collaborate with AI.</span>
</el-col>
</el-row>
<el-row :gutter="16" style="margin-top: 10px;">
<el-col :span="12">
<a href="#appendix-3" style="text-decoration: none; color: inherit;"><b>Appendix 3: Model Context</b></a><br/>
<span style="font-size: 12px; color: #909399">Understand commonly heard but easily confused concepts like "context length."</span>
</el-col>
<el-col :span="12">
<a href="#appendix-4" style="text-decoration: none; color: inherit;"><b>Appendix 4: Instruction Following</b></a><br/>
<span style="font-size: 12px; color: #909399">Learn why models sometimes "don't understand" and how to write clearer instructions.</span>
</el-col>
</el-row>
<div style="margin-top: 12px; font-size: 12px; color: #909399;">
Tip: You can press Ctrl/⌘+F to search for keywords, or copy confusing paragraphs to AI and ask it to explain again in a way "a complete beginner can understand."
</div>
</el-card>
## <span id="appendix-1">[Appendix 1: Do We Need Frontend Knowledge?](#appendix-nav)</span>
::: tip 💡 One-line Summary
You don't need to write code, but understanding the basic concepts helps you describe requirements to AI more effectively.
:::
<el-row :gutter="16" style="margin: 20px 0;">
<el-col :span="12" :xs="24" style="margin-bottom: 16px;">
<el-card shadow="hover" style="border-radius: 12px; height: 100%;">
<template #header>
<div style="display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">👁️</span>
<span style="font-weight: bold;">Frontend</span>
<el-tag type="success" size="small">Visible</el-tag>
</div>
</template>
<div style="color: #606266; line-height: 1.8;">
Everything users can <strong>see and click</strong>
<ul style="margin: 12px 0; padding-left: 20px;">
<li>Page titles, text, images</li>
<li>Buttons, input fields, dropdown menus</li>
<li>Game interfaces, animation effects</li>
</ul>
</div>
</el-card>
</el-col>
<el-col :span="12" :xs="24" style="margin-bottom: 16px;">
<el-card shadow="hover" style="border-radius: 12px; height: 100%;">
<template #header>
<div style="display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">⚙️</span>
<span style="font-weight: bold;">Backend</span>
<el-tag type="info" size="small">Invisible</el-tag>
</div>
</template>
<div style="color: #606266; line-height: 1.8;">
Data processing running on the server
<ul style="margin: 12px 0; padding-left: 20px;">
<li>User score storage</li>
<li>Login account verification</li>
<li>Level content distribution</li>
</ul>
</div>
</el-card>
</el-col>
</el-row>
### The Frontend Trio
Browsers use three types of "code" to build pages:
<el-tabs type="border-card" style="margin: 20px 0;">
<el-tab-pane label="🏗️ HTML - Skeleton">
<div style="padding: 10px;">
<p><strong>Purpose:</strong> Defines <strong>what elements</strong> are on the page</p>
<p><strong>Analogy:</strong> The structural blueprint of a house (where walls, doors, and windows go)</p>
<el-card style="background: #f5f7fa; margin-top: 12px;">
<pre style="margin: 0;"><code>&lt;button&gt;Click me&lt;/button&gt;
&lt;h1&gt;Title&lt;/h1&gt;
&lt;img src="photo.png"&gt;</code></pre>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane label="🎨 CSS - Style">
<div style="padding: 10px;">
<p><strong>Purpose:</strong> Controls <strong>how elements look</strong></p>
<p><strong>Analogy:</strong> The interior decoration of a house (colors, materials, layout)</p>
<el-card style="background: #f5f7fa; margin-top: 12px;">
<pre style="margin: 0;"><code>button {
background: blue;
color: white;
border-radius: 8px;
}</code></pre>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane label="⚡ JavaScript - Behavior">
<div style="padding: 10px;">
<p><strong>Purpose:</strong> Makes the page <strong>interactive</strong></p>
<p><strong>Analogy:</strong> The electrical switches of a house (responses after clicking)</p>
<el-card style="background: #f5f7fa; margin-top: 12px;">
<pre style="margin: 0;"><code>button.onclick = () => {
alert('You clicked me!')
}</code></pre>
</el-card>
</div>
</el-tab-pane>
</el-tabs>
### How Does Code Become a Page?
When you open a webpage, the browser processes three types of code in order:
**1. HTML — Defines the page structure**
The browser first parses HTML to understand what elements are on the page (headings, paragraphs, images, buttons, etc.) and their hierarchical relationships.
**2. CSS — Applies styles**
Then the browser applies CSS rules to add styles to these elements: colors, sizes, positions, spacing, etc., making the page look beautiful.
**3. JavaScript — Adds interactivity**
Finally, JavaScript code is executed to make the page "come alive": responding to clicks, submitting forms, playing animations, etc.
**4. Page rendering**
The combined result of all three is the webpage you ultimately see.
### Modern Frontend Frameworks: From HTML to React/Vue
The HTML, CSS, and JavaScript introduced above are the "three essentials" of frontend development—they are the foundation of all webpages. But when pages become complex, developing directly with these three can be challenging: code becomes hard to maintain, there's lots of repetitive work, and data synchronization is troublesome.
**Modern frontend frameworks** (like React, Vue, Angular) are built on top of HTML/CSS/JS to make development more efficient:
**1. HTML/CSS/JS (Basic stage)**
Directly manipulating page elements, suitable for simple pages. But as code grows, all logic gets mixed together and becomes hard to maintain.
**2. jQuery (Transitional stage)**
Simplified DOM operations, making code more concise. But you still need to manually manage page state and find corresponding elements to update when data changes.
**3. React/Vue (Modern stage)**
Adopts component-based and state-driven design:
- **Component-based**: Break the page into independent, reusable modules (like buttons, cards, navigation bars)
- **State-driven**: When data changes, the framework automatically updates the corresponding UI without manual manipulation
::: tip 💡 Simple Understanding
- **HTML/CSS/JS** = Basic materials (bricks, cement, steel)
- **React/Vue** = Building framework (provides standards and tools for constructing buildings)
In the AI-assisted programming era, you don't need to deeply master every detail of frameworks. You just need to understand their basic concepts, and you can describe requirements in natural language to have AI generate code for you.
:::
### In Vibe Coding
**Core point: You don't need to write code, you just need to know how to describe.**
After understanding frontend concepts, you can describe requirements to AI like this:
> "Use React to make a leaderboard page, with a score list on the right side. Clicking a row shows player details below. The style should be clean and modern."
If you want to dive deeper into frontend fundamentals like HTML, CSS, and JavaScript, check out the [Web Basics Appendix](/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive). To learn about the evolution of frontend technology, check out the [Frontend Evolution Appendix](/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks).
## <span id="appendix-2">[Appendix 2: What Exactly is Vibe Coding](#appendix-nav)</span>
> 💡 What is Vibe Coding? Computer scientist [Andrej Karpathy](https://karpathy.ai/) (one of the co-founders of OpenAI, former head of AI at Tesla) coined the term **vibe coding** in February 2025. This concept refers to a coding methodology that relies on LLMs, **allowing programmers to generate working code by providing natural language descriptions instead of manually writing code.**
![1767350588191](../../../zh-cn/stage-0/0.2-ai-capabilities-through-games/images/1767350588191.png)
Literally, Vibe Coding can be understood as a way of "developing by talking." The core change is: you no longer need to write code line by line, look up syntax, or debug yourself. Instead, you directly describe what you want in natural language, for example:
"I need a login page with a phone number input field and a verification code input field."
"After successful login, redirect to the homepage and display the username in the top right corner."
"Give me a simple Snake game that can be controlled with keyboard arrow keys."
The Large Language Model (LLM) will automatically translate these descriptions into real, runnable code and generate the corresponding pages, logic, and data structures. After you see the results, you can propose modifications in natural language, such as "make the button bigger," "change the background to dark," "record scores and display a leaderboard," and the AI will continue adjusting the implementation according to your requirements.
In this mode, you don't need to learn a programming language first before writing code. Instead, you focus your main energy on: clearly stating what you want to do, judging "what's wrong" after seeing the results, and then proposing new modifications. AI handles turning these high-level ideas into concrete implementations, significantly reducing mechanical, repetitive coding work.
You can click here to learn more about vibe coding: [https://www.ibm.com/think/topics/vibe-coding](https://www.ibm.com/think/topics/vibe-coding)
You can click here to see more of Karpathy's shared content: [https://karpathy.bearblog.dev/blog/](https://karpathy.bearblog.dev/blog/)
### How to Pretend You're a Vibe Coding Master
In practice, during real vibe coding, we usually don't use many complex prompts. Perhaps we need a specific and moderately complex prompt for the entire program at the beginning, but after that, at each step, you may only need prompts like these:
```
"There's a bug in the code, please fix it."
"I don't want partial code, give me the complete modified code."
"Your code still has problems."
"Please modify again and give me the complete corrected code."
"It was working before, why isn't it working now?"
"Did you not understand what I meant? Don't change my original code."
"Don't add any debugging features."
"Don't do things I didn't ask you to do."
"Where is the feature I asked you to implement?"
"Can you not understand what I'm saying?"
"I only want one function."
"I told you to refer to my previous code."
"Please don't add unnecessary comments."
"Please don't modify the basic logic of my original code."
"Help me modify the code."
"Modify based on my code..."
"Don't change my variable names!!!"
"Don't change the original function names!"
"Don't mess with my variables."
"Don't add extra features."
"Don't just generate a skeleton, generate the complete code."
```
This may sound a bit exaggerated, but in reality, these are the prompts we might use in daily work. Due to the **context length limitations** of large language models, or sometimes because their **instruction following ability** isn't very strong, models may forget content discussed earlier in the conversation. In vibe coding, we tend to use models with long context and strong instruction following ability. We can judge whether a model is good through rankings or metrics of these two aspects.
Alternatively, due to the style of training datasets, large models tend to respond in the style of their training data. For example, some speak very seriously, some like to add lots of embellishments, and some models like to add lots of comments or unnecessary modules to code.
## <span id="appendix-3">[Appendix 3: Model Context](#appendix-nav)</span>
Model context can be understood as AI's short-term memory. It refers to all the text content that the model can "see" and "remember" during a single conversation or task, including your previous questions, system-provided instructions, relevant materials, etc.
It is precisely because of context that AI can understand you're continuing from previous content, enabling round after round of coherent, natural conversation. Without context, every sentence you say would appear to the model as a completely new question—it wouldn't know what you said before, and there would be no way to continue a conversation.
Each model has its own effective context length (context window). This length is usually measured in tokens (which can be roughly understood as units of "word fragments"), and most mainstream models currently range from 32k to 128k tokens. The longer the context, the more content the model can "read" at once, for example:
- Reading an entire lengthy paper or report in one go
- Referencing multiple materials and cases in the same conversation
- Having the model remember conclusions from complex discussions several rounds ago
When your input approaches or exceeds the model's context limit, some common phenomena often appear:
- The model starts forgetting details or key information from earlier in the long text
- As the conversation progresses, the topic gradually drifts from the original goal
- Across different Q&As about the same material, the referenced content becomes inconsistent
These phenomena don't mean the model suddenly "got dumber"—they are natural results of the context capacity being used up or nearly used up.
In practical use, we want the context to be as long as possible, while also being aware that:
- The longer the context, the more computing resources it consumes
- The corresponding API costs (fees) also increase accordingly
Therefore, when designing AI applications, you need to balance letting the model see enough information with controlling costs and improving efficiency. For example:
- Distill information that truly needs long-term retention before feeding it to the model
- Avoid stuffing detail information that's no longer needed into the context repeatedly
- Use external knowledge bases and similar approaches to hand "long-term memory" to the system rather than forcing it into the model's context
## <span id="appendix-4">[Appendix 4: Instruction Following](#appendix-nav)</span>
Instruction following refers to: after the model understands your instructions, whether it can accurately and completely execute according to your requirements. This includes not only answering questions, but also completing tasks in specified formats, styles, and steps.
For example, the following are all instructions with clear requirements for the model:
- Summarize this article into three key points
- Write a reply email in a formal, polite tone
- Translate this word into English and create an example sentence for each
- Extract the author, time, and main events from the article
A model with strong instruction following ability typically has these characteristics:
- Outputs content in the required quantity
For example, if asked to summarize three key points, it won't give five.
- Covers all specified elements
For example, if asked to extract author, time, and events, it won't omit any of them.
- Follows the specified format and tone
For example, if asked to use a formal tone, it won't output overly colloquial responses.
- Doesn't make unnecessary additional extensions
For example, if only asked to translate and create sentences, it won't output a large paragraph of unrelated explanations.
In practical applications, strong instruction following ability is very important for these reasons:
- Improved stability: The same instruction produces more consistent output structure and behavior patterns across different times and multiple runs, less likely to go off-script
- Improved reproducibility: When you configure a prompt into a product or workflow, you can predict roughly how the model will respond, making testing and iteration easier
- Easier system integration: When model output conforms to expected formats, it's easier to automatically interface with backend programs, workflows, or other tools
Therefore, when selecting and evaluating a large language model, in addition to focusing on whether it's smart and has broad knowledge coverage, you also need to pay special attention to its instruction following ability. For industrial-grade applications, being able to stably and accurately execute instructions is often more important than occasionally giving a stunning answer.
+129
View File
@@ -0,0 +1,129 @@
# Build Your First AI Product
Welcome to the **Build Your First AI Product** stage! This is the starting point of the Easy-Vibe tutorial, specifically designed for learners with zero programming background.
## What You Will Learn
In this stage, you will start from zero and master the Vibe Coding workflow, becoming a super individual capable of independent product design.
### Getting Started
Suitable for learners with product, operations, and non-technical backgrounds. Understand AI programming logic through games and build learning confidence:
<NavGrid>
<NavCard
href="/en/stage-0/0.1-learning-map/"
title="1. Learning Map"
description="Understand the entire learning path and clarify the goals and outcomes of each stage."
/>
<NavCard
href="/en/stage-0/0.2-ai-capabilities-through-games/"
title="2. AI Era: If You Can Speak, You Can Code"
description="Experience the charm of AI programming through mini-games like Snake and break the fear of coding."
/>
</NavGrid>
### Product Prototype Practice
Master the Vibe Coding workflow, learn to deconstruct requirements, and independently complete high-fidelity Web application prototypes:
<NavGrid>
<NavCard
href="/zh-cn/stage-1/1.1-introduction-to-ai-ide/"
title="1. Master AI Programming Tools"
description="Learn about current mainstream AI programming tools and choose the most suitable development partner for you."
/>
<NavCard
href="/zh-cn/stage-1/1.0-finding-great-idea/"
title="2. Find Great Ideas"
description="Learn to find and validate product ideas and find projects worth doing."
/>
<NavCard
href="/zh-cn/stage-1/1.2-building-prototype/"
title="3. Build Product Prototypes"
description="Learn how to quickly transform product ideas into visual prototypes for low-cost trial and error."
/>
<NavCard
href="/zh-cn/stage-1/1.3-integrating-ai-capabilities/"
title="4. Integrate AI Capabilities"
description="Let your prototype have intelligent interaction capabilities by integrating simple AI APIs."
/>
<NavCard
href="/zh-cn/stage-1/1.4-complete-project-practice/"
title="5. Complete Project Practice"
description="Comprehensively use what you have learned to complete a complete product prototype development from 0 to 1."
/>
</NavGrid>
### Appendix: Business Thinking
**Why needed**: When you need to enhance product thinking and understand industry application scenarios, this content can help you establish a more comprehensive product perspective.
**When to watch**:
- Before starting to build a prototype, understand product thinking first to help you plan and design better.
- When you have a product idea but are unsure of the direction, refer to industry scenario cases.
- After completing the project, use product thinking to review and optimize your work.
<NavGrid>
<NavCard
href="/zh-cn/stage-1/appendix-a-product-thinking/"
title="Product Thinking and Solution Design"
description="Supplement necessary thinking models for product managers to improve demand analysis and product design capabilities."
/>
<NavCard
href="/zh-cn/stage-1/appendix-industry-scenarios/"
title="AI Industry Application Scenarios (B-end)"
description="Understand AI application scenarios in different industries to find product inspiration and direction."
/>
<NavCard
href="/zh-cn/stage-1/appendix-c-consumer-scenarios/"
title="AI Consumer Scenarios Inspiration (C-end)"
description="Explore AI application scenarios in consumer-grade products and inspire creative ideas."
/>
</NavGrid>
### Appendix: Technical Solutions
**Why needed**: When you encounter technical problems during development or want to know better tools, these technical appendices provide ready-to-use solutions.
**When to watch**:
- When you encounter an error and don't know how to solve it, check common errors and solutions.
- When you want to compare different AI programming tools, refer to platform measurement comparisons.
- When you want to learn more advanced development skills, check agent development cases.
<NavGrid>
<NavCard
href="/zh-cn/stage-1/appendix-b-common-errors/"
title="What to do if you encounter errors when coding"
description="Summarize common error messages and solutions during development to help you troubleshoot problems quickly."
/>
<NavCard
href="/zh-cn/stage-1/appendix-articles/example0-1/vibe-coding-tools-snake-game-tutorial"
title="Comparison of Seven AI Programming Tools"
description="Compare and test mainstream AI programming platforms to help you choose the most suitable tool."
/>
<NavCard
href="/zh-cn/stage-1/appendix-articles/example0-2/vibe-coding-tools-build-website-with-ai-coding-and-design-agents"
title="Design Websites using Design and Programming Agents"
description="Learn how to use AI agents to work together and improve development efficiency."
/>
</NavGrid>
## Suitable for
- Product managers and operations personnel with zero foundation
- Entrepreneurs who want to quickly validate ideas
- Non-technical individuals interested in AI programming
- Designers who hope to improve their prototype design capabilities
## How to Learn?
It is suggested to follow this order:
```
Play games to build confidence → Follow the tutorial to make a prototype → Add AI functions to the prototype → Independently complete a full project
```
Don't read everything at once; learning while doing is most effective. When you encounter problems, remember to check the appendix for solutions.
Start now, select the first section in the left navigation!
@@ -0,0 +1,361 @@
# Beginner Level 2: Learn AI Programming Tools
## Chapter Overview
<script setup>
const duration = 'About <strong>1 day</strong>, can be completed in multiple sessions'
</script>
<ChapterIntroduction :duration="duration" :tags="['Local Development Environment Setup', 'IDE vs AI IDE', 'Efficient Development Tips']" coreOutput="1 original game you create" expectedOutput="Built using Trae">
Previously, we experienced AI programming on z.ai, but the web version has many limitations — you **can't save your work anytime**, it's **hard to manage files**, and you **can't handle complex projects**. This chapter helps you move your development environment to your own computer so you can **truly build things independently**.
We'll first clarify **what the difference is between an IDE and an AI IDE**, and why the latter can **double your efficiency**. Then we'll **walk you through step by step** using Trae to build a Snake game locally, covering the **complete workflow** from installation to running. Finally, we'll share some **practical tips** for communicating with AI so you can avoid common pitfalls.
After completing this chapter, you'll have **mastered a development workflow similar to that of professional programmers**.
::: tip 💡 Advanced Tip
If you have some programming experience and want to use more powerful tools early on, you can refer to [Modern CLI Coding Tools](../../stage-2/backend/2.6-modern-cli/extra7/) to develop using the command line.
:::
</ChapterIntroduction>
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="0" :items="[
{ title: 'Understanding the Environment', description: 'IDE vs AI IDE' },
{ title: 'Hands-on Practice', description: 'Build Snake with Trae' },
{ title: 'Tool Deep Dive', description: 'Explore the IDE Interface' },
{ title: 'Communication Skills', description: 'Talk to AI Effectively' }
]" />
</ClientOnly>
</div>
## 1. What Environment and Tools Do You Need to Write Code
### 1.1 Mindset Shift: When in Doubt, Ask AI First
Before we introduce the various environments and tools, here's an important reminder: you need to **change your thinking habits**.
In traditional programming learning, if you need to install Python, configure Conda, or fix an npm installation failure, you'd typically open a search engine, find a tutorial, and follow the steps one by one. If you hit an error along the way, you'd search for the error message and try again repeatedly.
Wrong! ❌
In the AI era, especially when using an AI IDE, remember one core principle: **For any task, you can ask AI first, or even let it do it for you.**
- **Don't know how to set up your environment?** Just ask AI in the sidebar: "I want to write Python. Please check if Python is installed, and if not, install it for me."
- **Network stuck?** If installing dependencies keeps spinning or throwing errors, just throw the error to AI: "The download failed. Is it a network issue? Can you help me switch to a different mirror source?"
- **Can't remember commands?** No need to memorize Git or Conda commands. Just tell AI: "Help me create a new virtual environment called demo."
### 1.2 Why You Need an Environment and Tools
Going from "trying to write a few lines of code" to "building a long-term maintainable project" requires completely different environments and tools.
In theory, you could write code with the system's built-in Notepad, but problems quickly arise:
- **All code is plain black text** — keywords, strings, and comments are all mixed together, making it hard to see the structure at a glance
- **No smart suggestions** — you have to type every word completely by hand, and a single typo means repeatedly checking your code
- **Files become chaotic** — switching back and forth between dozens of files, often unable to find the line you need to edit
- **Debugging is guesswork** — when the program crashes, you don't know what went wrong and can only add print statements line by line
That's why you need an IDE (Integrated Development Environment). It displays code in different colors, provides auto-suggestions as you type, organizes files by project, and lets you trace errors step by step — making development more efficient and less error-prone.
## 2. What Is an IDE, and Why Do You Need One
::: info Pre-reading Tip
If you're not yet familiar with what an IDE is or what each interface element does, we recommend reading [IDE Basics](/zh-cn/appendix/2-development-tools/ide-basics) first to learn the basic concepts and common features.
:::
In the early days of programming, all we needed was a simple text editor and a language processor. But as projects grew more complex, developers urgently needed a tool that could efficiently manage files, support syntax highlighting, and enable debugging — and thus the Integrated Development Environment (IDE) was born.
You can think of an IDE as a program specifically designed to "edit, manage, run, and debug" code. Early IDEs looked very "primitive" and were operated almost entirely through the keyboard.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image1.png)![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image2.png)
Terminal Interface — Image source: https://en.wikipedia.org/wiki/File:Emacs-screenshot.png
Well-known and mature "built-in IDEs" like `Vim` are commonly used for remote server operations.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image3.png)
For greater efficiency, we need modern IDEs that support mouse interaction, typically including:
- **Source Code Editor**: Syntax highlighting, auto-completion.
- **Build and Run Tools**: Built-in compiler/interpreter.
- **Debugger**: Breakpoint debugging, variable inspection.
Modern IDEs often also include built-in tools like Git. The most popular is Microsoft's **[Visual Studio Code (VS Code)](https://code.visualstudio.com/)**, which is lightweight and extensible. While there are also professional IDEs like the JetBrains suite, VS Code is the most beginner-friendly.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image4.png)
VS Code's core philosophy is "everything is a plugin." Through its plugin system, it supports various languages — install the Python plugin and it becomes a Python IDE, install the C++ plugin and it becomes a C++ IDE. Without plugins, it's just an advanced text editor.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image5.png)
You can even use it to edit Markdown documents.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image6.png)
In short, an IDE is a set of tools that helps developers write code and run programs efficiently.
For more detailed explanations, check out the [Virtual IDE Visualization section in the Appendix](/zh-cn/appendix/2-development-tools/ide-basics).
## 3. How Is an AI IDE Different from a Regular IDE
A regular IDE (like the original VS Code) is essentially a "toolbox":
You can open projects, write code, run and debug, and install plugins — but the prerequisite is that you need to know what to do and how to do it yourself:
- When there's an error, you read the message yourself and figure out which line has the problem;
- When you want to add a new page or API endpoint, you find the right file and write the code yourself;
- When you want to configure the environment or build the project, you look up the documentation and follow the steps yourself.
But in an AI IDE, you can directly use a large language model to help you code and modify files:
- Just say "make a login page," and it generates the basic code structure first;
- Throw the error message and related code at it, and let it analyze the cause and suggest fixes;
- After you confirm, let it automatically create files, batch-edit code, and handle cross-file grunt work.
For example, you can select a piece of code and ask it to "refactor this" or "add comments." You can also ask in the sidebar "How is this project designed?" and specify the reference scope using `@filename` or `@entire project`, completing the tedious operations of creating files, writing code, and running with a single sentence.
In the latest version of VS Code, a large language model assistant is already built in. You can have conversations with the model about the entire codebase, a specific file, or even a specific function. You can also use it like the auto-coding tools you used on the web — send your requirements as prompts to the built-in coding Agent, and let it automatically implement the features you need, create files, modify code, configure environments, and more.
You can download and install VS Code, click the sidebar entry in the top-right corner, and open the AI feature area to experience these capabilities.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image7.png)
However, VS Code is not the IDE with the strongest AI capabilities. For scenarios that require heavy AI-assisted coding, we often want to use "smarter, more efficient" tools — a good AI IDE can significantly save time on writing code and fixing bugs. Below we'll introduce several popular AI IDEs. You can choose any AI IDE based on your personal preference.
Since VS Code is open source (anyone can download the source code and compile it themselves), the vast majority of AI IDEs on the market today are built on top of VS Code. So you don't need to worry about "learning many different IDEs" — **as long as you're familiar with the basics of VS Code**, migrating to these AI IDEs doesn't require starting from scratch.
Generally speaking, the differences between AI IDEs mainly come down to four aspects: pricing; available model types (some advanced models may be restricted in certain regions); Agent capabilities (how smart and capable it is at assisting with coding); and speed and performance. You can choose based on your own testing results — the best tool is the one that works best for you.
> Typical AI IDEs generally have the following core capabilities:
>
> - Smart Code Generation and Completion: In traditional IDEs, we typically type a few characters to auto-complete variable or function names. In modern AI IDEs, you can write a few lines of pseudocode or simply describe your requirements, and the IDE will auto-complete the full logic, or even generate large blocks of code based on instructions.
> - Code Understanding and Q&A: The IDE can understand and answer questions about a specific piece of code, a file, or even the entire project directory structure.
> - Code Refactoring and Optimization: The IDE can rewrite or optimize the implementation logic of specified code snippets based on your intent.
> - Automatic Test Generation: The IDE can automatically generate test code for different functions and modules, making it easy to perform targeted testing.
> - Agent-style Task Execution: Smart Agents can automatically generate, build, install, run, and modify code, partially replacing the work of junior software engineers in many tasks.
::: details Antigravity
### [Antigravity](https://antigravity.google/)
Antigravity is a brand-new AI IDE released by Google in November 2025 alongside Gemini 3, adopting an "Agent-First" development model. Unlike traditional AI-assisted coding, Antigravity makes the AI agent the "active executor," capable of directly operating the editor, terminal, browser, and other tools, taking on more "execution," "planning," and "verification" work. Developers only need to express high-level intent, and the agent will automatically break down tasks, create plans, execute code, run tests, and generate results. It supports multi-model switching, including Gemini 3 Pro, Claude Sonnet 4.5, and more. It's currently available as a public preview, supporting Windows, macOS, and Linux.
:::
::: details Trae
### [Trae](https://www.trae.ai/)
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image8.png)
Trae is an AI programming assistant developed by ByteDance that supports over 100 programming languages and can be integrated into mainstream IDEs. Its features include: generating code from natural language, automatic debugging, and converting design mockups into React/Vue components. After its August 2025 update, Trae added smart dependency imports, rename suggestions, task checklist management, and more. SOLO mode also began supporting backend code generation and technical architecture document editing.
:::
::: details Cursor
### [Cursor](https://cursor.com/)
Cursor is an AI code editor developed by Anysphere, built on a customized VS Code, with optimizations focused on large-scale codebases and multi-file collaboration scenarios. It supports models like GPT-4o and Claude 3.7. The Claude Max mode introduced in 2025 can handle projects with millions of lines of code. The Pro version removed request limits, making it ideal for complex enterprise projects.
Currently, Cursor is arguably one of the best AI IDEs with a graphical interface in terms of overall experience, with a large user base and frequent feature updates. Its biggest drawback is the higher price — the Pro version costs about $20 per month.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image9.png)
:::
::: details Qoder
### [Qoder](https://qoder.com/)
Qoder is an AI IDE from Alibaba that emphasizes "transparent collaboration" and "enhanced context engineering capabilities." It supports breaking tasks into multiple steps through Action Flow and tracks AI execution in real time. It also supports multi-model dynamic routing and task state machine management, making it ideal for architecture governance in medium-to-large projects and "reverse engineering" analysis of legacy systems.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image10.png)
:::
::: details CodeBuddy
### [CodeBuddy](https://www.codebuddy.com/)
CodeBuddy is an AI programming tool from Tencent Cloud that emphasizes Chinese language command support and enterprise-grade compliance capabilities. It offers code completion, batch code review, and multi-model switching. Its Craft agent can perform multi-file code generation and API integration. The enterprise version supports private deployment and has passed Level 3 security certification, making it suitable for industries with high data security requirements such as finance and healthcare.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image11.png)
:::
::: details VS Code + Cline
### VS Code + [Cline](https://cline.bot/)
Cline is an AI programming Agent plugin for VS Code (Visual Studio Code) that can flexibly switch between different large models by configuring different API endpoints. Cline supports multimodal input, MCP tool extensions, and cost monitoring, with all operations requiring user confirmation before execution. It's ideal for quickly validating ideas or integrating with existing development workflows. Basic features are free, and the enterprise version supports deploying models in private environments.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image13.png)
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image14.png)
:::
::: details Kiro
### [Kiro](https://kiro.dev/)
Kiro is an AI programming IDE from AWS (Amazon Web Services), deeply integrated with Amazon Bedrock and the AWS cloud service ecosystem. It supports multiple large models including Claude and Nova, making it particularly suitable for development scenarios that require tight integration with AWS cloud services. Kiro provides smart code generation, automated testing, and seamless integration with AWS resources (such as Lambda, S3, DynamoDB), offering unique advantages for cloud-native application development.
> **Note**: If you want to use Anthropic Claude models, you'll need to use Cursor, Kiro, or Antigravity as your IDE. These IDEs have official partnerships or deep integrations with Anthropic, providing a more stable and complete Claude model experience.
:::
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="1" :items="[
{ title: 'Understanding the Environment', description: 'IDE vs AI IDE' },
{ title: 'Hands-on Practice', description: 'Build Snake with Trae' },
{ title: 'Tool Deep Dive', description: 'Explore the IDE Interface' },
{ title: 'Communication Skills', description: 'Talk to AI Effectively' }
]" />
</ClientOnly>
</div>
## 4. Hands-on: Build a Snake Game Locally with an AI IDE
The previous sections were mainly about "concepts" and "differences." In this section, we'll turn abstract concepts into concrete actions through a complete hands-on exercise: **Create a new empty folder -> Open it with an AI IDE -> Chat in the sidebar and have it build a Snake game from scratch using React.** Here we'll use Trae as our example, so first we need to install it and understand what Trae is.
::: tip 💡 Quick Tip: Seamless Transition from Web to Local
If you've previously developed projects on z.ai or other web-based AI programming platforms, you can download the code directly to your local machine and open it with an AI IDE to continue development. This way you can keep your previous work while enjoying the more powerful AI assistance of a local IDE.
The steps are simple:
1. Click the download button on platforms like z.ai to save the project locally
2. Unzip and open the folder with an AI IDE like Trae/Cursor
3. Continue chatting with AI in the sidebar to iterate and improve your project
:::
### 4.1 Preparation: Install and Learn About Trae
#### 4.1.1 What Is Trae
Trae's full name can be understood as "The Real AI Engineer." It's an adaptive AI Integrated Development Environment (IDE) developed by ByteDance. It's built on top of the popular VS Code, which means if you're already familiar with VS Code, you'll find Trae's interface layout and basic operations very familiar and comfortable.
Trae's core goal is to be a developer's "smart programming partner." Through deep AI integration, it can automatically handle a large amount of repetitive work, providing you with a more intuitive and efficient development experience. It's not just a "code completion tool" — it aims to assist throughout the entire development workflow, from creating projects, writing code, debugging, testing, to deployment.
#### 4.1.2 Installing Trae
Trae comes in an international version and a China version. The international version requires access to overseas networks but lets you use the latest overseas models like GPT-5. The China version primarily supports the latest domestic large models such as GLM, Qwen, Kimi, etc.
International version download: https://www.trae.ai/
China version download: https://www.trae.cn/
##### Trae Pricing and Usage Options
::: info 💡 Version Selection Tips
- If you're primarily using it in China, we recommend choosing the China version for more stable network access and support for domestic large models
- If you need to use overseas models like GPT-5 and your network conditions allow it, you can choose the international version
- If you already have a third-party model API Key, connecting third-party models gives you flexible cost control
:::
> 💡 **Currently recommended: Use OpenRouter free models for testing**
>
> As of the time this tutorial was written (2026-02-12), you can still try StepFun's models for free. See section 4.2 below for how to connect the model `stepfun/step-3.5-flash:free`.
Regarding Trae's costs and usage options, here are several choices:
- **China Version (Recommended)**: Basic usage is free, but due to high user volume, you may need to wait in a queue.
- **International Version**: Subscription costs about $3 per month, giving access to overseas models like GPT-5, but requires overseas network access.
- **Third-party Model Integration**: If you already have a Token API from a domestic large model provider (such as DeepSeek, Tongyi Qianwen, Kimi, etc.), you can connect these APIs through Trae's third-party model configuration. Major cloud service providers (such as Alibaba Cloud, Tencent Cloud, Baidu Cloud, etc.) typically offer Coding Plan subscriptions that let you use their large model APIs at more favorable prices. This way you can freely choose your preferred model while controlling costs.
We recommend beginners start with the free China version. If you encounter queuing issues or need more stable service, consider connecting a third-party model and purchasing the corresponding cloud provider's Coding Plan.
#### 4.1.3 Trae Interface Overview
In terms of interface design, Trae is very similar to the VS Code we use daily: the same classic three-column layout with a file explorer on the left, an editing area in the center, and an extension panel on the right.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image17.png)
The sidebar on the right is the Copilot interaction window, which can also be thought of as the Agent window. If you can't see it right away, click the sidebar icon in the top-right corner of Trae to open it.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image18.png)
After opening the sidebar, you'll see a `Builder` option — this is the Agent mode. Simply put, it's like a "local version" of z.ai that can operate your local environment, install runtime environments, open web pages, and more.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image19.png)
After clicking "Builder," you'll see "Chat" mode and "Builder with MCP" mode:
- **Chat Mode**: Primarily used for chatting about the code in your current folder, or as a general chat model. (You can open a folder through the "File" menu in the top-left corner and edit within that folder. In this case, any files Builder creates or modifies will only happen inside this folder.)
- **Builder with MCP Mode**: Provides the Agent with more available tools (such as connecting the language model with other software, querying weather, etc.). You can simply understand it as: MCP makes it easier for the language model to call various external tools.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image20.png)
In the area below, you'll also see model selection options — click to change the current large model. In the China version, you can choose domestic models like Kimi k2 or GLM. If you're using the international version of Trae, you can also select overseas models like ChatGPT or Claude. However, since domestic large models are developing very rapidly, Kimi, Qwen, GLM, and others already offer experiences close to Claude 3.5 or 3.7 in many tasks, which is more than sufficient for daily development. There's no strict requirement to use the international or China version here.
**Note that we don't recommend using Auto mode (automatic model selection). For the international version, we recommend using Gemini or GPT models. For the China version, we recommend trying domestic models like Kimi k2, Minimax, or GLM.** Different models suit different use cases — there's no dogmatic rule about which is better. When you hit a wall with one model, try switching to another. Through multiple tests, you'll find the best results for your own workflow.
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/image21.png)
That's a brief introduction to Trae. Next, let's revisit what we did previously on z.ai and try doing the same thing in Trae.
### 4.2 Step 1: Create an Empty Folder and Open It with an AI IDE
Before getting started, we first need to prepare a clean project working directory.
For this section's example, you can create a new empty folder named `snake-game-react` on your local machine.
Then, open your installed AI IDE, select "Open Folder" on the startup screen, and import the empty folder as the project root directory. You can also drag the folder directly into the IDE window to open it. At this point, the file explorer on the left won't show any code files, indicating that we're starting from a completely blank project state.
::: details 📚 Optional: Connect a Cloud Service Provider's API or Coding Plan
This section introduces how to connect a cloud service provider's API or Coding Plan for more stable and frequent model calls. Screenshots of the Trae integration are provided at the end.
**What Is a Coding Plan**
A Coding Plan is a subscription offered by major cloud service providers. After purchasing, you can **use the provider's large model API without limits or at high frequency** for a certain period. Compared to per-token billing, a Coding Plan is more like a "monthly package" — you pay a fixed fee and can use it freely without worrying about per-call charges.
**Why Purchase a Coding Plan**
You might ask: since you can call large models directly via API, why buy a Coding Plan? The main reason is: **unlimited usage**. The core advantage of a Coding Plan is that you can call the large model anytime, as frequently as you want, without worrying about costs exploding or constantly checking billing statements.
**Recommended Domestic Cloud Service Coding Plans**
Here are recommended Coding Plan options from major domestic cloud service providers:
- Zhipu AI (BigModel Plan): https://bigmodel.cn/glm-coding
- Volcengine (ByteDance Cloud AI Plan): https://www.volcengine.com/activity/codingplan
> 💡 **You can also directly connect a large model API**
> Besides Coding Plans, you can also directly connect various model APIs through Add Model. You can refer to the method below for connecting the OpenRouter StepFun free API to integrate it with Trae. Testing shows it meets basic programming needs.
> If you need to top up, we suggest starting with a small amount (e.g., 10 RMB) to see how long it lasts, such as with cost-effective models like DeepSeek.
**How to Connect a Coding Plan**
Connecting a Coding Plan is very simple and takes just a few minutes:
1. Visit your chosen cloud service provider's website (e.g., Zhipu AI: https://bigmodel.cn/glm-coding, Volcengine: https://www.volcengine.com/activity/codingplan)
2. Register an account and log in
3. Find the "Pricing" or "Coding Plan" page
4. Choose a plan that suits you and complete the payment
5. After payment, you'll receive an API Key or Plan ID
::: tip 🎯 Custom Model Recommendations
When connecting custom models in Trae, we **recommend using the OpenRouter approach by default**. OpenRouter provides a unified API interface for conveniently connecting to multiple large language models.
**As of February 12, 2026, you can still use StepFun's free API:**
- **`stepfun/step-3.5-flash:free`**: A free model from StepFun that can be directly connected in Trae.
**Other free models:**
- **`openrouter/free`**: A model option that uses free LLM APIs by default. You can use it directly in Trae's Custom Model integration (just enter the model ID), experiencing AI programming features without any cost.
These free options are great for beginners. Before committing to production use, you can familiarize yourself with the AI IDE workflow through these free options.
**Optional: Connect a Large Model API (Using DeepSeek as an Example)**
1. Visit the DeepSeek platform: https://platform.deepseek.com/usage
2. Register an account and log in
3. Purchase a 10 RMB token package on the top-up page
4. After topping up, create and copy an API Key on the API Keys page
5. In Trae, click **"Add Model"**, find DeepSeek, select the corresponding model, and enter the API Key to start using it
Through the interface below, you can successfully add a model (note: after selecting the model option, **make sure to scroll all the way to the bottom** — there's a "Custom Model" option. Click it to enter a model ID, where you can type the recommended model IDs like `stepfun/step-3.5-flash:free`. Also click "Get Key" below to visit the official website and obtain the corresponding API Key.)
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/index-2026-02-12-14-14-51.png)
![](../../../zh-cn/stage-1/1.1-introduction-to-ai-ide/images/index-2026-02-12-14-15-29.png)
:::
@@ -0,0 +1,287 @@
---
title: 'Adding AI Capabilities to Your Prototype - Integrating Text and Image APIs'
description: 'Integrate real AI capabilities into your existing web prototype: understand the core concepts of APIs, learn how to find API Keys and official examples; hands-on integration of DeepSeek text model and various image generation services (SiliconFlow Qwen-Image, Recraft, Seedream), and master common model selection methods.'
---
<script setup>
const duration = 'About <strong>1 day</strong>'
</script>
# Beginner Level 4: Injecting AI Capabilities into Your Prototype
## Chapter Introduction
<ChapterIntroduction :duration="duration" :tags="['API', 'Text Model', 'Text-to-Image', 'Prototype Integration']" coreOutput="Prototype integrated with 1 text model + 1 image model (optional)" expectedOutput="AI prototype capable of calling real APIs">
In the previous chapters, we completed the entire process from **finding a great idea** to **building a product prototype**. But the current prototype is still just a "shell" — clicking buttons won't actually generate content, and all the data on the page is hardcoded.
Remember what we emphasized in the first chapter? **We want to build "products people are willing to pay for," not "prototypes that just look good."** Real value comes from a product that can **solve real problems**, and to achieve that, the prototype must be able to **actually run**.
This chapter will bring your prototype **"to life"**: we'll integrate **real AI capabilities**, starting from obtaining an API Key, reading official documentation, and having the AI IDE help you integrate the interface into your code. Using **DeepSeek's text model** as an example, you'll learn how to make your application **actually call a large language model to generate content**; if you're interested, you can also **optionally integrate image generation**.
After completing this chapter, your prototype will **no longer be a static demo**, but rather **an application that can call real AI capabilities and solve real problems**.
</ChapterIntroduction>
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="0" :items="[
{ title: 'API Basics', description: 'Understand core concepts and security practices' },
{ title: 'Text Integration', description: 'DeepSeek text generation hands-on' },
{ title: 'Image Integration', description: 'VLM image understanding and generation' }
]" />
</ClientOnly>
</div>
# 1. API Fundamentals
As mentioned earlier, our goal is to "integrate AI capabilities" so that the prototype is no longer a static demo but a tool that can call real AI services. The key to achieving this lies in understanding and using APIs (Application Programming Interfaces).
API is an important abstraction concept in computer science. Simply put: **you send a request in the format the other party requires, and they send back a result in the same format**.
- **What you send out**: Usually includes a "key (API Key)" and "what you want to generate"
- **What they send back**: If successful, you get the result; if it fails, they tell you why (e.g., "invalid key," "insufficient balance," "incorrect parameters")
Specifically, you need to master the following core elements:
1. **API Key**: Your "pass" and also your "wallet key." Anyone who gets it can make API calls on your behalf and incur charges.
2. **Endpoint**: The specific path for the API request, telling the server which function you want to access. The full request URL is typically composed of "Base URL + Endpoint path." For example:
- Text generation: Base URL (`https://api.service.com`) + Endpoint (`/v1/chat/completions`) = Full URL `https://api.service.com/v1/chat/completions`
- Image generation: Base URL (`https://api.service.com`) + Endpoint (`/v1/images/generations`) = Full URL `https://api.service.com/v1/images/generations`
3. **Call/Request**: The process of sending a task to the AI service and getting results back
4. **Request Content**: The specific content you send to the AI, such as the topic you want the AI to write about, the description of the image to generate, etc.
5. **Response**: The content the AI returns after processing, such as the generated article, image, etc.
6. **Error Handling**: Knowing how to troubleshoot when problems occur (such as incorrect API Key, too many requests, etc.)
::: info ️ What is an API
For a more in-depth explanation of APIs, see the appendix: [Introduction to APIs](/zh-cn/appendix/4-server-and-backend/api-intro).
::: warning 🔐 **API Security Notes**
The API Key is your "pass" for requesting AI services — it's a secret string used for authentication and billing.
Since the API Key is directly linked to your account and charges, be sure to:
- **Never share it** in group chats, screenshots uploaded online, or public forums
- **Never hardcode it** into your code and commit it to a Git repository (especially public repositories)
- If you suspect your Key has been leaked, **replace it with a new Key immediately**
In the content below, we will **paste the API KEY directly into the AI IDE for operations**. **Don't do this in real projects!!** Since we're just practicing, it's fine for now. (Once you're more experienced, you can have the AI generate a configuration file and simply put the API KEY in the config file.)
:::
<div style="margin: 50px 0;">
<ClientOnly>
<StepBar :active="1" :items="[
{ title: 'API Basics', description: 'Understand core concepts and security practices' },
{ title: 'Text Integration', description: 'DeepSeek text generation hands-on' },
{ title: 'Image Integration', description: 'VLM image understanding and generation' }
]" />
</ClientOnly>
</div>
# 2. Integrating the Text Generation API: DeepSeek
Although APIs involve these technical concepts, the actual operation during the prototyping phase can be very simple and efficient. The core approach is:
> **Find the official example, get the API Key, and have the AI IDE help you wire it to a button.**
Once you've grasped these concepts, you'll find that whether you're integrating a text model or an image model, the underlying process is the same: when the user clicks a button, the frontend organizes the input and sends a request; after the API returns a result, it displays the result on the page. Let's verify this through hands-on practice.
In `1.2 Building Your Prototype`, you already created an interactive prototype. What we need to do next is turn the "AI-like features" in the prototype into real, working capabilities: **when the user clicks a button, the prototype sends a request to an external AI service and displays the returned text.**
::: info ️ Further Reading on Principles
If you want to learn more about the underlying principles, check out the appendix: [Introduction to Large Language Models (LLM)](/zh-cn/appendix/8-artificial-intelligence/llm-principles).
::: details Learn More: What is DeepSeek?
**Hangzhou DeepSeek Artificial Intelligence Basic Technology Research Co., Ltd.**, operating under the brand name DeepSeek, is a **Chinese artificial intelligence (AI) company that develops large language models (LLMs)**. DeepSeek is headquartered in Hangzhou, Zhejiang, and is owned and funded by the Chinese hedge fund High-Flyer. DeepSeek was founded in July 2023 by Liang Wenfeng, co-founder of High-Flyer, who also serves as CEO of both companies. The company launched its eponymous chatbot and its DeepSeek-R1 model in January 2025.
Let's look at how DeepSeek compares with other top models in the GPQA benchmark rankings. Notably, DeepSeek is an open-source model (anyone can download the model from the internet), while other common models like Grok, Google Gemini, and ChatGPT are closed-source. As we can see, DeepSeek has largely caught up with the first tier of models.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-14-16-48.png)
GPQA stands for "Graduate-Level Google-Proof Q&A Benchmark," a graduate-level benchmark for scientific question-answering tasks. Here's a detailed introduction.
GPQA contains 448 multiple-choice questions covering subfields of biology, physics, and chemistry, such as quantum mechanics, organic chemistry, molecular biology, and more. These questions were written by 61 experts who hold or are pursuing doctoral degrees and have undergone a rigorous validation process.
:::
Follow these 3 steps to quickly integrate a large model generation API:
1. **Create an API Key on the DeepSeek platform**
2. **Find the text generation example in the DeepSeek documentation** (there's usually ready-made code you can copy directly)
3. **Open the AI IDE, paste in the API Key + official example**, and tell the AI what functionality to implement:
> Help me integrate this large model's API to support the copywriting generation task for this application
Next, we'll walk through a demo. You can follow along with the entire process. First, register a [DeepSeek](https://platform.deepseek.com/usage) account, create an API Key, and top up a small amount for testing.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-13-57-41.png)
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-13-58-13.png)
Click "API KEYS" and find "create new API key" at the bottom of the screen. You'll end up with an API key that looks something like sk-8573341c39fc44315aadc071c53rh7d2.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-13-58-32.png)
Once you have the key, you have permission to call the model.
At this point, you can directly read the [API](https://api-docs.deepseek.com/) documentation, which typically provides curl or Python call examples.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-13-58-56.png)
After finding the example, you can copy all the content from the documentation along with your key into the AI IDE's chat box, asking it to help you integrate the large language model into the prototype you've already developed.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-13-59-31.png)
Here's a reference prompt:
```
Based on this API call method, help me implement a copywriting generation feature that can generate Douyin (TikTok) e-commerce copy in various styles based on product information when clicked.
Reference materials:
api key: sk-8573341c39aefa1efe
api request reference:
curl \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"stream": false
}'
```
After some AI code generation, you'll easily get a corresponding copywriting generation button to test. If you can't find the entry point, you can ask the AI IDE to tell you which page leads to it. If you really can't find it, you can ask the AI IDE to directly refactor and improve based on your ideas to get the final copywriting generation result.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-14-23-23.png)
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-14-26-35.png)
Of course, you might be wondering: how do I know it's actually calling the large model and not just returning hardcoded responses? You can enter custom copy and have the large model generate corresponding content based on your custom analysis specified on the spot.
If you find that the results are different each time and logically coherent, you can be confident that the API is being called correctly. You can also check the [API usage management platform](https://platform.deepseek.com/usage) to see if the calls were successful (though it may take a few minutes to show up).
# 3. Integrating the Image-to-Text API: Qwen3 VL
::: info ️ Further Reading on Principles
If you want to learn more about the underlying principles, check out the appendix: [Introduction to Vision Language Models (VLM)](/zh-cn/appendix/8-artificial-intelligence/multimodal-models).
::: details Learn More: What is Qwen3 VL?
**Qwen3 VL** is the latest version in the multimodal vision-language model series developed by Alibaba Cloud's Tongyi Qianwen team. VL stands for "Vision-Language," meaning it's a vision-language model. It can understand image content and generate text descriptions based on images, answer questions about images, extract information from images, and more.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-14-48-27.png)
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-14-48-41.png)
**Key capabilities of Qwen3 VL include:**
- **Image Understanding**: Can recognize objects, scenes, people, text, and other content in images
- **Visual Q&A**: Accurately answers questions about images based on user queries
- **Image Captioning**: Generates detailed or concise text descriptions of images
- **Multi-image Understanding**: Supports processing multiple images simultaneously for comparative analysis
- **Text Extraction**: Extracts text content from images (OCR capability)
**Why choose Qwen3 VL?**
Compared to the previous generation, Qwen3 VL has significantly improved image understanding accuracy and supports longer, more complex image analysis tasks. It excels in Chinese language understanding, has relatively low API call costs, and offers good value for money. Additionally, its larger context window enables it to handle more complex visual reasoning tasks.
**Typical use cases:**
- E-commerce: Automatically generate titles, descriptions, and selling points from product images
- Content creation: Automatically generate copy or image suggestions based on reference images
- Office: Image content extraction, automatic report recognition
- Education: Automatic parsing of image-based questions, knowledge point extraction
:::
In the previous section, we explained how to integrate a text generation API. But for the application scenario above, we'll notice a problem: we're uploading an image, and if we only use a large language model, it can't understand the content of the image very well, so the generated results may be off.
We want a model that can help us turn an image into a text description — this requires a Vision Language Model (VLM). In our case, we'll use a vision language model to generate product selling point descriptions, improving the user experience.
For convenience, we'll use the API provided by [SiliconFlow cloud platform](https://cloud.siliconflow.cn/me) to integrate the image-to-text API.
::: details Learn More: What is SiliconFlow?
**SiliconFlow** is a well-known AI model aggregation platform in China, providing API services for various mainstream large language models and vision language models.
**Platform features:**
- **Multi-model support**: Integrates various mainstream AI models, including DeepSeek, Qwen, Llama series, and other open-source models
- **Technical optimization**: Optimized inference for open-source models, providing low-latency, high-concurrency API services
- **Interface compatibility**: Provides OpenAI-compatible API interfaces for easy integration with existing applications
- **Pay-as-you-go**: Supports usage-based billing
SiliconFlow is relatively mature in inference services for open-source large models and is a common choice for using domestic open-source AI models.
:::
Go to the SiliconFlow platform homepage, where you'll see many models to choose from. Find the filter in the upper left corner, click to expand it, select the "Vision" tag, and you'll see many image-to-text models, such as Zhipu GLM-4.6V or Qwen3-VL.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-15-05-04.png)
You can choose any one to test. Here we'll use `Qwen/Qwen3-VL-8B-Instruct` as an example.
![](../../../zh-cn/stage-1/1.3-integrating-ai-capabilities/images/index-2026-01-20-15-07-44.png)
Go to the [SiliconFlow platform](https://cloud.siliconflow.cn/me/account/ak), click "Create New API Key" in the API Keys section to create a new API Key.
You can directly use the code below as reference code, and send it along with the generated API Key to the AI IDE for feature integration.
::: details Image-to-Text Reference Code
```python
from openai import OpenAI
from typing import Dict, Any, List
import base64
import os
SILICONFLOW_API_KEY: str = ""
SILICONFLOW_BASE_URL: str = "https://api.siliconflow.cn/v1/"
MODEL_NAME: str = "Qwen/Qwen3-VL-8B-Instruct"
def encode_image(image_path: str) -> str:
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
def get_vlm_completion(client: OpenAI, messages: List[Dict[str, Any]]) -> str:
response = client.chat.completions.create(
model=MODEL_NAME,
messages=messages,
max_tokens=512,
temperature=0.7,
top_p=0.7,
frequency_penalty=0.5,
stream=False,
n=1
)
return response.choices[0].message.content
def caption_image(image_path: str) -> str:
base64_image = encode_image(image_path)
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Please describe this image in detail."
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
]
}
]
client = OpenAI(
api_key=SILICONFLOW_API_KEY,
base_url=SILICONFLOW_BASE_URL
)
return get_vlm_completion(client, messages)
image_path = "images.jpg"
caption = caption_image(image_path)
```
:::
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+212 -1
View File
@@ -1,3 +1,214 @@
# 数据治理与数据质量
> 待实现
::: tip 前言
**你有没有遇到过这种情况:报表上的数字和实际业务对不上,两个系统里同一个用户的信息不一样,或者分析结果因为脏数据完全不可信?** 数据治理就是解决这些问题的系统性方法。在"数据驱动决策"的时代,数据质量直接决定了决策质量——垃圾进,垃圾出(Garbage In, Garbage Out)。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **数据质量维度**:理解完整性、准确性、一致性等六大质量维度
- **数据治理体系**:了解从组织、流程到技术的治理框架
- **数据血缘**:掌握数据从源头到消费的全链路追踪
- **元数据管理**:理解"描述数据的数据"的重要性
- **数据分层架构**:掌握 ODS → DWD → DWS → ADS 的数仓分层模型
- **实战能力**:知道如何在项目中落地数据治理
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 数据质量维度 | 完整性、准确性、一致性、时效性 |
| **第 2 章** | 数据治理框架 | 组织、流程、技术、文化 |
| **第 3 章** | 数据血缘追踪 | 影响分析、问题排查、合规审计 |
| **第 4 章** | 元数据管理 | 技术元数据、业务元数据、操作元数据 |
| **第 5 章** | 数据分层架构 | ODS、DWD、DWS、ADS |
| **第 6 章** | 治理工具与实践 | Great Expectations、dbt、DataHub |
---
## 0. 全景图:为什么需要数据治理?
数据治理不是一个技术问题,而是一个**管理问题**。它回答的核心问题是:**谁对数据负责?数据的标准是什么?如何保证数据持续可信?**
想象一个公司有 100 个数据表,每个表由不同团队维护,没有统一的命名规范、没有数据字典、没有质量检查。结果就是:同一个"月活用户"指标,市场部算出来 500 万,产品部算出来 300 万——因为定义不一样。
::: tip 数据治理的四个支柱
1. **组织**:明确数据 Owner、数据管家(Data Steward)的角色和职责
2. **流程**:建立数据接入、变更、下线的标准流程
3. **技术**:部署数据质量监控、元数据管理、血缘追踪等工具
4. **文化**:让全公司认同"数据是资产",而不是"数据是副产品"
:::
---
## 1. 数据质量的六个维度
数据质量不是一个模糊的概念,而是可以从六个具体维度来衡量的。每个维度都有明确的定义和检测方法。
<DataQualityDemo />
| 维度 | 定义 | 检测方法 | 常见问题 |
|------|------|---------|---------|
| 完整性 | 数据是否存在缺失 | 空值率检查 | 必填字段为空、关联数据缺失 |
| 准确性 | 数据是否正确 | 规则校验、抽样核对 | 金额为负、日期不合法 |
| 一致性 | 多源数据是否一致 | 跨系统比对 | CRM 和订单系统用户名不同 |
| 时效性 | 数据是否及时更新 | 更新时间检查 | 库存数据滞后、价格未同步 |
| 唯一性 | 是否存在重复记录 | 去重检查 | 同一用户注册两次 |
| 有效性 | 是否符合格式规则 | 正则/范围校验 | 邮箱格式错误、年龄为负数 |
::: tip 数据质量的 1-10-100 法则
- **1 元**:在数据入口做校验,预防脏数据进入
- **10 元**:在数据仓库中清洗已有的脏数据
- **100 元**:因为脏数据导致错误决策的损失
越早发现和修复数据质量问题,成本越低。
:::
---
## 2. 数据治理框架:全生命周期管理
数据治理不是一次性项目,而是贯穿数据全生命周期的持续过程。从数据的产生到销毁,每个阶段都需要明确的规范和责任人。
<DataGovernanceFrameworkDemo />
| 阶段 | 核心产出 | 关键角色 |
|------|---------|---------|
| 定义标准 | 数据字典、命名规范、分类分级标准 | 数据架构师 |
| 采集接入 | 接入规范、校验规则、血缘记录 | 数据工程师 |
| 存储管理 | 分层模型、权限矩阵、生命周期策略 | DBA / 平台工程师 |
| 使用消费 | 数据目录、脱敏规则、质量报告 | 数据分析师 / 业务方 |
| 归档销毁 | 归档策略、删除记录、审计日志 | 安全合规团队 |
## 2. 数据治理框架
数据治理不是买一个工具就能解决的,它需要一套完整的框架来支撑。业界最常用的参考框架是 DAMA-DMBOK(数据管理知识体系)。
| 治理领域 | 核心内容 | 关键产出 |
|---------|---------|---------|
| 数据架构 | 定义数据模型、数据流、存储策略 | 数据架构图、ER 图 |
| 数据标准 | 统一命名规范、编码规范、指标定义 | 数据字典、指标库 |
| 数据质量 | 建立质量规则、监控告警、修复流程 | 质量报告、SLA 仪表盘 |
| 数据安全 | 分级分类、访问控制、脱敏加密 | 安全策略、审计日志 |
| 主数据管理 | 统一客户、商品等核心实体的"黄金记录" | 主数据中心 |
| 数据生命周期 | 管理数据从创建到归档到销毁的全过程 | 保留策略、归档规则 |
::: tip 数据治理的成熟度模型
- **Level 1 - 初始级**:没有统一标准,各团队各自为政
- **Level 2 - 可重复级**:有基本的规范文档,但执行不一致
- **Level 3 - 已定义级**:有统一的治理流程和工具,大部分团队遵守
- **Level 4 - 已管理级**:有量化的质量指标和自动化监控
- **Level 5 - 优化级**:持续改进,数据治理融入日常开发流程
:::
---
## 3. 数据血缘:从哪来,到哪去
数据血缘(Data Lineage)记录了数据从源头到最终消费的完整流转路径。它就像数据的"族谱",让你能追溯任何一个数据的来龙去脉。
<DataLineageDemo />
数据血缘在实际工作中有三个核心应用场景:
| 场景 | 问题 | 血缘如何帮助 |
|------|------|------------|
| 影响分析 | 要修改用户表的字段,会影响哪些下游报表? | 沿血缘向下追踪所有依赖 |
| 根因定位 | 今天的 GMV 报表数据异常,问题出在哪一步? | 沿血缘向上回溯每个环节 |
| 合规审计 | 用户的手机号经过了哪些系统?是否都做了脱敏? | 追踪敏感字段的全链路流转 |
::: tip 血缘采集的两种方式
- **主动采集**:解析 SQL 语句、ETL 配置,自动提取表级/字段级血缘关系
- **被动采集**:通过 Hook 拦截查询引擎(如 Hive、Spark)的执行计划,实时记录血缘
主流工具如 Apache Atlas、DataHub、OpenLineage 都支持自动化血缘采集。
:::
---
## 4. 元数据管理:"描述数据的数据"
元数据(Metadata)是关于数据的数据。如果数据是一本书的内容,元数据就是书的目录、作者、出版日期、ISBN 号。没有元数据,数据就是一堆无法理解的数字和字符串。
| 元数据类型 | 描述 | 示例 |
|-----------|------|------|
| 技术元数据 | 数据的物理存储信息 | 表名、字段类型、分区方式、存储位置 |
| 业务元数据 | 数据的业务含义 | 字段中文名、业务定义、计算口径 |
| 操作元数据 | 数据的运行状态 | ETL 执行时间、数据量、更新频率 |
::: tip 数据字典的重要性
数据字典是元数据管理最基础的产出。一个好的数据字典应该包含:
- **字段名**:英文名和中文名
- **数据类型**VARCHAR(50)、INT、DATETIME 等
- **业务定义**:这个字段代表什么?怎么计算的?
- **取值范围**:有效值是什么?空值是否允许?
- **负责人**:谁维护这个字段?有问题找谁?
没有数据字典的团队,新人入职后理解一张表的含义可能需要一周;有数据字典的团队,10 分钟就够了。
:::
---
## 5. 数据分层架构:ODS → DWD → DWS → ADS
数据仓库不是把所有数据堆在一起,而是按照**加工程度**分层存储。每一层有明确的职责,上层依赖下层,逐步从原始数据提炼为业务可用的数据。
| 层级 | 全称 | 职责 | 数据特点 |
|------|------|------|---------|
| ODS | 操作数据层 | 原样同步业务数据库 | 最原始,未经处理 |
| DWD | 明细数据层 | 清洗、标准化、去重 | 干净的明细记录 |
| DWS | 汇总数据层 | 按主题聚合(日/周/月) | 预计算的聚合指标 |
| ADS | 应用数据层 | 面向具体报表/接口 | 直接可用的结果数据 |
::: tip 为什么要分层?
- **复用**:DWD 层清洗一次,所有上层共享,避免重复清洗
- **解耦**:业务库表结构变更只影响 ODS 层,不会波及报表
- **性能**:DWS 层预聚合,报表查询直接读取,不需要实时计算
- **可追溯**:每一层都保留,出问题时可以逐层排查
:::
---
## 6. 治理工具与实践
| 工具 | 定位 | 核心能力 | 适用场景 |
|------|------|---------|---------|
| Great Expectations | 数据质量 | 声明式数据校验规则,自动生成质量报告 | Python 数据管道 |
| dbt | 数据转换 | SQL 模型化开发,内置测试和文档生成 | 数仓建模 |
| DataHub | 元数据管理 | 数据目录、血缘追踪、数据发现 | 企业级数据治理 |
| Apache Atlas | 元数据管理 | Hadoop 生态血缘追踪 | 大数据平台 |
| OpenMetadata | 元数据管理 | 开源数据目录,支持多种数据源 | 中小团队 |
| Amundsen | 数据发现 | 搜索式数据发现平台 | 数据民主化 |
::: tip 从零开始的治理路径
如果你的团队还没有数据治理,建议按这个顺序推进:
1. **先建数据字典**:把现有的表和字段含义记录下来(哪怕用 Excel)
2. **加质量检查**:在关键数据管道中加入基本的空值、范围校验
3. **统一指标定义**:把"日活""月活""GMV"等核心指标的计算口径统一
4. **引入工具**:当手动管理成本太高时,引入 DataHub 或 dbt 等工具
5. **建立流程**:数据变更需要评审,质量问题有 SLA 和告警
:::
---
## 总结
数据治理是让数据从"能用"变成"好用、可信、可追溯"的系统性工程。它不是一次性项目,而是持续运营的过程。
回顾本章的关键要点:
1. **六大质量维度**:完整性、准确性、一致性、时效性、唯一性、有效性
2. **治理四支柱**:组织、流程、技术、文化缺一不可
3. **数据血缘**:追踪数据的来龙去脉,支撑影响分析和问题排查
4. **元数据管理**:数据字典是最基础也最重要的治理产出
5. **分层架构**ODS → DWD → DWS → ADS,逐层提炼数据价值
6. **渐进式落地**:从数据字典开始,逐步引入工具和流程
## 延伸阅读
- [DAMA-DMBOK](https://www.dama.org/cpages/body-of-knowledge) - 数据管理知识体系,数据治理的"圣经"
- [DataHub](https://datahubproject.io/) - LinkedIn 开源的元数据管理平台
- [Great Expectations](https://greatexpectations.io/) - Python 数据质量框架
- [dbt](https://www.getdbt.com/) - 数据转换工具,内置测试和文档
- [Apache Atlas](https://atlas.apache.org/) - Hadoop 生态的元数据治理框架
- [The Data Warehouse Toolkit](https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/books/) - Kimball 数仓建模经典
@@ -1,3 +1,299 @@
# 数据可视化与仪表盘
> 待实现
::: tip 前言
**一张好的图表胜过一千行数据。** 数据可视化是将抽象的数字转化为直观的视觉表达,让人能在几秒内理解数据背后的故事。从 Excel 图表到 Grafana 监控大屏,可视化无处不在。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **图表选择**:根据数据目的选择最合适的图表类型
- **可视化原则**:掌握数据可视化的核心设计原则
- **仪表盘设计**:了解不同类型仪表盘的布局模式
- **工具生态**:熟悉主流可视化工具的定位和选型
- **常见陷阱**:避免误导性图表和常见的可视化错误
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 图表类型选择 | 比较、趋势、占比、分布、关系 |
| **第 2 章** | 可视化设计原则 | 数据墨水比、一致性、可读性 |
| **第 3 章** | 仪表盘布局 | 概览型、对比型、下钻型、实时型 |
| **第 4 章** | 工具选型 | ECharts、D3、Grafana、Metabase |
| **第 5 章** | 常见陷阱 | 截断坐标轴、3D 饼图、颜色滥用 |
---
## 0. 全景图:为什么需要可视化?
人类大脑处理视觉信息的速度比处理文字快 6 万倍。一张折线图能让你在 1 秒内看出"上个月销售额在下降",而同样的信息如果用表格呈现,你可能需要 30 秒才能得出结论。
可视化的核心价值:
- **发现模式**:趋势、周期、异常值在图表中一目了然
- **辅助决策**:让非技术人员也能理解数据,参与决策
- **沟通效率**:一图胜千言,减少数据解读的歧义
::: tip 可视化 ≠ 好看
可视化的目标是**传达信息**,不是炫技。一个朴素但准确的柱状图,远比一个花哨但难以理解的 3D 图表更有价值。
:::
---
## 1. 图表类型选择:用对图表讲对故事
选择图表的第一步不是"我喜欢什么图表",而是"我想传达什么信息"。不同的数据目的对应不同的最佳图表类型。
<ChartTypeSelectorDemo />
### 图表选择速查表
| 数据目的 | 推荐图表 | 不推荐 | 原因 |
|---------|---------|--------|------|
| 比较大小 | 柱状图、条形图 | 饼图 | 人眼对长度差异比角度差异更敏感 |
| 展示趋势 | 折线图、面积图 | 柱状图 | 折线的连续性暗示时间的连续性 |
| 展示占比 | 饼图(≤5 类)、堆叠柱状图 | 3D 饼图 | 3D 透视会扭曲面积比例 |
| 展示分布 | 直方图、箱线图 | 折线图 | 分布需要看频率,不是趋势 |
| 展示关系 | 散点图、气泡图 | 柱状图 | 两个连续变量的关系需要二维空间 |
::: tip 一个简单的决策规则
- **一个变量** → 直方图(分布)或数字卡片(KPI)
- **两个变量** → 折线图(时间 vs 数值)或散点图(数值 vs 数值)
- **多个类别** → 柱状图(比较)或饼图(占比,≤5 类)
- **多维度** → 雷达图或平行坐标图
:::
---
## 2. 可视化设计原则
Edward Tufte 在《The Visual Display of Quantitative Information》中提出了数据可视化的核心原则,至今仍是行业标准。
| 原则 | 说明 | 反面案例 |
|------|------|---------|
| 数据墨水比 | 图表中用于展示数据的"墨水"占比应尽量高 | 过多的网格线、装饰性元素 |
| 最小化非数据元素 | 去除不传达信息的视觉元素 | 3D 效果、阴影、渐变背景 |
| 一致的比例尺 | 坐标轴从零开始,刻度均匀 | Y 轴从 95 开始(夸大差异) |
| 合理的颜色使用 | 用颜色编码信息,而非装饰 | 彩虹色(无序)表示有序数据 |
| 清晰的标注 | 标题、轴标签、图例缺一不可 | 没有单位、没有时间范围 |
::: tip 颜色使用的三条规则
1. **同一指标用同一颜色**:收入在所有图表中都用蓝色,不要一会蓝一会绿
2. **有序数据用渐变色**:温度从低到高用蓝→红渐变,不要用离散色
3. **考虑色盲友好**:约 8% 的男性有红绿色盲,避免仅用红绿区分关键信息
:::
---
## 2. 可视化设计原则:让数据说话
好的可视化不是"好看",而是"好懂"。Edward Tufte 在《The Visual Display of Quantitative Information》中提出了几个经典原则,至今仍是可视化设计的黄金标准。
### 2.1 数据墨水比(Data-Ink Ratio
> 图表中用于表达数据的"墨水"占总"墨水"的比例应该尽可能高。
简单说:**删掉一切不传达信息的元素**。
| 应该删掉的 | 应该保留的 |
|-----------|-----------|
| 3D 效果、阴影、渐变 | 数据点、坐标轴标签 |
| 多余的网格线 | 关键参考线(如目标值) |
| 装饰性图标 | 图例(当有多系列时) |
| 花哨的背景色 | 清晰的标题和单位 |
### 2.2 一致性原则
- **颜色一致**:同一个维度在不同图表中用同一种颜色(如"收入"始终用蓝色)
- **比例一致**:坐标轴从 0 开始(除非有充分理由),避免误导
- **时间一致**:时间轴的间隔应该均匀,不要跳跃
### 2.3 可读性原则
- **标题要说结论**:不是"月度销售额",而是"销售额连续 3 个月下降"
- **标注关键点**:在异常值、拐点处加标注,引导读者注意力
- **控制信息密度**:一张图表传达 1-2 个核心信息,不要塞太多
::: tip Tufte 的名言
"Excellence in statistical graphics consists of complex ideas communicated with clarity, precision, and efficiency."(优秀的统计图形,是用清晰、精确、高效的方式传达复杂的想法。)
:::
---
## 3. 仪表盘布局:不同场景,不同模式
仪表盘(Dashboard)是多个图表的有机组合。好的仪表盘不是把图表堆在一起,而是根据使用场景选择合适的布局模式。
<DashboardLayoutDemo />
### 四种常见布局模式
| 布局模式 | 核心结构 | 适用场景 | 设计要点 |
|---------|---------|---------|---------|
| 全局概览型 | KPI 卡片 + 趋势图 + 明细表 | 管理层日报、运营大盘 | 核心指标放最上方,一眼看到关键数字 |
| 对比分析型 | 左右对称布局 | A/B 测试、同环比分析 | 保持对比维度一致,突出差异 |
| 下钻分析型 | 从汇总到明细逐层展开 | 销售分析、用户行为分析 | 支持点击交互,逐层深入 |
| 实时监控型 | 大数字 + 实时曲线 + 告警状态 | 双十一大屏、服务器监控 | 自动刷新,深色背景,适合投屏 |
### 仪表盘设计的 5 个原则
1. **先问"谁在看"**:CEO 看战略指标,运营看过程指标,工程师看技术指标
2. **5 秒规则**:用户应该在 5 秒内理解仪表盘的核心信息
3. **信息层次**:最重要的放左上角(F 型阅读模式),次要的放下方
4. **减少滚动**:一屏展示核心内容,避免用户需要滚动才能看到关键数据
5. **留白**:不要塞满每一寸空间,适当留白让视觉更舒适
::: tip 仪表盘 vs 报表
- **仪表盘**:实时/准实时,交互式,面向监控和快速决策
- **报表**:定期生成(日/周/月),静态,面向详细分析和存档
两者不是替代关系,而是互补关系。仪表盘发现问题,报表深入分析。
:::
---
## 4. 工具选型:从代码到拖拽
| 工具 | 类型 | 特点 | 适用场景 |
|------|------|------|---------|
| ECharts | JS 图表库 | 百度开源,图表类型丰富,中文文档完善 | 前端项目内嵌图表 |
| D3.js | JS 可视化库 | 底层灵活,可定制任意可视化效果 | 高度定制化需求 |
| Chart.js | JS 图表库 | 轻量简单,上手快 | 简单图表需求 |
| Grafana | 监控仪表盘 | 支持多数据源,实时刷新,告警集成 | 服务器/应用监控 |
| Metabase | BI 工具 | 开源,SQL 查询 + 拖拽建图 | 业务数据分析 |
| Superset | BI 工具 | Apache 开源,支持大数据源 | 企业级数据探索 |
| Tableau | 商业 BI | 拖拽式,交互能力强 | 企业级报表 |
::: tip 怎么选?
- **开发者做项目内嵌图表** → ECharts(功能全)或 Chart.js(轻量)
- **需要高度定制的可视化** → D3.js(学习曲线陡但无限可能)
- **运维监控大屏** → Grafana(行业标准)
- **业务团队自助分析** → Metabase(简单)或 Superset(功能强)
:::
---
## 4. 工具选型:从代码库到 BI 平台
可视化工具可以分为三个层次:底层绑定库、高层图表库、BI 平台。选择哪个取决于你的需求复杂度和团队技术能力。
### 4.1 代码级图表库
| 工具 | 语言/平台 | 特点 | 适用场景 |
|------|----------|------|---------|
| ECharts | JavaScript | 开箱即用,图表类型丰富,中文文档完善 | 业务系统内嵌图表 |
| D3.js | JavaScript | 底层灵活,可定制任何可视化效果 | 高度定制化的数据可视化 |
| Chart.js | JavaScript | 轻量简单,上手快 | 简单的图表需求 |
| Matplotlib | Python | 科学计算标准库,静态图表 | 数据分析、论文图表 |
| Plotly | Python/JS | 交互式图表,支持 3D | 数据探索、Jupyter Notebook |
### 4.2 BI 平台(无代码/低代码)
| 工具 | 定位 | 核心优势 | 适用团队 |
|------|------|---------|---------|
| Grafana | 监控可视化 | 时序数据支持好,告警集成 | 运维/SRE 团队 |
| Metabase | 轻量 BI | 开源免费,SQL 即可出图 | 中小团队快速搭建 |
| Apache Superset | 企业 BI | 开源,支持大数据源 | 有数据团队的公司 |
| Tableau | 商业 BI | 拖拽式操作,可视化效果好 | 业务分析师 |
| Power BI | 商业 BI | 与微软生态集成好 | 使用微软技术栈的企业 |
::: tip 选型建议
- **开发者做产品内嵌图表** → ECharts(中文生态好)或 Chart.js(简单场景)
- **数据分析师做探索分析** → Plotly + Jupyter 或 Metabase
- **运维监控大屏** → Grafana(事实标准)
- **业务团队自助分析** → Metabase(开源)或 Tableau(商业)
- **需要高度定制** → D3.js(学习曲线陡峭,但无所不能)
:::
---
## 5. 常见陷阱:这些图表在骗你
数据可视化是一把双刃剑——用得好能揭示真相,用得不好会制造谎言。以下是最常见的可视化陷阱,每个数据从业者都应该能识别。
### 5.1 截断坐标轴
把 Y 轴的起点从 0 改成一个较大的数字,会让微小的差异看起来像巨大的变化。
| 场景 | 真实差异 | 视觉感受 |
|------|---------|---------|
| Y 轴从 0 开始 | A 产品 98 分,B 产品 95 分 | 差距很小 |
| Y 轴从 90 开始 | 同样的数据 | A 看起来是 B 的好几倍 |
**什么时候可以截断?** 当数据的绝对值很大但变化很小时(如股价从 100 到 105),截断是合理的——但必须明确标注。
### 5.2 3D 饼图的透视陷阱
3D 透视会让靠近观察者的扇区看起来更大。一个 25% 的扇区在 3D 视角下可能看起来像 35%。
**解决方案**:永远不要用 3D 饼图。用普通饼图或环形图,或者干脆用柱状图。
### 5.3 颜色滥用
| 错误做法 | 正确做法 |
|---------|---------|
| 用红绿表示数据(色盲不友好) | 用蓝橙等色盲安全配色 |
| 每个类别用不同颜色(彩虹图) | 同一系列用同色系深浅变化 |
| 用颜色编码连续数据但不加图例 | 始终提供颜色图例和数值标注 |
| 背景色和数据色对比度不够 | 确保 WCAG AA 级对比度 |
### 5.4 其他常见错误
| 陷阱 | 问题 | 修复 |
|------|------|------|
| 双 Y 轴 | 两个不相关的指标共享 X 轴,暗示因果关系 | 拆成两张图,或明确说明无因果 |
| 面积误导 | 用圆的半径而非面积表示数值 | 数值翻倍时面积翻倍,不是半径翻倍 |
| 时间轴不均匀 | 1月、3月、12月的间距一样 | 按实际时间比例排列 |
| 过多类别 | 饼图有 15 个扇区 | 超过 5 个类别就用柱状图或合并"其他" |
::: tip 可视化的道德准则
可视化的目的是**帮助理解**,不是**操纵认知**。每次做图表时问自己:
- 如果我是读者,这张图会不会让我产生错误的结论?
- 我是否隐藏了不利的数据?
- 坐标轴、比例、颜色是否公正地呈现了数据?
:::
---
## 总结
数据可视化是数据价值传递的"最后一公里"。选对图表、遵循设计原则、避免常见陷阱,就能让数据真正"说话"。
回顾本章的关键要点:
1. **先问目的再选图表**:比较用柱状图、趋势用折线图、占比用饼图
2. **数据墨水比**:删掉一切不传达信息的视觉元素
3. **仪表盘有模式**:概览型、对比型、下钻型、实时型各有适用场景
4. **工具按需选**ECharts 做产品、Grafana 做监控、Metabase 做分析
5. **警惕视觉陷阱**:截断坐标轴、3D 饼图、颜色滥用都会误导读者
## 延伸阅读
- [ECharts 官方文档](https://echarts.apache.org/zh/index.html) - 最流行的中文图表库
- [D3.js](https://d3js.org/) - 数据驱动的可视化库
- [Grafana](https://grafana.com/) - 开源监控可视化平台
- [The Visual Display of Quantitative Information](https://www.edwardtufte.com/tufte/books_vdqi) - Tufte 的可视化经典
- [From Data to Viz](https://www.data-to-viz.com/) - 图表类型选择指南
---
## 总结
数据可视化是数据价值传递的"最后一公里"。再好的分析,如果不能被正确理解,就等于没有分析。
回顾本章的关键要点:
1. **选对图表**:根据数据目的(比较、趋势、占比、分布、关系)选择图表类型
2. **设计原则**:高数据墨水比、一致性、可读性是三大核心原则
3. **仪表盘布局**:概览型、对比型、下钻型、实时型四种模式覆盖大部分场景
4. **工具选型**:从 ECharts 到 Grafana,根据团队能力和需求复杂度选择
5. **避免陷阱**:截断坐标轴、3D 饼图、颜色滥用是最常见的误导手段
## 延伸阅读
- [The Visual Display of Quantitative Information](https://www.edwardtufte.com/tufte/books_vdqi) - Edward Tufte 的可视化经典
- [ECharts 官方文档](https://echarts.apache.org/zh/index.html) - 最流行的中文图表库
- [D3.js](https://d3js.org/) - 最强大的底层可视化库
- [Grafana](https://grafana.com/) - 监控可视化事实标准
- [From Data to Viz](https://www.data-to-viz.com/) - 图表类型选择决策树
- [ColorBrewer](https://colorbrewer2.org/) - 色盲安全的配色方案工具
@@ -1,3 +1,253 @@
# 分布式系统的挑战
> 待实现
::: tip 前言
**当一台机器不够用时,问题才真正开始。** 分布式系统是现代互联网的基石——从微信消息到淘宝下单,背后都是成百上千台机器协同工作。但"分布式"不是免费的午餐,它带来了一系列单机系统从未遇到的挑战。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **核心定理**:理解 CAP 定理及其对系统设计的影响
- **一致性模型**:区分强一致性、最终一致性、因果一致性
- **八大挑战**:掌握分布式系统面临的核心难题
- **共识算法**:了解 Paxos、Raft 等分布式共识的基本思想
- **实战模式**:熟悉 2PC、Saga、CRDT 等常用解决方案
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 为什么需要分布式 | 扩展性、可用性、地理分布 |
| **第 2 章** | CAP 定理 | 一致性、可用性、分区容错 |
| **第 3 章** | 一致性模型 | 强一致、最终一致、因果一致 |
| **第 4 章** | 八大挑战 | 网络、时钟、分区、脑裂等 |
| **第 5 章** | 共识算法 | Paxos、Raft、ZAB |
| **第 6 章** | 分布式事务 | 2PC、Saga、TCC |
---
## 0. 全景图:为什么需要分布式系统?
单机系统简单可靠,但有三个无法逾越的瓶颈:
| 瓶颈 | 说明 | 分布式的解法 |
|------|------|-------------|
| 性能上限 | 单机 CPU、内存、磁盘有物理极限 | 水平扩展:加更多机器分担负载 |
| 单点故障 | 一台机器挂了,整个服务就挂了 | 冗余副本:多台机器互为备份 |
| 地理延迟 | 用户在全球各地,单机只能在一个地方 | 多地部署:就近服务用户 |
::: tip 分布式的代价
分布式系统解决了上面的问题,但引入了新的复杂性:网络不可靠、时钟不同步、部分失败、数据一致性……这些就是本文要讨论的"挑战"。
**Peter Deutsch 的分布式计算八大谬误**告诉我们,以下假设在分布式环境中都是错的:
1. 网络是可靠的
2. 延迟是零
3. 带宽是无限的
4. 网络是安全的
5. 拓扑不会变化
6. 只有一个管理员
7. 传输成本是零
8. 网络是同构的
:::
---
## 1. CAP 定理:分布式系统的"不可能三角"
2000 年,Eric Brewer 提出了 CAP 猜想(后被证明为定理):一个分布式系统最多只能同时满足以下三个属性中的两个。
| 属性 | 含义 | 通俗理解 |
|------|------|---------|
| **C**onsistency(一致性) | 所有节点在同一时刻看到相同的数据 | 你在任何 ATM 查余额,结果都一样 |
| **A**vailability(可用性) | 每个请求都能收到非错误的响应 | 系统永远能回应你,不会说"服务不可用" |
| **P**artition tolerance(分区容错) | 网络分区时系统仍能继续运行 | 即使部分网线断了,系统还能工作 |
<CAPTheoremDemo />
### 为什么只能选两个?
在分布式环境中,网络分区(P)是不可避免的——光纤会被挖断、交换机会故障、数据中心会断网。所以 P 是必选项,实际的选择是在 C 和 A 之间权衡:
- **选 CP**:分区时拒绝不确定的请求,保证数据正确 → 适合金融、库存
- **选 AP**:分区时继续服务,但数据可能暂时不一致 → 适合社交、内容
::: tip CAP 不是非黑即白
现实中的系统不是简单的"CP 或 AP"。很多系统在不同操作上做不同的选择——比如同一个数据库,读操作可以是 AP(允许读旧数据),写操作可以是 CP(要求多数确认)。
:::
---
## 2. 一致性模型:数据同步的"严格程度"
一致性不是一个开关(有或没有),而是一个光谱。不同的一致性模型在"正确性"和"性能"之间做不同的权衡。
<ConsistencyModelsDemo />
### 一致性模型对比
| 模型 | 保证 | 延迟 | 适用场景 |
|------|------|------|---------|
| 强一致性 | 读到的一定是最新写入的值 | 高(需等待同步) | 银行转账、库存扣减 |
| 最终一致性 | 最终所有副本会一致,但中间可能读到旧值 | 低(写入立即返回) | 社交动态、DNS |
| 因果一致性 | 有因果关系的操作保证顺序 | 中等 | 评论回复、协作编辑 |
| 线性一致性 | 所有操作看起来像在单机上按顺序执行 | 最高 | 分布式锁、选主 |
| 会话一致性 | 同一会话内保证读到自己的写入 | 低-中 | 用户个人数据 |
::: tip "读己之写"一致性
最常见的实际需求是:用户修改了自己的数据后,自己能立即看到更新(但其他用户可以稍后看到)。这叫"Read Your Own Writes"一致性,是最终一致性的一个实用增强。
:::
---
## 3. 八大挑战:分布式系统的"地雷阵"
分布式系统的复杂性不是来自某一个问题,而是多个问题交织在一起。以下是最核心的八大挑战。
<DistributedChallengesDemo />
### 挑战之间的关联
这八大挑战不是孤立的,它们相互关联:
- **网络不可靠** → 导致 **网络分区** → 触发 **CAP 权衡**
- **时钟不同步** → 导致 **事件排序困难** → 影响 **数据一致性**
- **部分失败** → 可能导致 **脑裂** → 需要 **共识算法** 来解决
- **数据一致性** → 需要 **分布式事务** → 但事务又受 **网络不可靠** 影响
::: tip 没有银弹
分布式系统没有"完美"的解决方案,只有"合适"的权衡。理解这些挑战的本质,才能在设计系统时做出正确的取舍。
:::
---
## 4. 共识算法:如何让多台机器"达成一致"
共识算法是分布式系统的核心——它解决的问题是:多个节点如何就某个值达成一致?即使部分节点故障或网络延迟。
### 4.1 Paxos
Leslie Lamport 在 1990 年提出,是第一个被严格证明正确的共识算法。
| 角色 | 职责 |
|------|------|
| Proposer | 提出提案(值) |
| Acceptor | 投票接受或拒绝提案 |
| Learner | 学习最终被选定的值 |
**两阶段流程**
1. **Prepare 阶段**Proposer 发送提案编号,Acceptor 承诺不再接受更小编号的提案
2. **Accept 阶段**:Proposer 发送具体值,多数 Acceptor 接受则提案通过
::: tip Paxos 的问题
Paxos 虽然正确,但出了名的难以理解和实现。Lamport 自己的论文用了一个希腊议会的比喻,结果让更多人困惑了。
:::
### 4.2 Raft:为可理解性而生
2014 年 Diego Ongaro 提出 Raft,目标是做一个"容易理解的 Paxos"。它把共识问题分解为三个子问题:
| 子问题 | 说明 |
|--------|------|
| Leader 选举 | 集群中选出一个 Leader,所有写入都经过 Leader |
| 日志复制 | Leader 将操作日志复制到所有 Follower |
| 安全性 | 保证已提交的日志不会被覆盖 |
**Raft 的核心流程**
1. 集群启动时,所有节点都是 Follower
2. 如果 Follower 超时没收到 Leader 心跳,就变成 Candidate 发起选举
3. 获得多数票的 Candidate 成为新 Leader
4. Leader 接收客户端请求,将日志复制到多数节点后提交
### 4.3 共识算法对比
| 算法 | 提出时间 | 可理解性 | 使用系统 |
|------|---------|---------|---------|
| Paxos | 1990 | 困难 | Google Chubby |
| Raft | 2014 | 容易 | etcd、Consul、TiKV |
| ZAB | 2011 | 中等 | ZooKeeper |
| EPaxos | 2013 | 困难 | 学术研究为主 |
---
## 5. 分布式事务:跨节点的"全有或全无"
单机数据库的事务靠本地锁和日志就能实现 ACID。但当一个业务操作涉及多个服务/数据库时,如何保证原子性?
### 5.1 两阶段提交(2PC
最经典的分布式事务协议,分为两个阶段:
| 阶段 | 协调者动作 | 参与者动作 |
|------|-----------|-----------|
| Prepare | 问所有参与者"能提交吗?" | 执行操作但不提交,回复 Yes/No |
| Commit | 如果全部 Yes,发送 Commit | 正式提交;如果有 No,全部回滚 |
**2PC 的问题**
- **阻塞**:Prepare 后如果协调者挂了,参与者会一直等待
- **单点故障**:协调者是单点,挂了整个事务卡住
- **性能差**:需要多次网络往返,锁持有时间长
### 5.2 Saga 模式
Saga 把一个大事务拆成多个本地事务,每个本地事务有对应的补偿操作。如果某一步失败,就逆序执行补偿。
**电商下单的 Saga 示例**
| 步骤 | 正向操作 | 补偿操作 |
|------|---------|---------|
| T1 | 创建订单(待支付) | 取消订单 |
| T2 | 扣减库存 | 恢复库存 |
| T3 | 扣减余额 | 退还余额 |
| T4 | 确认订单(已支付) | — |
如果 T3(扣减余额)失败:执行 C2(恢复库存)→ C1(取消订单)。
**两种编排方式**
- **编排式(Choreography**:每个服务监听事件,自行决定下一步。简单但难以追踪全局状态
- **协调式(Orchestration**:有一个中心协调者控制流程。清晰但协调者是单点
### 5.3 TCCTry-Confirm-Cancel
TCC 是 2PC 的业务层实现,把每个操作分为三个阶段:
| 阶段 | 说明 | 示例(扣库存) |
|------|------|---------------|
| Try | 预留资源,但不真正执行 | 冻结 10 件库存(可用库存 -10,冻结库存 +10) |
| Confirm | 确认执行,消耗预留资源 | 冻结库存 -10(真正扣减) |
| Cancel | 取消预留,释放资源 | 冻结库存 -10,可用库存 +10(恢复) |
### 5.4 三种方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|------|--------|------|--------|---------|
| 2PC | 强一致 | 低 | 中 | 数据库层面的跨库事务 |
| Saga | 最终一致 | 高 | 高 | 长流程业务(订单、物流) |
| TCC | 最终一致 | 中 | 最高 | 资金类高可靠场景 |
::: tip 实际选择建议
- 能用单库事务就不要用分布式事务
- 大多数业务场景用 Saga + 消息队列就够了
- TCC 适合对一致性要求极高的金融场景,但开发成本很高
- 2PC 适合数据库中间件(如 ShardingSphere)自动处理
:::
---
## 总结
分布式系统是现代互联网的基础设施,但它的复杂性远超单机系统。理解这些挑战不是为了"解决"它们(很多是根本性的),而是为了在设计系统时做出正确的权衡。
回顾本章的关键要点:
1. **CAP 定理**:网络分区不可避免,实际选择是在一致性和可用性之间权衡
2. **一致性模型**:从强一致到最终一致是一个光谱,根据业务需求选择
3. **八大挑战**:网络不可靠、时钟不同步、网络分区、脑裂等相互关联
4. **共识算法**:Raft 是目前最实用的共识算法,etcd/Consul 都基于它
5. **分布式事务**:Saga 适合大多数场景,TCC 适合金融场景,2PC 适合数据库层
## 延伸阅读
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 的分布式系统经典
- [The Raft Consensus Algorithm](https://raft.github.io/) - Raft 官方可视化演示
- [CAP Twelve Years Later](https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/) - Brewer 对 CAP 的重新审视
- [Jepsen](https://jepsen.io/) - 分布式系统正确性测试框架
- [分布式系统模式](https://martinfowler.com/articles/patterns-of-distributed-systems/) - Martin Fowler 的分布式模式合集
@@ -1,3 +1,204 @@
# 高可用与容灾
> 待实现
::: tip 前言
**系统挂了 1 分钟,可能意味着几十万的损失。** 高可用(High Availability)是指系统在面对硬件故障、软件 Bug、网络问题等异常情况时,仍能持续提供服务的能力。容灾(Disaster Recovery)则是在更大范围的灾难发生时,系统能够恢复服务的能力。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **可用性度量**:理解"几个 9"的含义和对应的停机时间
- **故障转移**:掌握主备、主主、多活等高可用架构
- **容灾策略**:了解 RPO 和 RTO 的概念及设计方法
- **故障检测**:理解心跳、探针、熔断等故障发现机制
- **混沌工程**:了解如何主动注入故障来验证系统韧性
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 可用性度量 | SLA、几个 9、停机时间 |
| **第 2 章** | 故障转移架构 | 主备、主主、多可用区、异地多活 |
| **第 3 章** | 容灾设计 | RPO、RTO、备份策略 |
| **第 4 章** | 故障检测与恢复 | 心跳、熔断、自动扩缩容 |
| **第 5 章** | 混沌工程 | 故障注入、韧性验证 |
---
## 1. 可用性度量:几个 9 意味着什么?
可用性通常用"几个 9"来衡量,计算公式为:
**可用性 = 正常运行时间 / 总时间 × 100%**
例如一个月(30 天 = 43200 分钟)内停机了 43 分钟,可用性就是 (43200 - 43) / 43200 ≈ 99.9%。每多一个 9,允许的停机时间就少一个数量级,实现难度和成本也指数级增长。
| 可用性等级 | 百分比 | 每月允许停机 | 每年允许停机 | 典型要求 |
|-----------|--------|------------|------------|---------|
| 2 个 9 | 99% | 7.3 小时 | 3.65 天 | 内部工具 |
| 3 个 9 | 99.9% | 43 分钟 | 8.76 小时 | 普通业务系统 |
| 4 个 9 | 99.99% | 4.3 分钟 | 52.6 分钟 | 电商、SaaS |
| 5 个 9 | 99.999% | 26 秒 | 5.26 分钟 | 金融、支付 |
<AvailabilityCalculatorDemo />
::: tip SLA 是什么?
**SLAService Level Agreement,服务等级协议)** 是服务提供方与客户之间的正式承诺。比如 AWS S3 承诺 99.99% 的可用性,如果没达到,会按比例退款。SLA 不只是技术指标,更是商业合同——违反 SLA 意味着赔钱。
:::
::: tip 从 3 个 9 到 4 个 9 的鸿沟
3 个 9(99.9%)意味着每月可以停机 43 分钟——一次部署出问题,回滚一下就用完了。
4 个 9(99.99%)意味着每月只能停机 4 分钟——这要求你必须有自动故障转移、滚动部署、健康检查等完整的高可用体系。
:::
---
## 2. 故障转移架构
故障转移(Failover)是高可用的核心机制:当主节点故障时,自动切换到备用节点继续提供服务。
### 主备模式(Active-Standby
最常见的高可用架构。主节点处理所有请求,备节点实时同步数据但不处理请求。主节点故障时,备节点自动接管。
```
正常状态:
客户端 → 主节点(处理请求)
备节点(同步数据,待命)
故障转移:
客户端 → 备节点(接管为新主节点)
原主节点(故障,等待修复)
```
关键问题是**脑裂(Split Brain)**:网络分区时,主备节点都认为对方挂了,同时对外提供服务,导致数据不一致。解决方案是引入**仲裁节点(Quorum)**——至少 3 个节点投票决定谁是主节点。
### 多可用区(Multi-AZ
将服务部署在同一地域的多个数据中心(可用区)。单个数据中心断电、断网不影响整体服务。云厂商的可用区之间通常有低延迟专线连接(< 2ms)。
### 异地多活(Multi-Region Active-Active
在不同城市甚至不同国家部署完整的服务副本,每个站点都能独立处理请求。这是最高级别的高可用架构,但也最复杂——核心挑战是**跨地域数据同步**的延迟和一致性问题。
<FailoverStrategyDemo />
| 架构 | 可用性级别 | 成本 | 复杂度 | 适用场景 |
|------|-----------|------|--------|---------|
| 单机 | 99%~99.9% | 低 | 低 | 开发测试、内部工具 |
| 主备 | 99.9%~99.99% | 中 | 中 | 中小型业务系统 |
| 多可用区 | 99.99% | 高 | 高 | 电商、SaaS 平台 |
| 异地多活 | 99.999% | 极高 | 极高 | 金融、大型互联网 |
---
## 3. 容灾设计:RPO 与 RTO
容灾设计围绕两个核心指标展开:
| 指标 | 全称 | 含义 | 举例 |
|------|------|------|------|
| RPO | Recovery Point Objective | 能容忍丢失多少数据 | RPO=0 表示不能丢任何数据 |
| RTO | Recovery Time Objective | 能容忍停机多长时间 | RTO=5min 表示 5 分钟内恢复 |
### 备份策略与 RPO 的关系
| 备份方式 | RPO | 成本 | 说明 |
|---------|-----|------|------|
| 每日全量备份 | 24 小时 | 低 | 最多丢一天数据 |
| 实时增量备份 | 分钟级 | 中 | binlog/WAL 持续同步 |
| 同步复制 | 0 | 高 | 写入必须等副本确认 |
::: tip 不是所有数据都需要 RPO=0
用户头像丢了可以重新上传(RPO=24h 够了),但支付记录一条都不能丢(RPO=0)。根据数据的业务价值来决定备份策略,而不是一刀切。
:::
---
## 4. 故障检测与恢复
### 4.1 故障检测机制
| 机制 | 原理 | 检测速度 | 适用场景 |
|------|------|---------|---------|
| 心跳检测 | 定期发送心跳包,超时判定故障 | 秒级 | 节点存活检测 |
| 健康检查 | HTTP/TCP 探针检查服务状态 | 秒级 | 负载均衡器后端检测 |
| 业务探针 | 模拟真实请求检查业务逻辑 | 秒~分钟级 | 端到端可用性监控 |
**心跳检测的工作原理**:节点 A 每隔固定时间(如 5 秒)向监控方发送一个"我还活着"的信号。如果连续 N 次(如 3 次)没收到心跳,就判定节点 A 故障。关键参数是**心跳间隔**和**超时阈值**——间隔太短会增加网络开销,太长会延迟故障发现。
**健康检查的三种级别**
- **存活探针(Liveness**:进程还在运行吗?不在就重启
- **就绪探针(Readiness**:服务能接受请求吗?不能就从负载均衡中摘除
- **启动探针(Startup**:服务启动完成了吗?没完成就等待,不要误判为故障
### 4.2 自动恢复机制
| 机制 | 描述 | 典型工具 |
|------|------|---------|
| 自动重启 | 进程崩溃后自动拉起 | systemd、PM2、K8s |
| 自动扩缩容 | 负载升高时自动增加实例 | K8s HPA、云厂商 Auto Scaling |
| 熔断降级 | 下游故障时快速失败,防止级联故障 | Hystrix、Sentinel、Resilience4j |
| 限流 | 超过容量的请求直接拒绝 | Nginx limit_req、网关限流 |
**熔断器模式(Circuit Breaker)详解**
熔断器的灵感来自电路中的保险丝——当电流过大时自动断开,保护整个电路不被烧毁。在微服务中,当下游服务故障时,熔断器会"断开",让请求快速失败,而不是傻等超时。
```
熔断器三种状态:
关闭(正常)──→ 失败率超过阈值 ──→ 打开(熔断)
↑ │
│ 等待冷却时间
│ ↓
└── 探测请求成功 ←── 半开(试探)
```
- **关闭状态**:正常转发请求,同时统计失败率
- **打开状态**:所有请求直接返回错误(快速失败),不再调用下游
- **半开状态**:冷却时间到后,放行少量探测请求。如果成功,恢复关闭;如果失败,继续打开
**降级(Fallback)** 是熔断的配套策略:熔断触发后,不是直接报错,而是返回一个"兜底"结果。比如推荐服务挂了,就返回热门商品列表;用户头像加载失败,就显示默认头像。
---
## 5. 混沌工程:主动找问题
混沌工程的核心理念是:**与其等故障发生,不如主动制造故障**,在可控环境中验证系统的韧性。
| 工具 | 提出者 | 核心能力 |
|------|--------|---------|
| Chaos Monkey | Netflix | 随机终止生产环境的实例 |
| Chaos Mesh | PingCAP | K8s 环境下的故障注入 |
| Litmus | CNCF | 云原生混沌工程框架 |
| ChaosBlade | 阿里巴巴 | 多场景故障注入工具 |
::: tip 混沌工程的实施步骤
1. **定义稳态**:明确系统正常运行的指标(如 P99 延迟 < 200ms
2. **提出假设**:如果某个节点挂了,系统应该在 30 秒内自动恢复
3. **注入故障**:在可控范围内制造故障(先在测试环境,再到生产)
4. **观察结果**:系统是否如预期恢复?有没有级联故障?
5. **修复弱点**:发现问题后改进架构和流程
:::
---
## 总结
高可用不是一个功能,而是一种架构能力。它需要从设计、开发、部署、运维的每个环节去保障。
回顾本章的关键要点:
1. **几个 9**:每多一个 9,停机时间少一个数量级,成本和复杂度指数增长
2. **故障转移**:从主备到异地多活,根据业务需求选择合适的架构
3. **RPO 与 RTO**:根据数据价值和业务容忍度设计备份和恢复策略
4. **自动化**:故障检测、自动重启、熔断降级是高可用的基础设施
5. **混沌工程**:主动制造故障,在可控环境中验证系统韧性
## 延伸阅读
- [Site Reliability Engineering](https://sre.google/sre-book/table-of-contents/) - Google SRE 经典
- [Chaos Monkey](https://netflix.github.io/chaosmonkey/) - Netflix 混沌工程工具
- [Release It!](https://pragprog.com/titles/mnee2/release-it-second-edition/) - 生产环境设计模式
- [Chaos Mesh](https://chaos-mesh.org/) - K8s 混沌工程平台
@@ -1,3 +1,156 @@
# 从单体到微服务的演进
> 待实现
::: tip 前言
**没有哪个架构是"最好的",只有"最适合当前阶段的"。** 从单体到微服务不是一步到位的跳跃,而是随着业务规模和团队规模增长,逐步演进的过程。过早拆分微服务和过晚拆分一样危险。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **演进路径**:理解从单体到微服务的四个阶段
- **拆分时机**:知道什么时候该拆、什么时候不该拆
- **拆分策略**:掌握按业务域拆分的方法论
- **通信模式**:了解服务间同步和异步通信的选择
- **数据拆分**:理解数据库拆分的挑战和方案
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 架构演进路径 | 单体→模块化→SOA→微服务 |
| **第 2 章** | 拆分时机与原则 | Conway 定律、团队自治 |
| **第 3 章** | 拆分策略 | DDD 限界上下文、绞杀者模式 |
| **第 4 章** | 服务通信 | REST、gRPC、消息队列 |
| **第 5 章** | 数据拆分 | 数据库拆分、数据同步 |
---
## 1. 架构演进路径
架构演进不是技术驱动的,而是**组织规模驱动的**。当团队从 5 人增长到 500 人时,单体架构的协作效率会急剧下降。
| 阶段 | 架构 | 团队规模 | 特点 |
|------|------|---------|------|
| 起步期 | 单体应用 | 1~10 人 | 所有代码在一个项目中,部署简单 |
| 成长期 | 模块化单体 | 10~50 人 | 代码按模块划分,但仍然一起部署 |
| 扩张期 | SOA(面向服务) | 50~200 人 | 按业务线拆分为粗粒度服务 |
| 规模期 | 微服务 | 200+ 人 | 细粒度服务,每个团队独立开发部署 |
<ArchEvolutionDemo />
::: tip Conway 定律
"设计系统的组织,其产生的架构等同于组织的沟通结构。"——Melvin Conway
简单说:3 个团队做一个系统,最终会变成 3 个服务。架构拆分的本质是**组织拆分**。
**反向 Conway 定律**:既然组织结构决定了系统架构,那么想要什么样的架构,就先调整成什么样的组织结构。比如你想拆出独立的支付服务,就先组建一个独立的支付团队。很多公司微服务拆分失败,不是技术问题,而是组织没有跟着调整。
:::
---
## 2. 什么时候该拆微服务?
不是所有系统都需要微服务。过早拆分会带来不必要的复杂性。
| 信号 | 说明 | 建议 |
|------|------|------|
| 部署冲突频繁 | 多个团队改同一个代码库,经常冲突 | 考虑拆分 |
| 某模块需要独立扩容 | 搜索模块需要 10 倍于其他模块的资源 | 考虑拆分 |
| 技术栈需要差异化 | AI 模块用 Python,主站用 Java | 考虑拆分 |
| 团队 < 10 人 | 沟通成本低,单体足够 | 不要拆 |
| 业务还在探索期 | 需求变化快,边界不清晰 | 不要拆 |
| 没有 DevOps 能力 | 没有 CI/CD、容器化、监控体系 | 不要拆 |
---
## 3. 拆分策略
### 3.1 按业务域拆分(DDD 限界上下文)
DDD(领域驱动设计)的限界上下文(Bounded Context)是拆分微服务的最佳指导原则。每个限界上下文对应一个独立的业务域,有自己的数据模型和业务规则。
**什么是限界上下文?** 同一个词在不同业务域中含义不同。比如"用户"在用户域是指注册信息(姓名、邮箱),在订单域是指下单人(收货地址、支付方式),在推荐域是指行为画像(浏览历史、偏好标签)。限界上下文就是划定一个边界,在这个边界内,术语和模型有明确统一的含义。
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户域 │ │ 订单域 │ │ 支付域 │
│ │ │ │ │ │
│ User │ │ Order │ │ Payment │
│ Profile │ │ OrderItem │ │ Refund │
│ Address │ │ Cart │ │ Transaction │
│ │ │ │ │ │
│ 用户服务 │ │ 订单服务 │ │ 支付服务 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────── API 调用 / 事件通信 ───────┘
```
| 限界上下文 | 核心实体 | 对应服务 |
|-----------|---------|---------|
| 用户域 | User、Profile、Address | 用户服务 |
| 商品域 | Product、Category、SKU | 商品服务 |
| 订单域 | Order、OrderItem | 订单服务 |
| 支付域 | Payment、Refund | 支付服务 |
| 物流域 | Shipment、Tracking | 物流服务 |
### 3.2 绞杀者模式(Strangler Fig Pattern
不要一次性重写整个单体,而是像绞杀榕一样,逐步用新服务替换旧模块:
1. 在单体外部创建新服务
2. 通过代理层将部分流量路由到新服务
3. 验证新服务稳定后,逐步迁移更多流量
4. 最终完全替换旧模块
---
## 4. 服务通信模式
| 方式 | 协议 | 特点 | 适用场景 |
|------|------|------|---------|
| REST | HTTP/JSON | 简单通用,生态好 | 对外 API、CRUD 操作 |
| gRPC | HTTP/2 + Protobuf | 高性能,强类型 | 内部服务间高频调用 |
| 消息队列 | AMQP/Kafka | 异步解耦,削峰填谷 | 事件通知、异步任务 |
| GraphQL | HTTP/JSON | 客户端按需查询 | BFF 层、移动端 |
::: tip 同步 vs 异步的选择
- **需要立即返回结果** → 同步(REST/gRPC
- **不需要立即返回** → 异步(消息队列)
- **一个事件触发多个动作** → 异步(发布-订阅)
经验法则:能异步就异步,同步调用链越长,系统越脆弱。
:::
---
## 5. 数据拆分:最难的部分
微服务拆分中最痛苦的不是代码拆分,而是数据库拆分。每个服务应该拥有自己的数据库,但这意味着跨服务查询变得困难。
| 挑战 | 描述 | 解决方案 |
|------|------|---------|
| 跨服务 JOIN | 不能直接 JOIN 两个服务的表 | API 组合查询、数据冗余 |
| 分布式事务 | 跨库事务无法用本地事务 | Saga、本地消息表 |
| 数据一致性 | 多个服务的数据可能暂时不一致 | 最终一致性、事件驱动 |
| 数据迁移 | 从共享库迁移到独立库 | 双写过渡、数据同步工具 |
---
## 总结
从单体到微服务是一个渐进的过程,不是一蹴而就的革命。
回顾本章的关键要点:
1. **演进路径**:单体→模块化单体→SOA→微服务,每一步都有明确的驱动力
2. **拆分时机**:团队规模、部署冲突、扩容需求是拆分的信号
3. **拆分策略**:用 DDD 限界上下文指导拆分,用绞杀者模式渐进迁移
4. **通信选择**:能异步就异步,同步调用链越短越好
5. **数据拆分**:最难但最重要,接受最终一致性是关键心态转变
## 延伸阅读
- [Building Microservices](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/) - Sam Newman 微服务经典
- [Monolith to Microservices](https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/) - 渐进式迁移指南
- [Domain-Driven Design](https://www.domainlanguage.com/ddd/) - Eric Evans 的 DDD 经典
- [The Strangler Fig Pattern](https://martinfowler.com/bliki/StranglerFigApplication.html) - Martin Fowler 的绞杀者模式
@@ -1,3 +1,252 @@
# 系统设计方法论
> 待实现
::: tip 前言
**系统设计不是拍脑袋画架构图,而是一套有章可循的方法论。** 无论是面试中的系统设计题,还是实际工作中的架构设计,都遵循相似的思考框架:先搞清楚问题,再估算规模,然后设计方案,最后深入优化。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **设计流程**:掌握系统设计的四步法框架
- **容量估算**:学会"信封背面估算"的技巧
- **常见模式**:熟悉缓存、分库分表、消息队列等核心模式
- **权衡思维**:理解架构设计中的 trade-off 思维
- **实战案例**:通过短链服务、Feed 流等案例理解设计过程
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 设计四步法 | 需求澄清、容量估算、架构设计、深入优化 |
| **第 2 章** | 容量估算 | QPS、存储、带宽、信封背面估算 |
| **第 3 章** | 核心设计模式 | 缓存、分库分表、消息队列、CDN |
| **第 4 章** | 权衡思维 | 一致性 vs 可用性、性能 vs 成本 |
| **第 5 章** | 经典案例 | 短链服务、Feed 流、秒杀系统 |
---
## 1. 系统设计四步法
系统设计不是一上来就画架构图。无论是面试还是实战,都应该遵循一个结构化的流程。
<SystemDesignStepsDemo />
::: tip 为什么要先澄清需求?
很多人拿到题目就开始画图,结果设计了一个"正确但不是面试官想要的"系统。花 5 分钟问清楚需求,能避免后面 30 分钟的返工。
常见的澄清问题:
- 系统的核心功能是什么?(不要设计所有功能)
- 用户规模多大?(决定是否需要分布式)
- 读写比例?(决定缓存策略)
- 数据需要保留多久?(决定存储方案)
:::
---
## 2. 容量估算:信封背面的艺术
"信封背面估算"Back-of-envelope estimation)是系统设计中的核心技能。不需要精确计算,只需要知道量级。
<CapacityEstimationDemo />
### 常用换算速查
| 量级 | 换算 | 记忆技巧 |
|------|------|---------|
| 1 天 | 86,400 秒 | ≈ 10 万秒 |
| 1 亿请求/天 | ≈ 1,200 QPS | 除以 10 万 |
| 1 KB × 1 亿 | ≈ 100 GB | 1 亿条小记录 |
| 1 MB × 100 万 | ≈ 1 TB | 100 万张图片 |
### 2-8 法则在估算中的应用
大多数系统遵循 80/20 法则:20% 的数据承载 80% 的请求。这意味着:
- **缓存大小** ≈ 总数据量 × 20%
- **热点 QPS** ≈ 总 QPS × 80% 集中在 20% 的 key 上
- **缓存命中率**目标 ≈ 80%+(低于这个值说明缓存策略有问题)
---
## 3. 核心设计模式
系统设计中反复出现的模式,掌握这些就能应对大多数场景。
### 3.1 缓存模式
| 模式 | 读路径 | 写路径 | 适用场景 |
|------|--------|--------|---------|
| Cache-Aside | 先查缓存,miss 则查 DB 并回填 | 先写 DB,再删缓存 | 通用场景,最常用 |
| Read-Through | 缓存层自动从 DB 加载 | 同 Cache-Aside | 需要缓存框架支持 |
| Write-Behind | 同 Cache-Aside | 先写缓存,异步写 DB | 写密集型,可容忍丢数据 |
::: tip 为什么是"删缓存"而不是"更新缓存"
更新缓存在并发场景下容易出现数据不一致:线程 A 和 B 同时更新,A 先写 DB 但 B 先更新缓存,导致缓存中是 B 的旧值。删除缓存则让下次读请求重新从 DB 加载,天然避免这个问题。
:::
### 3.2 分库分表
当单表数据量超过千万级,或单库 QPS 超过瓶颈时,就需要考虑分库分表。
| 策略 | 做法 | 优点 | 缺点 |
|------|------|------|------|
| 垂直分库 | 按业务域拆分数据库 | 业务解耦,独立扩展 | 跨库 JOIN 困难 |
| 水平分表 | 同一张表按规则拆成多张 | 单表数据量可控 | 分片键选择关键 |
| 垂直分表 | 把大字段拆到独立表 | 减少 IO,提升查询效率 | 需要额外 JOIN |
**分片键选择原则**
- 选择查询最频繁的字段(如 user_id)
- 数据分布要均匀,避免热点
- 尽量让同一用户的数据在同一分片(减少跨分片查询)
### 3.3 消息队列
消息队列是分布式系统的"减震器",核心作用是解耦、异步、削峰。
| 场景 | 不用队列 | 用队列 |
|------|---------|--------|
| 下单后发通知 | 下单接口同步调用通知服务,通知失败导致下单失败 | 下单成功后发消息,通知服务异步消费 |
| 秒杀抢购 | 瞬间流量打爆数据库 | 请求先入队列,后端按能力消费 |
| 数据同步 | 服务 A 直接调用服务 B 的接口 | 服务 A 发事件,服务 B 订阅处理 |
---
## 4. 权衡思维:没有银弹
架构设计的本质是权衡(Trade-off)。每个决策都有代价,关键是理解代价并做出适合当前阶段的选择。
| 权衡维度 | 选项 A | 选项 B | 决策依据 |
|---------|--------|--------|---------|
| 一致性 vs 可用性 | 强一致(CP) | 高可用(AP) | 业务能否容忍短暂不一致? |
| 性能 vs 成本 | 全量缓存 | 按需缓存 | 数据量和预算 |
| 简单 vs 灵活 | 单体架构 | 微服务 | 团队规模和业务复杂度 |
| 实时 vs 批量 | 流式处理 | 批处理 | 数据时效性要求 |
| 自建 vs 托管 | 自己搭 MySQL | 用云数据库 RDS | 运维能力和成本 |
::: tip 架构决策记录(ADR
每个重要的架构决策都应该记录下来:**背景是什么、考虑了哪些方案、为什么选了这个、有什么代价**。这不是为了甩锅,而是为了让后来的人理解"为什么当时这么设计"。
格式很简单:
- **标题**:用 XXX 替代 YYY
- **背景**:我们遇到了什么问题
- **决策**:我们选择了什么方案
- **理由**:为什么选这个
- **代价**:这个决策的缺点和风险
:::
### 常见的错误权衡
| 错误 | 表现 | 正确做法 |
|------|------|---------|
| 过早优化 | 日活 1000 就上分库分表 | 先用单库,遇到瓶颈再拆 |
| 技术驱动 | "我想用 Kafka" 而不是 "我需要异步" | 从问题出发,而非从技术出发 |
| 忽略运维成本 | 选了最优方案但团队维护不了 | 方案要匹配团队能力 |
| 追求完美一致性 | 所有场景都用分布式事务 | 大多数场景最终一致性就够了 |
---
## 5. 经典案例
通过三个经典案例,把前面学到的方法论串起来。
### 5.1 短链服务(TinyURL
短链服务是系统设计面试的经典题目,麻雀虽小五脏俱全。
**需求澄清**
- 核心功能:长链接 → 短链接(写),短链接 → 重定向(读)
- 读写比:约 100:1(读远多于写)
- 日均重定向:1 亿次
- 短链永不过期
**容量估算**
| 指标 | 计算 | 结果 |
|------|------|------|
| 写 QPS | 1 亿 / 100 / 86400 | ≈ 12 QPS |
| 读 QPS | 1 亿 / 86400 | ≈ 1,200 QPS |
| 峰值读 QPS | 1,200 × 3 | ≈ 3,600 QPS |
| 5 年存储 | 100 万/天 × 365 × 5 × 100B | ≈ 18 GB |
| 缓存(20% | 18 GB × 20% | ≈ 3.6 GB |
**架构设计**
```
写路径:客户端 → API Server → ID 生成器 → Base62 编码 → 写入 MySQL + Redis
读路径:客户端 → CDN → API Server → Redis 查询 → 302 重定向
↓ (cache miss)
MySQL 查询 → 回填 Redis
```
**关键设计决策**
- 短码生成:Snowflake 分布式 ID + Base62 编码,避免哈希冲突
- 缓存策略:Cache-Aside,热点短链用 CDN 加速
- 数据库:单表即可(18GB 很小),按短码做索引
### 5.2 Feed 流系统
社交平台的 Feed 流(朋友圈、微博首页)是另一个经典题目。
**核心挑战**:用户发一条动态,如何让所有关注者看到?
| 方案 | 做法 | 优点 | 缺点 |
|------|------|------|------|
| 拉模式(Pull) | 读取时实时聚合关注者的动态 | 写入简单,存储少 | 读取慢,关注多时延迟高 |
| 推模式(Push) | 发布时写入所有粉丝的收件箱 | 读取极快 | 大 V 发动态写扩散严重 |
| 推拉结合 | 普通用户推,大 V 拉 | 平衡读写性能 | 实现复杂 |
**推拉结合方案**
- 粉丝数 < 1 万:发布时推送到所有粉丝的 Feed 缓存(推模式)
- 粉丝数 > 1 万:不推送,粉丝读取时实时拉取(拉模式)
- 用户打开 Feed 时:合并推送的内容 + 实时拉取大 V 的内容,按时间排序
### 5.3 秒杀系统
秒杀的核心挑战:瞬间超高并发 + 库存不能超卖。
**流量特征**
- 活动开始前:大量用户刷新页面等待
- 活动开始瞬间:QPS 可能是平时的 100 倍以上
- 活动结束后:流量迅速回落
**分层削峰策略**
```
用户请求 → CDN(静态页面)→ 网关(限流)→ 消息队列(削峰)→ 库存服务(扣减)
```
| 层级 | 策略 | 效果 |
|------|------|------|
| 前端 | 按钮置灰 + 随机延迟 + 验证码 | 过滤机器人,分散请求 |
| CDN | 静态资源缓存 | 减少 90% 的页面请求 |
| 网关 | 令牌桶限流 | 只放行系统能承受的流量 |
| 消息队列 | 请求入队,异步处理 | 削峰填谷,保护数据库 |
| 库存服务 | Redis 预扣减 + Lua 原子操作 | 防止超卖,毫秒级响应 |
::: tip 秒杀的核心原则
1. **尽量拦截在上游**:能在 CDN 挡住的就不要到应用层
2. **读写分离**:商品详情页走缓存,只有下单走数据库
3. **异步处理**:用户点击"抢购"后立即返回"排队中",后台异步处理
4. **兜底方案**:限流、熔断、降级,任何一层出问题都有 Plan B
:::
---
## 总结
系统设计是一门实践性很强的技能,核心在于结构化思考和权衡取舍。
回顾本章的关键要点:
1. **四步法框架**:需求澄清 → 容量估算 → 架构设计 → 深入优化,每一步都不可跳过
2. **信封背面估算**:不需要精确,只需要知道量级,用于指导架构决策
3. **核心模式**:缓存、分库分表、消息队列、CDN、限流熔断——这些是系统设计的"积木"
4. **权衡思维**:没有完美方案,只有适合当前阶段的方案,记录每个决策的理由和代价
5. **经典案例**:短链服务练基础、Feed 流练推拉模型、秒杀练高并发——掌握这三个就能举一反三
## 延伸阅读
- [System Design Interview](https://www.amazon.com/System-Design-Interview-insiders-Second/dp/B08CMF2CQF) - Alex Xu 系统设计面试经典
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 数据密集型应用设计
- [The System Design Primer](https://github.com/donnemartin/system-design-primer) - GitHub 上最全的系统设计学习资源
- [ByteByteGo](https://bytebytego.com/) - Alex Xu 的系统设计可视化博客
@@ -1,3 +1,221 @@
# Docker 容器化
> 待实现
::: tip 前言
**"在我机器上能跑"是开发者最经典的借口,Docker 让这个借口彻底消失。** 容器化技术将应用及其所有依赖打包成一个标准化的单元,确保在任何环境中都能一致运行。它是现代软件交付的基石。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **核心概念**:理解镜像、容器、仓库三大核心概念
- **架构对比**:明白容器和虚拟机的本质区别
- **实操能力**:掌握 Dockerfile 编写和常用命令
- **编排基础**:学会用 Docker Compose 管理多服务应用
- **最佳实践**:了解镜像优化、安全加固等生产级实践
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 为什么需要容器 | 环境一致性、资源效率、标准化交付 |
| **第 2 章** | 核心概念 | 镜像、容器、仓库、Dockerfile |
| **第 3 章** | Docker 生命周期 | 编写、构建、推送、运行、管理 |
| **第 4 章** | Docker Compose | 多服务编排、网络、数据卷 |
| **第 5 章** | 最佳实践 | 镜像优化、安全、多阶段构建 |
---
## 1. 为什么需要容器?
在容器出现之前,部署一个应用需要在服务器上手动安装运行时、配置环境变量、处理依赖冲突。不同环境(开发、测试、生产)之间的差异是 bug 的温床。
<DockerArchitectureDemo />
### 容器解决了什么问题?
| 问题 | 传统方式 | 容器方式 |
|------|---------|---------|
| 环境不一致 | "我本地能跑" | 打包所有依赖,到处一致 |
| 依赖冲突 | App A 要 Node 14App B 要 Node 18 | 每个容器独立环境 |
| 资源浪费 | 每个 VM 一个完整 OS | 共享内核,MB 级开销 |
| 部署慢 | 手动安装配置 | docker run 一条命令 |
| 扩容难 | 新建 VM、装环境、部署 | 秒级启动新容器 |
::: tip 容器的本质
容器不是轻量级虚拟机。它的本质是**被隔离的进程**。Linux 内核通过两个机制实现容器:
- **Namespace**:隔离进程的视野(PID、网络、文件系统等)
- **Cgroups**:限制进程的资源使用(CPU、内存、IO)
容器里的进程和宿主机上的普通进程没有本质区别,只是被"关在了一个看不到外面的房间里"。
:::
---
## 2. 核心概念
Docker 的世界围绕三个核心概念:镜像(Image)、容器(Container)、仓库(Registry)。
| 概念 | 类比 | 说明 |
|------|------|------|
| 镜像(Image) | 类 / 模板 | 只读的应用模板,包含代码、运行时、库、配置 |
| 容器(Container) | 实例 / 对象 | 镜像的运行实例,可读写,有独立的生命周期 |
| 仓库(Registry) | 应用商店 | 存储和分发镜像的服务(Docker Hub、ACR、ECR |
| Dockerfile | 配方 / 蓝图 | 定义如何构建镜像的文本文件 |
| 数据卷(Volume) | 外接硬盘 | 持久化数据,容器删除后数据不丢失 |
### 镜像的分层结构
Docker 镜像由多个只读层(Layer)叠加而成,每条 Dockerfile 指令创建一层:
```
┌─────────────────────────┐
│ CMD ["node", "app.js"] │ ← 启动命令层
├─────────────────────────┤
│ COPY . /app │ ← 应用代码层(经常变)
├─────────────────────────┤
│ RUN npm install │ ← 依赖安装层(偶尔变)
├─────────────────────────┤
│ FROM node:18-alpine │ ← 基础镜像层(很少变)
└─────────────────────────┘
```
::: tip 为什么分层很重要?
Docker 会缓存每一层。如果某一层没有变化,构建时会直接复用缓存。所以 Dockerfile 中应该把**变化频率低的指令放在前面**(如安装依赖),**变化频率高的放在后面**(如复制代码)。这样大部分构建都能命中缓存,速度快很多。
:::
---
## 3. Docker 生命周期
从编写 Dockerfile 到容器运行,Docker 的工作流程是一条清晰的流水线。
<DockerLifecycleDemo />
### Dockerfile 常用指令速查
| 指令 | 作用 | 示例 |
|------|------|------|
| `FROM` | 指定基础镜像 | `FROM node:18-alpine` |
| `WORKDIR` | 设置工作目录 | `WORKDIR /app` |
| `COPY` | 复制文件到镜像 | `COPY package.json ./` |
| `RUN` | 构建时执行命令 | `RUN npm install` |
| `ENV` | 设置环境变量 | `ENV NODE_ENV=production` |
| `EXPOSE` | 声明端口(仅文档作用) | `EXPOSE 3000` |
| `CMD` | 容器启动命令 | `CMD ["node", "app.js"]` |
| `ENTRYPOINT` | 容器入口点(不易被覆盖) | `ENTRYPOINT ["nginx"]` |
---
## 4. Docker Compose:多服务编排
真实项目通常不止一个容器。一个 Web 应用可能需要:应用服务器 + 数据库 + Redis + Nginx。Docker Compose 用一个 YAML 文件定义和管理多个容器。
### docker-compose.yml 示例
```yaml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
db:
image: postgres:15-alpine
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secret
redis:
image: redis:7-alpine
volumes:
db-data:
```
### Compose 核心概念
| 概念 | 说明 | 示例 |
|------|------|------|
| services | 定义各个容器服务 | app、db、redis |
| volumes | 持久化数据卷 | db-data 保存数据库文件 |
| networks | 自定义网络(默认自动创建) | 服务间通过服务名互相访问 |
| depends_on | 启动顺序依赖 | app 依赖 db 和 redis |
| environment | 环境变量 | 数据库密码、连接地址 |
::: tip 服务发现
在 Docker Compose 中,服务名就是主机名。app 容器可以直接用 `db:5432` 访问数据库,用 `redis:6379` 访问 Redis,不需要知道 IP 地址。这是 Docker 内置 DNS 的功劳。
:::
---
## 5. 最佳实践
### 5.1 多阶段构建(Multi-stage Build
多阶段构建是优化镜像大小的利器。构建阶段安装所有工具和依赖,最终阶段只保留运行时需要的文件。
```dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]
```
### 5.2 镜像优化清单
| 优化项 | 做法 | 效果 |
|--------|------|------|
| 选择小基础镜像 | 用 `alpine` 而非 `ubuntu` | 镜像从 ~200MB 降到 ~50MB |
| 合并 RUN 指令 | 多个命令用 `&&` 连接 | 减少镜像层数 |
| 使用 .dockerignore | 排除 node_modules、.git 等 | 加速构建,减小上下文 |
| 多阶段构建 | 分离构建和运行环境 | 最终镜像不含构建工具 |
| 固定版本号 | `node:18.17-alpine` 而非 `node:latest` | 构建可重复 |
### 5.3 安全实践
| 实践 | 说明 |
|------|------|
| 不用 root 运行 | `USER node` 指定非 root 用户 |
| 扫描漏洞 | `docker scout` 或 Trivy 扫描镜像 |
| 最小权限 | 只安装必要的包,不装调试工具 |
| 不硬编码密钥 | 用环境变量或 Docker Secrets |
| 定期更新基础镜像 | 及时修复安全漏洞 |
---
## 总结
Docker 容器化是现代软件交付的基础设施,理解它对于任何开发者都至关重要。
回顾本章的关键要点:
1. **容器 vs 虚拟机**:容器共享宿主内核,更轻量、更快,但隔离性略弱于 VM
2. **核心三件套**:镜像(模板)、容器(实例)、仓库(分发)
3. **Dockerfile**:分层构建,利用缓存,变化少的指令放前面
4. **Docker Compose**:用 YAML 定义多服务应用,服务名即主机名
5. **生产实践**:多阶段构建减小镜像、alpine 基础镜像、非 root 运行
## 延伸阅读
- [Docker 官方文档](https://docs.docker.com/) - 最权威的参考资料
- [Docker Getting Started](https://docs.docker.com/get-started/) - 官方入门教程
- [Dockerfile Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) - 官方最佳实践指南
- [Docker Compose 文档](https://docs.docker.com/compose/) - Compose 完整参考
@@ -1,3 +1,192 @@
# Kubernetes 编排
> 待实现
::: tip 前言
**Docker 解决了"打包"问题,Kubernetes 解决了"管理"问题。** 当你有几十上百个容器需要部署、扩缩容、故障恢复时,手动管理是不现实的。Kubernetes(K8s)就是容器的"操作系统",它自动化了容器化应用的部署、扩展和运维。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **架构理解**:掌握 K8s 控制平面和工作节点的组成
- **核心资源**:熟悉 Pod、Deployment、Service 等核心概念
- **声明式管理**:理解"声明期望状态,系统自动收敛"的思想
- **运维能力**:了解滚动更新、自动扩缩容、健康检查等机制
- **实战入门**:能用 kubectl 和 YAML 部署一个完整应用
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 为什么需要 K8s | 容器编排的挑战 |
| **第 2 章** | K8s 架构 | 控制平面、工作节点、etcd |
| **第 3 章** | 核心资源 | Pod、Deployment、Service、Ingress |
| **第 4 章** | 声明式管理 | YAML、kubectl、控制循环 |
| **第 5 章** | 运维实践 | 滚动更新、HPA、健康检查 |
---
## 1. 为什么需要 Kubernetes
Docker 让单个容器的打包和运行变得简单,但当你面对以下场景时,手动管理就力不从心了:
| 挑战 | 描述 | K8s 的解决方案 |
|------|------|---------------|
| 多实例部署 | 一个服务需要运行 10 个副本 | Deployment 自动管理副本数 |
| 故障恢复 | 某个容器挂了需要自动重启 | 控制器自动检测并重建 Pod |
| 服务发现 | 容器 IP 会变,怎么找到对方? | Service 提供稳定的 DNS 和 IP |
| 滚动更新 | 更新版本时不能停服 | 逐步替换旧 Pod,零停机 |
| 弹性伸缩 | 流量高峰自动扩容 | HPA 根据 CPU/内存自动调整副本数 |
| 资源调度 | 把容器放到最合适的机器上 | Scheduler 智能调度 |
::: tip K8s 的核心思想:声明式
你不需要告诉 K8s "启动 3 个容器"(命令式),而是告诉它 "我要 3 个副本在运行"(声明式)。K8s 会持续监控,确保实际状态与你声明的期望状态一致。如果一个 Pod 挂了,它会自动创建新的来补上。
:::
---
## 2. Kubernetes 架构
K8s 集群由控制平面(Control Plane)和工作节点(Worker Node)组成。
<K8sArchitectureDemo />
### 一次请求的完整路径
```
用户请求 → Ingress Controller → Service → kube-proxy → Pod(容器)
Endpoint 列表(由 Service 维护)
```
---
## 3. 核心资源对象
K8s 通过各种"资源对象"来描述集群的期望状态。
<K8sWorkloadsDemo />
### 资源对象分类
| 类别 | 资源 | 用途 |
|------|------|------|
| 工作负载 | Pod、Deployment、StatefulSet、DaemonSet、Job | 运行应用 |
| 网络 | Service、Ingress、NetworkPolicy | 服务发现和流量管理 |
| 配置 | ConfigMap、Secret | 配置和敏感数据管理 |
| 存储 | PersistentVolume、PersistentVolumeClaim | 持久化存储 |
| 调度 | Node、Namespace、ResourceQuota | 资源隔离和限制 |
---
## 4. 声明式管理与 kubectl
### 控制循环(Reconciliation Loop
K8s 的核心工作机制是控制循环:
```
观察(Observe)→ 比较(Diff)→ 行动(Act)→ 观察...
↓ ↓ ↓
读取实际状态 与期望状态对比 执行修正操作
```
你声明 `replicas: 3`,控制器发现只有 2 个 Pod 在运行,就会创建 1 个新的。这个循环每隔几秒执行一次,确保系统始终向期望状态收敛。
### kubectl 常用命令
| 命令 | 作用 | 示例 |
|------|------|------|
| `kubectl apply -f` | 应用 YAML 配置 | `kubectl apply -f deployment.yaml` |
| `kubectl get` | 查看资源列表 | `kubectl get pods -o wide` |
| `kubectl describe` | 查看资源详情 | `kubectl describe pod my-app-xxx` |
| `kubectl logs` | 查看 Pod 日志 | `kubectl logs -f my-app-xxx` |
| `kubectl exec` | 进入 Pod 终端 | `kubectl exec -it my-app-xxx -- sh` |
| `kubectl delete` | 删除资源 | `kubectl delete -f deployment.yaml` |
| `kubectl scale` | 手动扩缩容 | `kubectl scale deploy my-app --replicas=5` |
::: tip apply vs create
`kubectl create` 是命令式的——"创建这个资源",如果已存在会报错。`kubectl apply` 是声明式的——"确保资源是这个状态",不存在就创建,已存在就更新。生产环境中应该始终使用 `apply`
:::
---
## 5. 运维实践
### 5.1 滚动更新与回滚
Deployment 默认使用滚动更新策略:逐步创建新版本 Pod,同时逐步终止旧版本 Pod。
```yaml
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多创建 1 个 Pod
maxUnavailable: 0 # 不允许有 Pod 不可用
```
| 操作 | 命令 |
|------|------|
| 更新镜像 | `kubectl set image deploy/my-app app=my-app:2.0` |
| 查看更新状态 | `kubectl rollout status deploy/my-app` |
| 查看历史版本 | `kubectl rollout history deploy/my-app` |
| 回滚到上一版本 | `kubectl rollout undo deploy/my-app` |
### 5.2 自动扩缩容(HPA
HPAHorizontal Pod Autoscaler)根据 CPU、内存或自定义指标自动调整 Pod 副本数。
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
```
### 5.3 健康检查(Probe
K8s 通过三种探针监控 Pod 的健康状态:
| 探针 | 作用 | 失败后果 |
|------|------|---------|
| livenessProbe | 检测容器是否存活 | 重启容器 |
| readinessProbe | 检测容器是否就绪 | 从 Service 摘除,不接收流量 |
| startupProbe | 检测容器是否启动完成 | 启动期间不执行其他探针 |
::: tip 探针的重要性
没有配置健康检查的 Pod,K8s 只能通过进程是否存在来判断健康状态。但很多时候进程还在,服务已经不响应了(比如死锁、OOM 边缘)。配置 livenessProbe 可以让 K8s 自动重启这些"假死"的容器。
:::
---
## 总结
Kubernetes 是容器编排的事实标准,理解它的核心概念是云原生开发的基础。
回顾本章的关键要点:
1. **声明式管理**:告诉 K8s "我要什么",而不是"怎么做",控制循环自动收敛
2. **架构分层**:控制平面负责决策,工作节点负责执行,etcd 存储状态
3. **核心资源**Pod(最小单元)、Deployment(副本管理)、Service(服务发现)、Ingress(外部入口)
4. **运维自动化**:滚动更新零停机、HPA 弹性伸缩、探针自动故障恢复
5. **配置分离**ConfigMap 和 Secret 让配置与镜像解耦
## 延伸阅读
- [Kubernetes 官方文档](https://kubernetes.io/zh-cn/docs/) - 最权威的中文参考
- [Kubernetes the Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way) - 从零手动搭建 K8s 集群
- [The Illustrated Children's Guide to Kubernetes](https://www.cncf.io/phippy/) - CNCF 出品的趣味入门
- [Kubernetes Patterns](https://www.oreilly.com/library/view/kubernetes-patterns-2nd/9781098131678/) - K8s 设计模式
@@ -1,3 +1,397 @@
# Linux 基础
> 待实现
::: tip 前言
**服务器的世界,Linux 是绝对的主角。** 全球超过 90% 的服务器运行 Linux,从你每天用的微信到 Google 搜索,背后都是 Linux 在支撑。作为开发者,掌握 Linux 基础不是可选项,而是必修课。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **文件系统**:理解 Linux 目录结构和"一切皆文件"的哲学
- **常用命令**:掌握文件操作、文本处理、进程管理等核心命令
- **权限模型**:理解用户、组、权限的概念
- **Shell 基础**:了解管道、重定向、环境变量等 Shell 核心概念
- **实战技能**:学会日志查看、进程排查、网络诊断等运维基本功
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 文件系统 | 目录结构、一切皆文件 |
| **第 2 章** | 常用命令 | 文件、文本、进程、网络 |
| **第 3 章** | 权限模型 | 用户、组、rwx、sudo |
| **第 4 章** | Shell 基础 | 管道、重定向、变量、脚本 |
| **第 5 章** | 实战场景 | 日志排查、性能诊断 |
---
## 1. 文件系统:一切皆文件
Linux 最核心的哲学之一就是**一切皆文件**。普通文件是文件,目录是文件,硬盘是文件,甚至网络连接、进程信息都是文件。这个统一的抽象让你可以用同一套工具(读、写、权限控制)操作几乎所有系统资源。
<LinuxFileSystemDemo />
### 目录结构速记
把 Linux 文件系统想象成一棵倒过来的树:
```
/ ← 根目录(树根)
├── home/ ← 用户的家(你的文件都在这)
├── etc/ ← 配置文件(系统的"设置面板")
├── var/ ← 变化的数据(日志、缓存)
├── usr/ ← 用户安装的程序
├── tmp/ ← 临时文件(重启就没了)
├── proc/ ← 进程信息(虚拟的,不占磁盘)
├── dev/ ← 设备文件(硬盘、终端)
├── bin/ ← 基础命令(ls、cp、mv)
├── sbin/ ← 系统管理命令(需要 root)
├── opt/ ← 第三方软件
└── root/ ← root 用户的家目录
```
### 路径的两种写法
| 类型 | 格式 | 示例 | 说明 |
|------|------|------|------|
| 绝对路径 | 从 `/` 开始 | `/home/alice/code/app.js` | 从根目录出发,不会歧义 |
| 相对路径 | 从当前目录开始 | `./code/app.js``../config` | `.` 是当前目录,`..` 是上级目录 |
::: tip "一切皆文件"的威力
想知道 CPU 信息?读文件:`cat /proc/cpuinfo`
想知道内存使用?读文件:`cat /proc/meminfo`
想产生随机数?读文件:`cat /dev/urandom`
想丢弃输出?写文件:`echo "no thanks" > /dev/null`
不需要专门的 API,读写文件就够了。这就是 Unix 哲学的优雅之处。
:::
---
## 2. 常用命令
Linux 命令遵循一个统一的格式:`命令 [选项] [参数]`。比如 `ls -la /home` 中,`ls` 是命令,`-la` 是选项,`/home` 是参数。
<LinuxCommandDemo />
### 最常用的 10 个命令
如果只能记住 10 个命令,记这些:
| 命令 | 用途 | 记忆技巧 |
|------|------|----------|
| `ls` | 列出文件 | list |
| `cd` | 切换目录 | change directory |
| `cat` | 查看文件 | concatenate |
| `grep` | 搜索文本 | global regular expression print |
| `find` | 查找文件 | 就是 find |
| `ps` | 查看进程 | process status |
| `tail -f` | 实时看日志 | 看文件"尾巴"-f 是 follow |
| `chmod` | 改权限 | change mode |
| `curl` | 发 HTTP 请求 | client URL |
| `ssh` | 远程登录 | secure shell |
### 命令组合的艺术
Linux 的强大不在于单个命令,而在于**命令组合**。通过管道 `|` 把多个简单命令串起来,解决复杂问题:
```bash
# 找出占用 CPU 最多的 5 个进程
ps aux --sort=-%cpu | head -6
# 统计日志中出现最多的错误类型
grep "ERROR" app.log | awk '{print $4}' | sort | uniq -c | sort -rn | head -10
# 查找大于 100MB 的文件
find / -size +100M -type f 2>/dev/null
# 实时监控日志中的错误
tail -f /var/log/app.log | grep --color "ERROR"
```
::: tip Unix 哲学
"做一件事,做好它。" 每个命令只负责一个功能,通过管道组合实现复杂操作。这就是为什么 Linux 命令都很短小——它们是积木,不是瑞士军刀。
:::
---
## 3. 权限模型
Linux 是多用户系统,权限模型是安全的基石。每个文件都有三组权限,分别控制**所有者(Owner)**、**所属组(Group**、**其他人(Others**能做什么。
### 读懂 `ls -l` 的输出
```bash
$ ls -l app.js
-rwxr-xr-- 1 alice developers 2048 Jan 15 10:30 app.js
│├──┤├──┤├──┤ │ │ │
│ │ │ │ │ │ └── 文件大小
│ │ │ │ │ └── 所属组
│ │ │ │ └── 所有者
│ │ │ └── 其他人权限:r-- (只读)
│ │ └── 组权限:r-x (读+执行)
│ └── 所有者权限:rwx (读+写+执行)
└── 文件类型:- 普通文件,d 目录,l 链接
```
### 权限的三种操作
| 权限 | 字母 | 数字 | 对文件的含义 | 对目录的含义 |
|------|------|------|-------------|-------------|
| 读 | `r` | 4 | 查看文件内容 | 列出目录内容(ls) |
| 写 | `w` | 2 | 修改文件内容 | 创建/删除目录中的文件 |
| 执行 | `x` | 1 | 运行程序/脚本 | 进入目录(cd) |
<LinuxPermissionsDemo />
### 数字权限速算
三个数字分别代表 Owner、Group、Others 的权限,每个数字是 r(4) + w(2) + x(1) 的和:
```
chmod 755 script.sh
7 = rwx (4+2+1) → 所有者:读+写+执行
5 = r-x (4+0+1) → 组:读+执行
5 = r-x (4+0+1) → 其他人:读+执行
```
| 常见权限 | 含义 | 典型用途 |
|---------|------|---------|
| `644` | rw-r--r-- | 普通文件(所有者可写,其他人只读) |
| `755` | rwxr-xr-x | 可执行文件/目录 |
| `600` | rw------- | 私密文件(如 SSH 密钥) |
| `777` | rwxrwxrwx | 所有人可读写执行(危险,避免使用) |
### sudo:临时获取超级权限
普通用户权限有限,有些操作需要 root 权限。`sudo` 让你临时以 root 身份执行命令:
```bash
# 普通用户无法修改系统配置
$ vim /etc/nginx/nginx.conf
# Permission denied
# 用 sudo 临时提权
$ sudo vim /etc/nginx/nginx.conf
# 输入你的密码后可以编辑
# 切换到 root 用户(谨慎使用)
$ sudo su -
```
::: warning 最小权限原则
永远不要用 `chmod 777` 解决权限问题,这等于把门锁拆了。正确做法是搞清楚谁需要什么权限,精确授予。同样,不要长期以 root 身份操作,只在必要时用 `sudo`
:::
---
## 4. Shell 基础
Shell 是你和 Linux 内核之间的"翻译官"。你输入命令,Shell 解释并交给内核执行。最常用的 Shell 是 **Bash**(大多数 Linux 发行版默认)和 **Zsh**macOS 默认)。
### 管道与重定向
这是 Shell 最强大的两个特性:
| 符号 | 名称 | 作用 | 示例 |
|------|------|------|------|
| `|` | 管道 | 把前一个命令的输出作为后一个的输入 | `cat log | grep ERROR` |
| `>` | 输出重定向 | 把输出写入文件(覆盖) | `echo "hello" > file.txt` |
| `>>` | 追加重定向 | 把输出追加到文件末尾 | `echo "world" >> file.txt` |
| `<` | 输入重定向 | 从文件读取输入 | `wc -l < file.txt` |
| `2>` | 错误重定向 | 把错误信息写入文件 | `cmd 2> error.log` |
| `2>&1` | 合并输出 | 把错误和正常输出合并 | `cmd > all.log 2>&1` |
### 环境变量
环境变量是 Shell 中的"全局配置",影响命令的行为:
```bash
# 查看所有环境变量
env
# 查看某个变量
echo $PATH
echo $HOME
# 临时设置(只在当前 Shell 有效)
export API_KEY="abc123"
# 永久设置(写入配置文件)
echo 'export API_KEY="abc123"' >> ~/.bashrc
source ~/.bashrc # 让配置立即生效
```
| 常见变量 | 含义 | 示例值 |
|---------|------|--------|
| `$PATH` | 命令搜索路径 | `/usr/local/bin:/usr/bin:/bin` |
| `$HOME` | 用户主目录 | `/home/alice` |
| `$USER` | 当前用户名 | `alice` |
| `$PWD` | 当前工作目录 | `/var/log` |
| `$SHELL` | 当前使用的 Shell | `/bin/bash` |
### Shell 脚本入门
把多个命令写进一个文件,就是 Shell 脚本。它是自动化运维的起点:
```bash
#!/bin/bash
# deploy.sh - 简单的部署脚本
APP_DIR="/opt/myapp"
LOG_FILE="/var/log/deploy.log"
echo "$(date) - 开始部署..." >> $LOG_FILE
# 拉取最新代码
cd $APP_DIR && git pull origin main
# 安装依赖
npm install --production
# 重启服务
pm2 restart myapp
echo "$(date) - 部署完成" >> $LOG_FILE
```
```bash
# 给脚本执行权限并运行
chmod +x deploy.sh
./deploy.sh
```
::: tip 脚本调试技巧
在脚本开头加 `set -ex``-e` 让脚本遇到错误立即退出(而不是继续执行),`-x` 会打印每条执行的命令(方便排查问题)。这两个选项在生产脚本中几乎是标配。
:::
---
## 5. 实战场景
理论学完了,来看几个开发中最常遇到的实战场景。
### 5.1 日志排查
服务出问题,第一反应就是看日志。以下是日志排查的常用套路:
```bash
# 1. 实时跟踪日志(最常用)
tail -f /var/log/app/error.log
# 2. 搜索特定时间段的错误
grep "2024-01-15 14:" error.log | grep "ERROR"
# 3. 统计每小时的错误数量
grep "ERROR" app.log | awk '{print substr($1,1,13)}' | uniq -c
# 4. 查看最近 100 行日志
tail -100 app.log
# 5. 在多个日志文件中搜索
grep -r "OutOfMemory" /var/log/app/
```
### 5.2 进程排查
应用卡死、CPU 飙高、内存泄漏——这些问题都需要从进程入手:
```bash
# 查看 CPU 占用最高的进程
ps aux --sort=-%cpu | head -10
# 查看内存占用最高的进程
ps aux --sort=-%mem | head -10
# 查找特定进程
ps aux | grep "node"
# 查看进程的详细信息(包括线程)
top -Hp <PID>
# 查看进程打开的文件
lsof -p <PID>
# 优雅终止进程(SIGTERM
kill <PID>
# 强制终止(SIGKILL,最后手段)
kill -9 <PID>
```
### 5.3 网络诊断
服务连不上?先搞清楚是网络问题还是应用问题:
```bash
# 测试目标是否可达
ping -c 4 google.com
# 检查端口是否开放
telnet db-server 3306
# 或者用 nc
nc -zv db-server 3306
# 查看本机监听的端口
ss -tlnp
# 或
netstat -tlnp
# DNS 解析检查
dig api.example.com
nslookup api.example.com
# 测试 HTTP 接口
curl -v http://localhost:3000/health
# 查看网络连接状态统计
ss -s
```
### 5.4 磁盘空间排查
磁盘满了是线上最常见的故障之一:
```bash
# 查看各分区使用情况
df -h
# 找出占用空间最大的目录
du -sh /* 2>/dev/null | sort -rh | head -10
# 进一步定位大目录
du -sh /var/log/* | sort -rh | head -10
# 查找大文件(>100MB
find / -type f -size +100M 2>/dev/null | head -20
# 清理常见的空间占用
# 清理旧日志
sudo journalctl --vacuum-size=500M
# 清理 Docker 无用镜像
docker system prune -a
```
::: tip 线上排查口诀
**"一看日志,二看进程,三看网络,四看磁盘"**。90% 的线上问题都能通过这四步定位到原因。养成习惯后,排查效率会大幅提升。
:::
---
## 总结
Linux 是开发者的必备技能,掌握基础就能应对大部分日常开发和运维场景。
回顾本章的关键要点:
1. **一切皆文件**:Linux 用文件抽象统一了对硬件、进程、网络等资源的访问方式
2. **命令组合**:单个命令功能简单,通过管道 `|` 组合才能发挥真正威力
3. **权限模型**Owner/Group/Others × Read/Write/Execute,用数字(如 755)快速设置
4. **Shell 基础**:管道、重定向、环境变量、脚本是自动化的基石
5. **实战排查**:日志 → 进程 → 网络 → 磁盘,四步定位大部分线上问题
## 延伸阅读
- [Linux 命令大全](https://man7.org/linux/man-pages/) - Linux man pages 官方文档
- [The Linux Command Line](https://linuxcommand.org/tlcl.php) - 免费的 Linux 命令行入门书
- [Linux Journey](https://linuxjourney.com/) - 交互式 Linux 学习网站
- [explainshell.com](https://explainshell.com/) - 输入命令自动解释每个参数的含义
@@ -1,3 +1,315 @@
# 神经网络与深度学习
> 待实现
::: tip 前言
**神经网络是 AI 革命的引擎。** 从 ChatGPT 的语言理解到自动驾驶的图像识别,背后都是神经网络在工作。它不是魔法,而是一套精巧的数学框架——通过大量数据"学习"出输入到输出的映射关系。理解它的基本原理,能帮你更好地使用和调试 AI 工具。
:::
**这篇文章会带你学什么?**
学完这章后,你将获得:
- **核心概念**:理解神经元、层、前向传播、反向传播的基本原理
- **网络类型**:了解 CNN、RNN、Transformer 等主流架构的特点和适用场景
- **训练过程**:明白模型是如何从数据中"学习"的
- **关键技巧**:掌握过拟合、学习率、正则化等实用概念
- **发展脉络**:了解从感知机到大语言模型的演进历程
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 从神经元到网络 | 感知机、激活函数、前向传播 |
| **第 2 章** | 网络如何学习 | 损失函数、梯度下降、反向传播 |
| **第 3 章** | 主流网络架构 | CNN、RNN、Transformer |
| **第 4 章** | 训练的艺术 | 过拟合、正则化、超参数调优 |
| **第 5 章** | 发展历程与前沿 | 从感知机到 GPT |
---
## 1. 从神经元到网络
### 单个神经元
神经网络的最小单元是**神经元**(Neuron)。它模拟了生物神经元的工作方式:接收多个输入信号,加权求和,通过激活函数产生输出。
```
输入 x1 ──→ ×w1 ──┐
输入 x2 ──→ ×w2 ──┼──→ Σ(加权求和) + b(偏置) ──→ f(激活函数) ──→ 输出
输入 x3 ──→ ×w3 ──┘
```
数学表达:**y = f(w₁x₁ + w₂x₂ + w₃x₃ + b)**
<NeuronDemo />
### 激活函数:为什么需要非线性?
如果没有激活函数,无论多少层神经元叠加,最终都等价于一个线性变换(矩阵乘法)。激活函数引入**非线性**,让网络能学习复杂的模式。
| 激活函数 | 公式 | 特点 | 常用场景 |
|---------|------|------|---------|
| ReLU | max(0, x) | 简单高效,训练快 | 隐藏层的默认选择 |
| Sigmoid | 1/(1+e⁻ˣ) | 输出 0~1 | 二分类输出层 |
| Tanh | (eˣ-e⁻ˣ)/(eˣ+e⁻ˣ) | 输出 -1~1 | RNN 中常用 |
| Softmax | eˣᵢ/Σeˣⱼ | 输出概率分布 | 多分类输出层 |
### 从神经元到网络
把多个神经元组织成**层**,多个层串联起来,就构成了神经网络:
```
输入层 隐藏层1 隐藏层2 输出层
(特征) (提取低级特征) (提取高级特征) (预测结果)
x1 ──→ [○ ○ ○ ○] ──→ [○ ○ ○] ──→ [○ ○]
x2 ──→ [○ ○ ○ ○] ──→ [○ ○ ○] ──→ 猫/狗
x3 ──→ [○ ○ ○ ○] ──→ [○ ○ ○]
```
| 概念 | 说明 |
|------|------|
| 输入层 | 接收原始数据(图片像素、文本向量等) |
| 隐藏层 | 中间处理层,层数越多网络越"深"(深度学习的"深" |
| 输出层 | 产生最终预测(分类概率、回归值等) |
| 前向传播 | 数据从输入层逐层流向输出层的过程 |
::: tip 为什么叫"深度"学习?
传统机器学习通常只有 1-2 层。当隐藏层数量增加到几十甚至上百层时,就叫"深度"学习。更深的网络能学习更抽象的特征:第一层学边缘,第二层学纹理,第三层学部件,更深的层学到"这是一只猫"。
:::
---
## 2. 网络如何学习
神经网络的"学习"本质上是一个**优化问题**:找到一组权重(w)和偏置(b),使得网络的预测尽可能接近真实答案。
### 训练三步曲
```
1. 前向传播:输入数据,得到预测结果
2. 计算损失:用损失函数衡量预测与真实值的差距
3. 反向传播:根据损失,计算每个权重的梯度,更新权重
重复以上步骤,直到损失足够小
```
### 损失函数:衡量"错得有多离谱"
损失函数(Loss Function)量化了预测值和真实值之间的差距。训练的目标就是最小化损失。
| 损失函数 | 公式简述 | 适用场景 |
|---------|---------|---------|
| MSE(均方误差) | 预测值与真实值差的平方的均值 | 回归问题 |
| Cross-Entropy(交叉熵) | -Σ y·log(ŷ) | 分类问题 |
| Binary Cross-Entropy | 交叉熵的二分类版本 | 二分类问题 |
### 梯度下降:找到最低点
想象你站在一座山上,蒙着眼睛要走到最低点。你能做的就是**摸一下脚下的坡度,然后往下坡方向走一步**。这就是梯度下降。
```
损失值
│ ╱╲
│ ╱ ╲ ← 当前位置
│ ╱ ╲ ↙ 沿梯度方向下降
│ ╱ ╲╱ ← 局部最小值
│╱ ╲╱ ← 全局最小值
└──────────────→ 权重值
```
| 概念 | 说明 |
|------|------|
| 梯度 | 损失函数对每个权重的偏导数,指示"往哪个方向调整能减少损失" |
| 学习率 | 每一步走多远。太大会跳过最低点,太小会收敛太慢 |
| 批量大小 | 每次用多少样本计算梯度。全量太慢,单样本太抖,小批量(mini-batch)是折中 |
### 反向传播:链式法则的胜利
反向传播(Backpropagation)是计算梯度的高效算法。它利用微积分的**链式法则**,从输出层开始,逐层向后计算每个权重对损失的贡献。
```
前向传播:输入 → 隐藏层1 → 隐藏层2 → 输出 → 损失
反向传播:损失 → 输出 → 隐藏层2 → 隐藏层1 → 更新所有权重
```
::: tip 直觉理解反向传播
把神经网络想象成一条流水线。产品(预测)出了问题(损失大),你需要从最后一道工序开始往回查,看每道工序(每层权重)对最终问题贡献了多少,然后按贡献大小调整。贡献大的多调,贡献小的少调。
:::
---
## 3. 主流网络架构
不同类型的数据需要不同的网络架构。选对架构,事半功倍。
<NetworkLayersDemo />
### 3.1 CNN(卷积神经网络)
CNN 是处理图像的王者。核心思想:用小的卷积核在图像上滑动,提取局部特征。
```
输入图像 → [卷积层→激活→池化] × N → 全连接层 → 输出
28×28 提取边缘/纹理/形状 分类结果
```
| 特点 | 说明 |
|------|------|
| 局部连接 | 每个神经元只看一小块区域,而非整张图 |
| 参数共享 | 同一个卷积核在整张图上复用,大幅减少参数 |
| 平移不变性 | 猫在图片左边还是右边,都能识别 |
| 层级特征 | 浅层学边缘,深层学语义 |
代表模型:LeNet、AlexNet、VGG、ResNet、EfficientNet
### 3.2 RNN(循环神经网络)
RNN 专为**序列数据**设计。它的隐藏状态会传递到下一个时间步,让网络具有"记忆"能力。
```
时间步 t1 时间步 t2 时间步 t3
"我" ──→ "喜欢" ──→ "猫"
↓ ↓ ↓
[h1] ──→ [h2] ──→ [h3] ──→ 输出
↑ ↑ ↑
隐藏状态在时间步之间传递(记忆)
```
| 变体 | 解决的问题 | 核心机制 |
|------|-----------|---------|
| 原始 RNN | 基础序列建模 | 简单循环连接 |
| LSTM | 长序列梯度消失 | 遗忘门、输入门、输出门 |
| GRU | LSTM 参数太多 | 简化为重置门和更新门 |
| 双向 RNN | 只能看到过去 | 同时从前往后和从后往前处理 |
::: tip LSTM 的门控机制
LSTM 的精妙之处在于三个"门":**遗忘门**决定丢弃哪些旧记忆,**输入门**决定存入哪些新信息,**输出门**决定输出哪些内容。就像你读一本书,会选择性地记住重要情节、忘掉无关细节。
:::
### 3.3 Transformer:注意力就是一切
2017 年 Google 发表的 "Attention Is All You Need" 论文提出了 Transformer,彻底改变了 AI 领域。它用**自注意力机制**替代了循环结构,是 GPT、BERT、Claude 等大模型的基础。
```
输入序列 → 嵌入 + 位置编码 → [多头注意力 → 前馈网络] × N → 输出
每个词都能"看到"所有其他词
```
| 优势 | 说明 |
|------|------|
| 并行计算 | 不像 RNN 必须逐步处理,Transformer 可以并行处理整个序列 |
| 长距离依赖 | 任意两个位置之间直接建立联系,不受距离限制 |
| 可扩展性 | 模型越大、数据越多,效果越好(Scaling Law |
**自注意力的直觉**:读"小猫坐在垫子上,因为**它**很累"这句话时,"它"需要关注"小猫"才能理解含义。自注意力让模型学会这种关联——为序列中的每对词计算一个"相关性分数"。
<NetworkArchitectureDemo />
## 4. 训练的艺术
有了好的架构还不够,训练过程中有很多"坑"需要避开。
### 4.1 过拟合 vs 欠拟合
| 问题 | 表现 | 原因 | 解决方案 |
|------|------|------|---------|
| 过拟合 | 训练集表现好,测试集表现差 | 模型太复杂,"背答案"而非学规律 | 正则化、Dropout、数据增强、早停 |
| 欠拟合 | 训练集和测试集都表现差 | 模型太简单,学不到规律 | 增加模型容量、训练更久、更好的特征 |
```
误差
│ ╲ 训练误差 测试误差 ╱
│ ╲ ╱
│ ╲─────────────────╱
│ 欠拟合 ← 最佳点 → 过拟合
└──────────────────────────→ 模型复杂度
```
### 4.2 关键超参数
超参数是训练前需要人为设定的参数(不是模型自己学的):
| 超参数 | 作用 | 常见范围 | 调优建议 |
|--------|------|---------|---------|
| 学习率 | 每步更新的幅度 | 1e-5 ~ 1e-1 | 最重要的超参数,通常从 1e-3 开始 |
| 批量大小 | 每次训练用多少样本 | 16 ~ 512 | 越大训练越稳定,但需要更多显存 |
| 训练轮数(Epoch) | 遍历整个数据集的次数 | 10 ~ 100+ | 配合早停法,验证集不再提升就停 |
| 优化器 | 梯度更新策略 | Adam、SGD | Adam 是默认选择,SGD+动量适合精调 |
### 4.3 正则化技巧
防止过拟合的常用手段:
| 技巧 | 原理 | 使用方式 |
|------|------|---------|
| Dropout | 训练时随机关闭部分神经元 | 通常 p=0.1~0.5 |
| 权重衰减 | 在损失函数中加入权重大小的惩罚 | L2 正则化,λ=1e-4 |
| 数据增强 | 对训练数据做随机变换(翻转、裁剪、旋转) | 图像任务必备 |
| 早停法 | 验证集损失不再下降时停止训练 | patience=5~10 |
| Batch Normalization | 标准化每层的输入分布 | 加速收敛,有轻微正则化效果 |
::: tip 训练的经验法则
1. 先用小数据集跑通整个流程,确认代码没 bug
2. 从已有的预训练模型开始微调,而非从零训练
3. 学习率是最值得花时间调的超参数
4. 如果训练损失不下降,先检查数据和代码,再怀疑模型
:::
---
## 5. 发展历程与前沿
神经网络的发展经历了几次"寒冬"和"复兴",每次突破都源于关键的技术创新。
| 年代 | 里程碑 | 关键突破 |
|------|--------|---------|
| 1958 | 感知机(Perceptron) | 第一个神经网络模型,只能处理线性问题 |
| 1986 | 反向传播算法 | 让多层网络的训练成为可能 |
| 1998 | LeNetCNN) | 卷积网络在手写数字识别上大获成功 |
| 2012 | AlexNet | 深度 CNN 在 ImageNet 上碾压传统方法,深度学习爆发 |
| 2014 | GAN(生成对抗网络) | 两个网络对抗训练,能生成逼真图像 |
| 2017 | Transformer | "Attention Is All You Need",注意力机制取代 RNN |
| 2018 | BERT | 预训练+微调范式,NLP 全面突破 |
| 2020 | GPT-3 | 1750 亿参数,展示了大模型的涌现能力 |
| 2022 | ChatGPT | RLHF 对齐技术,AI 进入大众视野 |
| 2023+ | 多模态大模型 | GPT-4V、Claude 等,同时理解文本和图像 |
### 当前趋势
| 方向 | 说明 |
|------|------|
| 大模型(LLM) | 参数量从亿级到万亿级,涌现出推理、编程等能力 |
| 多模态 | 同一个模型处理文本、图像、音频、视频 |
| 高效微调 | LoRA、QLoRA 等技术让普通开发者也能微调大模型 |
| AI Agent | 让大模型使用工具、规划任务、自主完成复杂目标 |
| 小模型蒸馏 | 用大模型的知识训练小模型,在端侧部署 |
::: tip 对开发者的启示
你不需要从零训练神经网络。现代 AI 开发更多是**调用 API**(如 OpenAI、Claude API)或**微调预训练模型**(如用 Hugging Face)。但理解底层原理能帮你更好地选择模型、设计 prompt、诊断问题。
:::
---
## 总结
| 核心概念 | 一句话总结 |
|---------|-----------|
| 神经元 | 加权求和 + 激活函数,网络的最小计算单元 |
| 前向传播 | 数据从输入层逐层流向输出层,产生预测 |
| 反向传播 | 从损失出发,逐层计算梯度,更新权重 |
| CNN | 卷积核提取局部特征,图像处理的首选 |
| RNN/LSTM | 循环连接保持记忆,处理序列数据 |
| Transformer | 自注意力并行处理,大模型的基础架构 |
| 过拟合 | 模型"背答案",用正则化、Dropout 等手段防止 |
| 迁移学习 | 站在巨人肩膀上,用预训练模型微调解决新问题 |
---
## 延伸阅读
- [3Blue1Brown - 神经网络系列视频](https://www.3blue1brown.com/topics/neural-networks) — 最直观的可视化讲解
- [Stanford CS231n](http://cs231n.stanford.edu/) — 经典的卷积神经网络课程
- [The Illustrated Transformer](https://jalammar.github.io/illustrated-transformer/) — 图解 Transformer 架构
- [Neural Networks and Deep Learning](http://neuralnetworksanddeeplearning.com/) — 免费在线教材
- [Hugging Face 课程](https://huggingface.co/learn) — 动手实践 Transformer 和大模型
+8
View File
@@ -2,6 +2,14 @@
本附录涵盖从计算机基础到工程素养的完整知识体系,是你学习旅程中的重要参考库。
## 📍 附录知识地图
点击下方的分类卡片,查看每个分类的完整学习路径:
<AppendixFlowMap />
---
## 内容分类
### 一、计算机是怎么回事