Files
test-repo/docs/.vitepress/theme/components/appendix/web-basics/ImperativeVsDeclarativeDemo.vue
T
sanbuphy ebe2bf6109 feat: enhance demo components with consistent styling and info boxes
- Add standardized header and info box components to all demo files
- Improve visual consistency with theme colors and spacing
- Add max-height and overflow-y for better content containment
- Update package.json build script with --force flag
- Add .gitignore entries for REFACTORING files
- Fix table formatting in audio-intro.md
2026-02-14 12:14:07 +08:00

313 lines
6.8 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="imperative-declarative-demo">
<div class="demo-header">
<span class="icon">🔄</span>
<span class="title">命令式 vs 声明式</span>
<span class="subtitle">两种编程思维的对比通俗说手动操作 vs 自动响应</span>
</div>
<div class="demo-content">
<div class="demo-grid">
<!-- Imperative (jQuery Style) -->
<div class="panel imperative">
<div class="panel-header">
<span class="badge yellow">命令式 (Imperative)</span>
<span class="sub-text">jQuery Style - 手动操作</span>
</div>
<div class="code-preview">
<code>
// 手动操作 DOM<br />
$('#count').text(val);<br />
if (val > 5) $('#msg').show();
</code>
</div>
<div class="interactive-area">
<div class="output-box">
Count: <span id="imp-count-display">{{ impCount }}</span>
<div v-show="impShowMsg" class="warning-msg"> Count is high!</div>
</div>
<div class="actions">
<button @click="impIncrement" class="btn">Step 1: Value++</button>
<button @click="impUpdateText" class="btn" :disabled="!impChanged">
Step 2: Update Text
</button>
<button
@click="impCheckState"
class="btn"
:disabled="!impTextUpdated"
>
Step 3: Check Logic
</button>
</div>
<div class="status-log">{{ impStatus }}</div>
</div>
</div>
<!-- Declarative (Vue Style) -->
<div class="panel declarative">
<div class="panel-header">
<span class="badge green">声明式 (Declarative)</span>
<span class="sub-text">Vue/React Style - 自动响应</span>
</div>
<div class="code-preview">
<code v-pre>
// 只需要绑定数据
{{ count }}
&lt;div v-if="count > 5"&gt;...&lt;/div&gt;
</code>
</div>
<div class="interactive-area">
<div class="output-box">
Count: <span>{{ decCount }}</span>
<div v-if="decCount > 5" class="warning-msg">⚠️ Count is high!</div>
</div>
<div class="actions">
<button @click="decIncrement" class="btn primary">
Value++ (Auto Render)
</button>
</div>
<div class="status-log">
Framework handles DOM updates automatically.
</div>
</div>
</div>
</div>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>命令式像"手把手教电脑怎么做"声明式像"告诉电脑要什么,它自己搞定"
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
// Imperative State
const impCount = ref(0)
const impShowMsg = ref(false)
const impChanged = ref(false)
const impTextUpdated = ref(false)
const impStatus = ref('Ready.')
const impIncrement = () => {
// Logic layer changes, but DOM doesn't
impStatus.value =
'Variable `count` is now ' + (impCount.value + 1) + '. DOM is NOT updated.'
impCount.value++
impChanged.value = true
impTextUpdated.value = false
}
const impUpdateText = () => {
// Manually update text
impStatus.value = 'DOM text updated manually.'
impChanged.value = false
impTextUpdated.value = true
}
const impCheckState = () => {
// Manually check logic
if (impCount.value > 5) {
impShowMsg.value = true
impStatus.value = 'Logic checked: > 5. Manually showing message.'
} else {
impShowMsg.value = false
impStatus.value = 'Logic checked: <= 5. No message.'
}
}
// Declarative State
const decCount = ref(0)
const decIncrement = () => {
decCount.value++
}
</script>
<style scoped>
.imperative-declarative-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
}
.demo-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.demo-header .icon {
font-size: 1.25rem;
}
.demo-header .title {
font-weight: bold;
font-size: 1rem;
}
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.85rem;
margin-left: 0.5rem;
}
.demo-content {
margin-bottom: 0.5rem;
}
.demo-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
@media (max-width: 640px) {
.demo-grid {
grid-template-columns: 1fr;
}
}
.panel {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.panel-header {
padding: 0.75rem;
border-bottom: 1px solid var(--vp-c-divider);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--vp-c-bg-alt);
}
.badge {
font-size: 0.75rem;
font-weight: bold;
padding: 2px 8px;
border-radius: 4px;
color: white;
}
.badge.yellow {
background: var(--vp-c-warning);
}
.badge.green {
background: var(--vp-c-success);
}
.sub-text {
font-size: 0.75rem;
color: var(--vp-c-text-2);
}
.code-preview {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
font-family: monospace;
font-size: 0.75rem;
color: var(--vp-c-text-1);
height: 70px;
overflow: hidden;
}
.interactive-area {
padding: 0.75rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.output-box {
background: var(--vp-c-bg-soft);
padding: 0.75rem;
border-radius: 6px;
text-align: center;
font-weight: bold;
min-height: 70px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.warning-msg {
color: var(--vp-c-danger);
margin-top: 0.5rem;
font-size: 0.85rem;
}
.actions {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.btn {
padding: 0.5rem;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
background: var(--vp-c-bg);
cursor: pointer;
font-size: 0.75rem;
transition: all 0.2s;
}
.btn:hover:not(:disabled) {
background: var(--vp-c-bg-soft);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn.primary {
background: var(--vp-c-brand);
color: white;
border: none;
}
.btn.primary:hover {
opacity: 0.9;
}
.status-log {
font-size: 0.7rem;
color: var(--vp-c-text-2);
font-style: italic;
min-height: 1.2em;
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
display: flex;
gap: 0.25rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
</style>