在学习现代前端框架时,很多人会问:“为什么 Vue/React 这样写代码?它们到底比原生 JS 强在哪里?”
今天,我们通过一个简单的 Todo 列表应用,对比传统 DOM 操作与 Vue 响应式开发的差异,深入理解“响应式数据驱动界面”这一核心思想。
你将看到:
- 为什么不再需要手动操作 DOM?
ref、v-model、computed是如何工作的?- Vue 如何让你聚焦业务逻辑,而非 DOM 操作?
一、传统方式:手动操作 DOM —— 繁琐且易错
先看一段最原始的 JavaScript 代码实现一个输入框更新标题的功能:
<h2 id="app"></h2>
<input type="text" id="todo-input">
<script>
const app = document.getElementById("app");
const todoInput = document.getElementById("todo-input");
todoInput.addEventListener('change', function(event) {
const todo = event.target.value.trim();
if (!todo) {
console.log('请输入任务');
return;
}
app.innerHTML = todo; // 手动修改 DOM
});
</script>
问题分析
- 必须先找到 DOM 元素:
getElementById是第一步; - 事件监听复杂:需要绑定事件,处理空值等边界情况;
- DOM 操作低效:每次修改都要重新设置
innerHTML,性能差; - 代码耦合严重:UI 与逻辑紧密绑定,难以维护和复用。
这就是“命令式编程”的典型代表:我告诉你怎么做(操作 DOM),而不是你想表达什么(数据变化) 。
二、Vue 方式:响应式驱动 —— 数据是核心
现在我们用 Vue 3 实现同一个功能,但体验完全不同。
完整代码示例
<template>
<div>
<h1></h1>
<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>
<div>
全选 <input type="checkbox" v-model="allDone">
{{ active }} / {{ todos.length }}
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const title = ref('');
const todos = ref([
{ id: 1, title: '打王者', done: false },
{ id: 2, title: '吃饭', done: true },
{ id: 3, title: '睡觉', done: false },
{ id: 4, title: '学习Vue', done: false }
]);
const addTodo = () => {
if (!title.value) return;
todos.value.push({
id: Math.random(),
title: title.value,
done: false
});
title.value = '';
};
const active = computed(() => {
return todos.value.filter(todo => !todo.done).length;
});
const allDone = computed({
get() {
return todos.value.every(todo => todo.done);
},
set(val) {
todos.value.forEach(todo => todo.done = val);
}
});
</script>
<style>
.done {
color: gray;
text-decoration: line-through;
}
</style>
三、关键概念解析:Vue 的强大之处
1. ref:自动追踪数据变化
const title = ref('');
const todos = ref([...]);
ref将普通变量包装为响应式对象;- 当
title.value或todos.value被修改时,Vue 会自动触发视图更新; - 不再需要手动调用
innerHTML或appendChild。
优势:开发者只需关注数据变化,Vue 自动完成 UI 同步。
2. v-model:双向绑定简化交互
<input type="text" v-model="title">
等价于:
input.addEventListener('input', e => {
title.value = e.target.value;
});
但 v-model 更简洁、更安全、更高效。
优势:
- 减少样板代码;
- 避免手动事件绑定出错;
- 支持多种输入类型(checkbox、radio、select 等)。
3. v-for:数据驱动列表渲染
<li v-for="todo in todos" :key="todo.id">
- 根据
todos数组自动生成列表; - 添加或删除项时,自动更新 DOM;
:key提供唯一标识,优化 diff 算法性能。
优势:
- 不再需要手动创建元素;
- 避免内存泄漏和重复节点;
- 性能优于手动循环插入。
4. computed:智能计算属性
const active = computed(() => {
return todos.value.filter(todo => !todo.done).length;
});
- 计算结果是响应式的;
- 只有当依赖的数据变化时才会重新执行;
- 内部缓存机制避免重复计算,提升性能。
优势:
- 代码可读性高;
- 自动更新,无需手动触发;
- 支持
get/set实现复杂逻辑(如全选控制)。
四、Vue 相比原生 JS 的六大核心优势
| 对比维度 | 原生 JS | Vue |
|---|---|---|
| 开发效率 | 需要手动查找节点、绑定事件、操作 DOM | 使用指令(v-model, v-for)快速构建界面 |
| 代码质量 | 易出错,难以维护 | 结构清晰,逻辑分离 |
| 性能表现 | 频繁操作 DOM,性能差 | 虚拟 DOM + 差异算法,高效更新 |
| 开发体验 | 机械式编码,容易疲劳 | 声明式编程,专注业务逻辑 |
| 可扩展性 | 复用困难,组件化成本高 | 组件化设计,易于复用和维护 |
| 调试能力 | 错误定位难,状态不可控 | DevTools 支持,状态可视化 |
五、结语:Vue 的强大,源于对“数据”的尊重
Vue 并没有发明新的语言或语法,它的强大来自于对 “数据即状态” 这一理念的极致实践。
相比原生 JS,Vue 的优势体现在:
- 自动响应:数据变化 → 视图自动更新,无需手动干预;
- 声明式编程:你描述“应该是什么样子”,而不是“怎么一步步做”;
- 状态管理:所有 UI 都由数据驱动,状态可追踪、可测试;
- 生态完善:配合 Vue Router、Pinia、Vite 等工具,形成完整开发体系;
- 学习曲线平缓:API 设计友好,适合新手快速上手,又支持高级模式。
一句话总结:
Vue 让你从“操作像素”中解脱出来,回归到“建模业务”的本质。
当你不再纠结于 querySelector 和 createElement,而是专注于“用户添加了一个任务”、“待办事项数量变了”这些业务行为时,你就已经站在了现代前端开发的正确轨道上。