
在 Vue 应用中,应避免手动操作 DOM 来更新响应式数据渲染;正确方式是通过响应式引用(如 ref)绑定单条动态数据,并利用模板语法自动触发视图更新。
在 vue 应用中,应避免手动操作 dom 来更新响应式数据渲染;正确方式是通过响应式引用(如 `ref`)绑定单条动态数据,并利用模板语法自动触发视图更新。
在构建类似 Blackjack 这类需要频繁切换显示卡片的交互场景时,核心原则是:让 Vue 管理状态与视图同步,而非绕过响应式系统直接修改 HTML。你原先尝试用 document.querySelector().innerHTML = ... 拼接含 {{cards[i].xxx}} 的字符串,本质上是在操作原始 DOM,而 Vue 的虚拟 DOM 会在下一次更新周期中覆盖该修改——导致数据不生效、样式丢失,甚至引发响应式失效。
✅ 正确做法是:将“当前选中卡片”抽象为一个独立的响应式变量(例如 ref()),每次抽卡时仅更新该变量的值,模板中直接绑定它。这样 Vue 能自动追踪依赖并高效重渲染相关节点。
以下是推荐实现(Vue 3 Composition API):
<script setup>
import { ref } from 'vue'
const cards = [
{ id: 1, cardName: 'Ace of Spades', cardPic: '♠', color: 'black', value: 1 },
{ id: 2, cardName: 'Ace of Clubs', cardPic: '♣', color: 'black', value: 1 },
{ id: 3, cardName: 'Ace of Hearts', cardPic: '♥', color: 'red', value: 1 }, // 修正拼写:Heats → Hearts
{ id: 4, cardName: 'Ace of Diamonds', cardPic: '♦', color: 'red', value: 1 }
]
// 响应式引用:存储当前显示的卡片对象
const currentCard = ref(cards[0]) // 初始化为第一张牌
const pickCard = () => {
const randomIndex = Math.floor(Math.random() * cards.length)
currentCard.value = cards[randomIndex]
}
// 页面加载时自动抽一张
pickCard()
</script><template><div class="card">
<div class="cPic" :style="{ color: currentCard.color }">{{ currentCard.cardPic }}</div>
<div class="cName">{{ currentCard.cardName }}</div>
</div>
<button>Draw Card</button>
</template><style scoped>
.card { border: 1px solid #ccc; padding: 12px; border-radius: 8px; margin: 16px 0; }
.cPic { font-size: 2rem; font-weight: bold; }
.cName { margin-top: 8px; font-size: 0.9rem; color: #555; }
button { padding: 8px 16px; background: #42b883; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>
? 关键要点说明:
-
currentCard是一个ref,其.value指向某张具体卡片对象(如cards[2]),模板中通过currentCard.cardPic访问属性,Vue 自动建立响应式连接; -
pickCard()函数只负责更新currentCard.value,无需关心 DOM 操作,Vue 会自动刷新<div class="cPic"> 和 <code><div class="cName"> 的内容与样式;<li>使用 <code>:style缩写替代v-bind:style更简洁;Math.floor(Math.random() * cards.length)是安全获取随机索引的标准写法(避免| 0在负数时异常,虽此处无风险,但更健壮); - 注意修正了原数据中
'Ace of Heats'的拼写错误为'Ace of Hearts',确保语义准确。 - ❌ 不要使用
innerHTML+ 字符串模板插入 Vue 插值语法(如{{cards[i].xxx}})——浏览器不会解析,Vue 也不会接管; - ❌ 不要在
data()或setup()中重复定义cards多次(如在多个组件中硬编码),建议提取为const CARD_DECK = [...]常量或通过provide/inject/Pinia 管理; - ✅ 若需多张手牌(如玩家手牌数组),可改用
ref([])存储cardList,配合v-for渲染,逻辑同样清晰可控。
⚠️ 常见误区提醒:
遵循这一模式,你的 Blackjack 游戏就能实现流畅、可维护、符合 Vue 哲学的卡片动态渲染。











