锦书在线
80.52M · 2026-03-21
假设我们手头有一份传统的 demo.html 文件(基于原生 JavaScript 或 jQuery 实现)。在这类文件中,实现一个待办事项列表通常意味着:
document.getElementById('input'), querySelectorAll('li')。addEventListener('click', ...),addEventListener('keydown', ...)。createElement、appendChild;完成任务时 classList.toggle;统计数量时遍历 DOM 节点计数。这种“命令式”编程让开发者陷入了细节的泥潭:代码耦合严重、维护困难、性能隐患大。
当我们转向你提供的这段 Vue 3 <script setup> 代码时,会发现一种截然不同的优雅。让我们逐行拆解,看看 Vue 是如何通过响应式系统、声明式渲染和计算属性来解决传统痛点的。
ref 与数据焦点import {ref, computed} from 'vue'
// 响应式数据
const title = ref();
const todos = ref([
{ id:1, title:'吃鸡', done:true },
{ id:2, title:'睡觉', done:true }
]);
ref() 将普通变量包裹成响应式引用。
title 和 todos 不再是普通变量,而是带有“魔法”的数据容器。title.value 是什么,todos.value 里有什么,完全不需要知道页面上有几个 <li> 标签。<script> 中通过 .value 访问真实数据(如 title.value),而在 <template> 中 Vue 会自动解包,直接使用 {{ title }}。<h2>{{ title }}</h2>
<input type="text" v-model="title" @keydown.enter="addTodo">
<ul v-if="todos.length">
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{done: todo.done}">{{ todo.title }}</span>
</li>
</ul>
<div v-else>
暂无计划
</div>
这段模板代码展示了 Vue 三大指令的精妙配合,彻底摒弃了手动操作 DOM:
v-modelv-model="title" 和 v-model="todo.done"title 变量绑定。用户打字,title 自动变;代码修改 title,输入框自动变。todo.done 绑定。input 事件更新变量,变量变化更新 input 值,代码量翻倍且容易出错。Vue 一行搞定。@keydown.enter@keydown.enter="addTodo"@ 是 v-on: 的缩写,用于事件。.enter 是事件修饰符,意为“只在按下回车键时触发”。if (event.key === 'Enter') 判断逻辑,语义清晰,代码极简。注释中提到“不用 addEventListener”,正是指这种声明式绑定的便捷性。v-if / v-for / :keyv-if="todos.length" 和 v-for="todo in todos" :key="todo.id"v-if 和 v-else 实现了“有数据显示列表,无数据显示提示”的逻辑切换,无需手动 display: none。v-for 根据 todos 数组自动生成 <li>。:key="todo.id" 是 Vue 优化渲染的关键。它给每个节点发了“身分证”,当数组顺序变化或删除项时,Vue 能精准复用 DOM 节点,而不是暴力销毁重建,极大提升性能。:class:class="{done: todo.done}": 是 v-bind: 的缩写。todo.done 为 true 时,应用 done 类(灰色删除线);为 false 时,不应用。element.classList.add('done'),只需改变数据 todo.done = true,样式自动生效。computed 计算属性代码中两处使用了 computed,这是区分新手与高手的关键。
// 依赖于 todos 响应式数据的计算属性
const active = computed(() => {
return todos.value.filter(todo => !todo.done).length
})
{{ active }} / {{ todos.length }}todos 数组,active 不会重新执行 filter,直接返回缓存结果。{{ todos.filter(...).length }},每次组件更新(哪怕无关)都会重新遍历数组,浪费性能。const allDone = computed({
get() {
return todos.value.every(todo => todo.done)
},
set(val) {
todos.value.forEach(todo => todo.done = val)
}
})
<input type="checkbox" v-model="allDone">computed 的读写模式(Getter/Setter)。
every)。如果是,全选框自动勾选。set,将所有任务的 done 状态设为 val。v-model 同时实现了“状态同步”和“批量修改”。传统 JS 需要分别编写“检查所有状态更新全选框”和“全选框更新所有状态”两段逻辑,极易出现不同步 Bug。Vue 将其收敛为一个计算属性,逻辑严密且优雅。addTodo 函数const addTodo = () => {
if(!title.value) return; // 数据校验
todos.value.push({
id: Date.now(), // 使用时间戳生成唯一 ID,比 Math.random() 更可靠
title: title.value,
done: false
})
// 注意:这里没有操作 DOM!
// 只要 push 进数组,Vue 会自动在页面上添加一个新的 <li>
}
document 相关代码。Date.now() 生成唯一 ID,配合 :key 确保列表渲染稳定。push 操作触发 Vue 的响应式系统,视图自动更新。通过这段代码,我们可以清晰地看到 Vue 带来的思维转变:
| 维度 | 传统 DOM 操作 (demo.html) | Vue 数据驱动 (当前代码) |
|---|---|---|
| 关注点 | How:怎么找到元素?怎么添加类名?怎么事件? | What:数据是什么?状态是什么? |
| 状态同步 | 手动双向同步,易出错 | 自动双向绑定 (v-model) |
| 列表渲染 | 手动循环创建/删除节点 | 声明式循环 (v-for),自动 Diff |
| 复杂逻辑 | 分散在事件回调中,难以维护 | 封装在 computed 中,自动缓存 |
| 代码量 | 多且冗余 | 少而精悍 |
| 可维护性 | 低,牵一发而动全身 | 高,逻辑与视图分离 |
v-if, v-for),而不是告诉它一步步怎么做。computed 利用缓存。<script setup> 让相关逻辑(如 todos, active, addTodo)聚集在一起,代码组织更符合人类思维。这段看似简单的 Todo List 代码,实则是现代前端开发哲学的缩影。它展示了 Vue 如何通过响应式系统将开发者从繁琐的 DOM 操作中解放出来,让我们能专注于业务逻辑本身。
从 demo.html 的“手动挡”到 Vue 的“自动挡”,不仅仅是语法的升级,更是开发效率与代码质量质的飞跃。当你习惯了“修改数据即修改视图”的思维模式后,你会发现,构建复杂的交互应用变得前所未有的简单、高效且充满乐趣。
这,就是 Vue 赋予我们的超能力。