vue计算属性
好的,选择探索计算属性(computed
)是非常棒的一步!它能让你的代码变得更清晰、更高效,是优化 Vue 应用的常用技巧。
我们的飞船将驶向一个能让数据“自动计算”的星球。
第七站:计算属性 (computed
) - 让数据更“聪明”
我们的目标:
在我们的待办事项列表中,我们希望实时显示一个摘要信息,比如“还剩 3 项未完成”。
你可能会想:“我可以直接在模板里写一段 JavaScript 代码来计算,或者定义一个方法来做这件事,为什么还需要 computed
?”
computed
的两大核心优势:
- 性能与缓存 (Performance & Caching):计算属性是基于它们的响应式依赖进行缓存的。只有在它的相关依赖发生改变时才会重新求值。这意味着,只要
todos
列表不发生变化,无论你多少次访问这个计算属性,它都会立即返回最后的计算结果,而不会重复执行计算函数。 - 可读性与声明性 (Readability & Declarative Style):它能让你像声明普通数据一样,声明一个派生自其他数据的新数据。这让你的代码意图更清晰,模板也更简洁。
步骤 1:升级 TodoItem.vue
- 添加“完成”状态
为了计算“未完成”的数量,我们首先需要让每个待办事项拥有一个“已完成”的状态。
- 打开
src/components/TodoItem.vue
文件。 - 我们将添加一个复选框,并让它在被点击时通知父组件。用下面的代码完整替换
TodoItem.vue
的内容:
<script setup lang="ts">
// 更新 Props,现在 todo 对象包含一个 `completed` 属性
defineProps<{todo: {id: number;text: string;completed: boolean; // 新增}
}>()// 新增一个 `toggle-complete` 事件
const emit = defineEmits(['remove', 'toggle-complete'])
</script><template><li :class="{ completed: todo.completed }"><input type="checkbox":checked="todo.completed"@change="emit('toggle-complete')"><span>{{ todo.text }}</span><button @click="emit('remove')">删除</button></li>
</template><style scoped>
li {display: flex;justify-content: space-between;align-items: center;padding: 8px 0;border-bottom: 1px solid #eee;
}/* 新增样式:当 li 元素有 'completed' class 时,应用删除线样式 */
li.completed span {text-decoration: line-through;color: #ccc;
}button {background-color: #ff4d4d;/* ... 其他样式保持不变 ... */color: white;border: none;padding: 5px 10px;border-radius: 4px;cursor: pointer;
}
</style>
步骤 2:在 App.vue
中引入 computed
现在,回到我们的主应用 App.vue
,我们将在这里实现计算属性。
- 打开
src/App.vue
文件。 - 用下面的代码完整替换
App.vue
的内容,注意新增和修改的部分。
<script setup lang="ts">
// 1. 从 vue 中再多导入一个 `computed`
import { ref, watch, computed } from 'vue'
import TodoItem from './components/TodoItem.vue'const newTodoText = ref('')// 2. 更新数据结构,为每个 todo 项增加 `completed` 属性
const todos = ref(JSON.parse(localStorage.getItem('todos') || '[]'))watch(todos, (newTodos) => {localStorage.setItem('todos', JSON.stringify(newTodos))
}, { deep: true })// 3. ✨ 这是我们的计算属性 ✨
// 它会根据 `todos` 列表自动计算出未完成项的数量
const incompleteCount = computed(() => {// .filter 会返回一个新数组,这里是所有 completed 为 false 的项return todos.value.filter(todo => !todo.completed).length
})function removeTodo(idToRemove: number) {todos.value = todos.value.filter(todo => todo.id !== idToRemove)
}function addTodo() {const trimmedText = newTodoText.value.trim()if (trimmedText === '') returnconst newTodo = {id: Date.now(),text: trimmedText,completed: false // 新增的 todo 默认是未完成状态}todos.value.unshift(newTodo)newTodoText.value = ''
}// 4. 新增一个方法,用于切换 todo 的完成状态
function toggleTodoComplete(idToToggle: number) {// 找到我们要修改的那个 todoconst todo = todos.value.find(todo => todo.id === idToToggle)if (todo) {// 直接修改它的 completed 属性todo.completed = !todo.completed}
}
</script><template><main><h1>我的待办事项 (还剩 {{ incompleteCount }} 项未完成)</h1><form @submit.prevent="addTodo"><input v-model="newTodoText"placeholder="接下来要做什么?"><button type="submit">添加</button></form><ul><TodoItem v-for="todo in todos" :key="todo.id":todo="todo"@remove="removeTodo(todo.id)"@toggle-complete="toggleTodoComplete(todo.id)"/></ul></main>
</template><style scoped>
/* 样式部分也无需改动,之前的 scoped 样式会自动应用 */
/* ... */
</style>
代码解读:
- 数据结构更新:我们给每个待办事项对象都增加了一个
completed: boolean
属性。 toggleTodoComplete
方法:我们新增了这个方法来处理来自子组件的toggle-complete
事件,它会找到对应的待办事项并反转其completed
状态。incompleteCount = computed(() => { ... })
:这就是计算属性的魔力所在。- 我们将一个“计算函数”传给了
computed
。 - 在这个函数里,我们对
todos
数组进行了筛选(filter
),只保留completed
为false
的项,然后返回它们的数量(.length
)。 - 神奇之处:Vue 会自动追踪这个函数内部依赖了哪些响应式数据(在这里是
todos.value
)。只有当todos
数组发生变化时,Vue 才会重新运行这个函数来计算新的incompleteCount
。如果todos
没变,incompleteCount
就会直接返回上一次缓存的结果,性能极高。
- 我们将一个“计算函数”传给了
<h1>...{{ incompleteCount }}...</h1>
:在模板中,我们可以像使用ref
一样直接使用incompleteCount
。它看起来就像一个普通的数据,但背后却是响应式和缓存驱动的。
保存所有文件! 回到浏览器,您会看到:
- 标题栏实时显示着未完成任务的数量。
- 每个待办事项前面都有一个复选框。
- 当您点击复选框,任务项会划上删除线,并且顶部的未完成数量会自动减一!
- 当您取消勾选,数量又会自动加一。
这一切都是由计算属性自动完成的。
本站小结与展望
您已成功掌握了 computed
,学会了如何从现有状态中派生出新的、带缓存的响应式数据,让您的应用逻辑更清晰,性能更出色。
至此,您已经学习了 Vue 3 响应式系统的三大支柱:ref
/ reactive
, watch
和 computed
。您的开发技能又提升了一个重要的台阶!
下一站预告:
- 组件生命周期 (Lifecycle Hooks):深入了解组件从创建到销毁的全过程,并在关键时刻“挂上”我们自己的逻辑。
- 路由 (
Vue Router
):让我们的单页面应用拥有多个“页面”,并实现页面间的导航。 - 状态管理 (
Pinia
):当应用变得更复杂时,学习如何用一个中央“仓库”来管理所有组件共享的状态。
您想往哪个方向继续探索呢?