Live Demo 展示
在笔记里直接嵌 Playground 的好处:边读边改。讲响应式时,光看代码不如让你点一下按钮看它重算几次。
1. 响应式基础:ref 与事件绑定
最小可跑的响应式示例,点按钮看 count 如何在模板里自动更新:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div class="box">
<h3>点一下按钮观察计数</h3>
<button @click="count++">当前 {{ count }}</button>
</div>
</template>
<style scoped>
.box {
padding: 24px;
text-align: center;
font-family: system-ui;
}
button {
padding: 8px 20px;
border: 1px solid #8b5cf6;
border-radius: 6px;
background: #8b5cf6;
color: white;
cursor: pointer;
}
</style>
改代码看看:把 ref 换成普通变量,计数会失效。
2. computed 的缓存
讲响应式缓存时最直观的演示:同样的表达式,computed 只在依赖变化时重算,method 每次渲染都跑。
改改输入框,对比两行的执行次数:
<script setup>
import { ref, computed } from 'vue'
const text = ref('hello')
const other = ref(0)
let computedRuns = 0
let methodRuns = 0
const upper = computed(() => {
computedRuns++
return text.value.toUpperCase()
})
function upperMethod() {
methodRuns++
return text.value.toUpperCase()
}
</script>
<template>
<div class="box">
<label>
输入框:
<input v-model="text" />
</label>
<p>computed: {{ upper }} (实际执行 {{ computedRuns }} 次)</p>
<p>method: {{ upperMethod() }} (实际执行 {{ methodRuns }} 次)</p>
<p>其他状态:{{ other }}</p>
<button @click="other++">改动无关状态,观察 method 被重跑</button>
</div>
</template>
<style scoped>
.box { padding: 20px; font-family: system-ui; }
input { padding: 4px 8px; }
button {
margin-top: 8px;
padding: 6px 14px;
border: 1px solid #8b5cf6;
border-radius: 4px;
background: white;
cursor: pointer;
}
</style>
Java 类比:computed 像带 @Cacheable 的方法,入参不变直接返回缓存;method 像没缓存的普通方法,调用一次执行一次。
3. v-for 的 key 影响状态复用
这是 key 值踩坑的经典现场:在每个输入框里打点字,然后点「插入到头部」,观察状态错位。
<script setup>
import { ref } from 'vue'
const list = ref([
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 3, name: 'C' },
])
function insertTop() {
list.value.unshift({ id: Date.now(), name: '新' })
}
</script>
<template>
<div class="box">
<p>在每个输入框里打几个字,再点「插入到头部」,左边会错位</p>
<button @click="insertTop">插入到头部</button>
<div class="grid">
<div>
<h4>key=index(错误)</h4>
<div v-for="(it, i) in list" :key="i" class="row">
<span>{{ it.name }}</span>
<input placeholder="打点字" />
</div>
</div>
<div>
<h4>key=id(正确)</h4>
<div v-for="it in list" :key="it.id" class="row">
<span>{{ it.name }}</span>
<input placeholder="打点字" />
</div>
</div>
</div>
</div>
</template>
<style scoped>
.box { padding: 20px; font-family: system-ui; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 12px; }
.row { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
input { padding: 2px 6px; flex: 1; }
button { padding: 6px 14px; border: 1px solid #8b5cf6; border-radius: 4px; background: white; cursor: pointer; }
</style>
结论:v-for 的 key 必须能稳定标识一个项,index 不是稳定标识——列表顺序一变,key 就和 DOM 节点错位,已输入的内容会跟错条目。
语法说明
只要把代码块语言写成 vue live 就会变成沙箱:
```vue live height=400px
<script setup>
// 你的代码
</script>
```
参数:
height=400px:沙箱高度(CSS 单位任意)view=editor:默认展示代码栏而不是预览(默认preview)
普通
vue代码块不会变沙箱,只是普通高亮 + 右上角多一个「在 Playground 打开」按钮,点一下也能送到独立页里玩。
延伸阅读
- Vue SFC Playground — 官方版本
- @vue/repl GitHub — 本站 Playground 用的底层组件