Files
test-repo/docs/.vitepress/theme/components/appendix/component-state-management/StateManagementComparisonDemo.vue
T

544 lines
12 KiB
Vue
Raw Normal View History

<template>
<div class="state-management-comparison">
<div class="demo-header">
<h4>状态管理库全景对比</h4>
<p class="hint">全面对比主流状态管理方案的特性适用场景和学习曲线</p>
</div>
<!-- 简化版对比表格 -->
<div class="comparison-table-wrapper">
<table class="comparison-table">
<thead>
<tr>
<th class="feature-col">特性</th>
<th v-for="lib in libraries" :key="lib.id" class="lib-col">
<div class="lib-header">
<span class="lib-icon">{{ lib.icon }}</span>
<span class="lib-name">{{ lib.name }}</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="feature-name">学习曲线</td>
<td v-for="lib in libraries" :key="lib.id" class="feature-value">
<div class="curve-bar">
<div class="curve-fill" :style="{ width: lib.learningCurve + '%', background: getCurveColor(lib.learningCurve) }"></div>
</div>
<span class="curve-label">{{ getCurveLabel(lib.learningCurve) }}</span>
</td>
</tr>
<tr>
<td class="feature-name">包大小</td>
<td v-for="lib in libraries" :key="lib.id" class="feature-value">
<span class="size-badge" :class="getSizeClass(lib.bundleSize)">{{ lib.bundleSize }}</span>
</td>
</tr>
<tr>
<td class="feature-name">TypeScript</td>
<td v-for="lib in libraries" :key="lib.id" class="feature-value">
<span class="boolean-badge" :class="{ yes: lib.typescript, no: !lib.typescript }">
{{ lib.typescript ? '✓' : '✗' }}
</span>
</td>
</tr>
<tr>
<td class="feature-name">开发工具</td>
<td v-for="lib in libraries" :key="lib.id" class="feature-value">
<span class="boolean-badge" :class="{ yes: lib.devtools, no: !lib.devtools }">
{{ lib.devtools ? '✓' : '✗' }}
</span>
</td>
</tr>
<tr>
<td class="feature-name">SSR 支持</td>
<td v-for="lib in libraries" :key="lib.id" class="feature-value">
<span class="boolean-badge" :class="{ yes: lib.ssr, no: !lib.ssr }">
{{ lib.ssr ? '✓' : '✗' }}
</span>
</td>
</tr>
<tr>
<td class="feature-name">适用框架</td>
<td v-for="lib in libraries" :key="lib.id" class="feature-value">
<span class="text-value">{{ lib.framework }}</span>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 选中库的详细信息 -->
<div v-if="selectedLibrary" class="library-detail">
<div class="detail-header">
<span class="detail-icon">{{ selectedLibrary.icon }}</span>
<div class="detail-title">
<h5>{{ selectedLibrary.name }}</h5>
<span class="detail-tagline">{{ selectedLibrary.tagline }}</span>
</div>
<a :href="selectedLibrary.docsUrl" target="_blank" class="docs-link">
官方文档
</a>
</div>
<div class="detail-grid">
<div class="detail-section">
<h6>🎯 适用场景</h6>
<ul>
<li v-for="(scenario, index) in selectedLibrary.scenarios" :key="index">{{ scenario }}</li>
</ul>
</div>
<div class="detail-section">
<h6> 优势</h6>
<ul class="advantages">
<li v-for="(pro, index) in selectedLibrary.pros" :key="index">{{ pro }}</li>
</ul>
</div>
<div class="detail-section">
<h6> 劣势</h6>
<ul class="disadvantages">
<li v-for="(con, index) in selectedLibrary.cons" :key="index">{{ con }}</li>
</ul>
</div>
</div>
</div>
<!-- 决策流程图 -->
<div class="decision-flow">
<h5>🤔 如何选择</h5>
<div class="flow-chart">
<div class="flow-node start">开始</div>
<div class="flow-arrow"></div>
<div class="flow-node question">需要跨框架支持</div>
<div class="flow-arrow"> </div>
<div class="flow-node result">考虑 Pinia / Vuex</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const libraries = [
{
id: 'redux',
name: 'Redux',
icon: '🔄',
tagline: 'JavaScript 应用的可预测状态容器',
docsUrl: 'https://redux.js.org/',
scenarios: ['大型企业级应用', '需要严格数据流控制', '复杂的状态逻辑'],
pros: ['严格的数据流,易于调试', '强大的中间件生态', '时间旅行调试', '可预测的状态更新'],
cons: ['学习曲线陡峭', '样板代码较多', '小型项目可能过于复杂'],
codeExample: '// Redux 示例代码',
learningCurve: 80,
bundleSize: '7KB',
typescript: true,
devtools: true,
ssr: true,
framework: 'React/Vue/Angular'
},
{
id: 'vuex',
name: 'Vuex',
icon: '🌿',
tagline: 'Vue.js 的官方状态管理库',
docsUrl: 'https://vuex.vuejs.org/',
scenarios: ['Vue 2/3 中大型项目', '需要模块化管理状态', '团队成员熟悉 Vue 生态'],
pros: ['与 Vue 深度集成', '响应式系统', '模块化管理', '优秀的开发工具'],
cons: ['仅适用于 Vue', 'Vue 3 中被 Pinia 取代', '相对冗余的 API'],
codeExample: '// Vuex 示例代码',
learningCurve: 60,
bundleSize: '4KB',
typescript: true,
devtools: true,
ssr: true,
framework: 'Vue Only'
},
{
id: 'pinia',
name: 'Pinia',
icon: '🍍',
tagline: '直观、类型安全、灵活的 Vue Store',
docsUrl: 'https://pinia.vuejs.org/',
scenarios: ['Vue 3 新项目首选', '重视 TypeScript 支持', '希望简化状态管理'],
pros: ['轻量级设计', '原生 TypeScript 支持', '组合式 API 风格', '代码更简洁'],
cons: ['Vue 3 专属', '生态系统相对年轻', '大型项目需自定义规范'],
codeExample: '// Pinia 示例代码',
learningCurve: 30,
bundleSize: '2KB',
typescript: true,
devtools: true,
ssr: true,
framework: 'Vue 3 Only'
}
]
const features = [
{ key: 'learningCurve', label: '学习曲线', icon: '📈' },
{ key: 'bundleSize', label: '包大小', icon: '📦' },
{ key: 'typescript', label: 'TypeScript', icon: '🔷' },
{ key: 'devtools', label: '开发工具', icon: '🛠️' },
{ key: 'ssr', label: 'SSR 支持', icon: '🚀' },
{ key: 'framework', label: '适用框架', icon: '🔧' }
]
const selectedLib = ref(null)
const selectedLibrary = computed(() => {
if (!selectedLib.value) return null
return libraries.find(lib => lib.id === selectedLib.value)
})
function selectLib(id) {
selectedLib.value = id
}
function getValue(lib, key) {
return lib[key]
}
function getCurveColor(value) {
if (value <= 30) return '#22c55e'
if (value <= 60) return '#f59e0b'
return '#ef4444'
}
function getCurveLabel(value) {
if (value <= 30) return '简单'
if (value <= 60) return '中等'
return '陡峭'
}
function getSizeClass(size) {
const num = parseInt(size)
if (num <= 2) return 'small'
if (num <= 5) return 'medium'
return 'large'
}
</script>
<style scoped>
.state-management-comparison {
padding: 1rem;
background: var(--vp-c-bg-soft);
border-radius: 8px;
}
.demo-header {
margin-bottom: 1.5rem;
text-align: center;
}
.demo-header h4 {
margin: 0 0 0.5rem;
color: var(--vp-c-text-1);
}
.hint {
margin: 0;
color: var(--vp-c-text-2);
font-size: 0.9rem;
}
.comparison-table-wrapper {
overflow-x: auto;
margin-bottom: 1.5rem;
}
.comparison-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
.comparison-table th,
.comparison-table td {
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
text-align: left;
}
.comparison-table th {
background: var(--vp-c-bg);
font-weight: 600;
}
.feature-col {
width: 120px;
background: var(--vp-c-bg-soft);
}
.lib-col {
min-width: 120px;
cursor: pointer;
transition: background 0.2s;
}
.lib-col:hover,
.lib-col.selected {
background: rgba(102, 126, 234, 0.1);
}
.lib-header {
display: flex;
align-items: center;
gap: 0.5rem;
}
.lib-icon {
font-size: 1.2rem;
}
.lib-name {
font-weight: 500;
}
.feature-name {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
}
.feature-value {
text-align: center;
}
.curve-bar {
width: 100%;
height: 6px;
background: var(--vp-c-divider);
border-radius: 3px;
overflow: hidden;
margin-bottom: 0.25rem;
}
.curve-fill {
height: 100%;
border-radius: 3px;
transition: width 0.3s;
}
.curve-label {
font-size: 0.75rem;
color: var(--vp-c-text-2);
}
.size-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 500;
}
.size-badge.small {
background: rgba(34, 197, 94, 0.1);
color: #22c55e;
}
.size-badge.medium {
background: rgba(245, 158, 11, 0.1);
color: #f59e0b;
}
.size-badge.large {
background: rgba(239, 68, 68, 0.1);
color: #ef4444;
}
.boolean-badge {
display: inline-block;
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
border-radius: 50%;
font-size: 0.8rem;
font-weight: 600;
}
.boolean-badge.yes {
background: rgba(34, 197, 94, 0.1);
color: #22c55e;
}
.boolean-badge.no {
background: rgba(239, 68, 68, 0.1);
color: #ef4444;
}
.text-value {
font-size: 0.85rem;
color: var(--vp-c-text-2);
}
.library-detail {
margin-top: 1.5rem;
padding: 1rem;
background: var(--vp-c-bg);
border-radius: 8px;
border: 1px solid var(--vp-c-divider);
}
.detail-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.detail-icon {
font-size: 2rem;
}
.detail-title {
flex: 1;
}
.detail-title h5 {
margin: 0 0 0.25rem;
font-size: 1.2rem;
}
.detail-tagline {
color: var(--vp-c-text-2);
font-size: 0.9rem;
}
.docs-link {
padding: 0.5rem 1rem;
background: var(--vp-c-brand);
color: white;
border-radius: 6px;
text-decoration: none;
font-size: 0.85rem;
transition: opacity 0.2s;
}
.docs-link:hover {
opacity: 0.9;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.detail-section {
padding: 1rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.detail-section h6 {
margin: 0 0 0.75rem;
font-size: 1rem;
color: var(--vp-c-text-1);
}
.detail-section ul {
margin: 0;
padding-left: 1.2rem;
}
.detail-section li {
margin: 0.5rem 0;
color: var(--vp-c-text-2);
font-size: 0.9rem;
}
.code-block {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
overflow-x: auto;
}
.code-block pre {
margin: 0;
font-size: 0.85rem;
line-height: 1.6;
}
.code-block code {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
color: var(--vp-c-text-1);
}
.decision-flow {
margin-top: 1.5rem;
padding: 1rem;
background: var(--vp-c-bg);
border-radius: 8px;
border: 1px solid var(--vp-c-divider);
}
.decision-flow h5 {
margin: 0 0 1rem;
text-align: center;
font-size: 1.1rem;
}
.flow-chart {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.flow-node {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-size: 0.9rem;
font-weight: 500;
text-align: center;
}
.flow-node.start {
background: var(--vp-c-brand);
color: white;
}
.flow-node.question {
background: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-divider);
}
.flow-node.result {
background: rgba(34, 197, 94, 0.1);
color: #22c55e;
border: 2px solid #22c55e;
}
.flow-arrow {
font-size: 1.2rem;
color: var(--vp-c-text-2);
}
@media (max-width: 768px) {
.comparison-table {
font-size: 0.8rem;
}
.comparison-table th,
.comparison-table td {
padding: 0.5rem;
}
.lib-icon {
font-size: 1rem;
}
.detail-grid {
grid-template-columns: 1fr;
}
}
</style>