<style>
.reactive-demo { font-family: system-ui; }
.state-display { background: #1e293b; padding: 16px; border-radius: 8px; margin: 12px 0; }
.state-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #334155; }
.state-item:last-child { border-bottom: none; }
.label { color: #94a3b8; }
.value { color: #22c55e; font-weight: bold; }
.computed { color: #3b82f6; }
.btn-group { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.2s; }
.btn-primary { background: #3b82f6; color: white; }
.btn-primary:hover { background: #2563eb; }
.btn-danger { background: #ef4444; color: white; }
.btn-danger:hover { background: #dc2626; }
.log { background: #0f172a; padding: 12px; border-radius: 6px; margin-top: 12px; font-family: monospace; font-size: 12px; max-height: 120px; overflow-y: auto; }
.log-item { color: #94a3b8; padding: 2px 0; }
.log-item.new { color: #fbbf24; }
</style>
<div class="reactive-demo">
<div class="state-display">
<div class="state-item">
<span class="label">count (ref)</span>
<span class="value" id="countValue">0</span>
</div>
<div class="state-item">
<span class="label">double (computed)</span>
<span class="computed" id="doubleValue">0</span>
</div>
<div class="state-item">
<span class="label">user.name (reactive)</span>
<span class="value" id="userName">Vue</span>
</div>
</div>
<div class="btn-group">
<button class="btn btn-primary" onclick="increment()">count++</button>
<button class="btn btn-primary" onclick="decrement()">count--</button>
<button class="btn btn-danger" onclick="changeName()">修改 user.name</button>
</div>
<div class="log" id="logPanel">
<div class="log-item">💡 点击按钮体验响应式更新...</div>
</div>
</div>
<script>
// 模拟 Vue 的 Proxy 响应式
const logs = [];
function log(msg) {
logs.unshift(msg);
if (logs.length > 8) logs.pop();
document.getElementById('logPanel').innerHTML =
logs.map((l, i) => '<div class="log-item' + (i === 0 ? ' new' : '') + '">' + l + '</div>').join('');
}
// 模拟 ref
let count = 0;
function updateCount(val) {
const old = count;
count = val;
document.getElementById('countValue').textContent = count;
document.getElementById('doubleValue').textContent = count * 2;
log('watch: count ' + old + ' → ' + count);
}
// 模拟 reactive
const user = new Proxy({ name: 'Vue' }, {
set(target, key, value) {
const old = target[key];
target[key] = value;
document.getElementById('userName').textContent = value;
log('Proxy set: user.' + key + ' = "' + value + '"');
return true;
}
});
function increment() { updateCount(count + 1); }
function decrement() { updateCount(count - 1); }
const names = ['Vue', 'React', 'Angular', 'Svelte', 'Solid'];
let nameIndex = 0;
function changeName() {
nameIndex = (nameIndex + 1) % names.length;
user.name = names[nameIndex];
}
</script>