《Vue3 从入门到大神04篇》【实战】我的第一个 Vue3 应用 —— TodoList 快速上手
·
一、目标效果与功能拆解
我们要实现的 TodoList 功能包括:
-
✅ 输入待办事项
-
✅ 点击按钮添加到列表
-
✅ 显示待办数量
-
✅ 点击删除某一项
最终效果示意
[ 输入待办 ] [ 添加 ]
-----------------------
✅ 学习 Vue3
❌ 写博客
✅ 运动
-----------------------
共 3 条待办
二、组件拆分设计(很重要)
很多新手会犯一个错误:
👉 所有逻辑都写在 App.vue
我们先做一次正确的组件拆分:
src/
├── App.vue # 页面组装
└── components/
├── TodoInput.vue # 输入 + 添加按钮
├── TodoList.vue # 列表展示
└── TodoItem.vue # 单个待办项
📌 拆分原则:
-
一个组件只负责一类 UI 和行为
-
父组件管数据,子组件管展示和事件
三、App.vue:数据中枢
1️⃣ 使用 ref和 reactive
<script setup lang="ts">
import { ref, reactive } from 'vue'
import TodoInput from './components/TodoInput.vue'
import TodoList from './components/TodoList.vue'
// 输入框内容
const inputValue = ref('')
// 待办列表
const todos = reactive([
{ id: 1, text: '学习 Vue3', done: false },
{ id: 2, text: '写博客', done: true }
])
// 添加待办
const addTodo = () => {
if (!inputValue.value.trim()) return
todos.push({
id: Date.now(),
text: inputValue.value,
done: false
})
inputValue.value = ''
}
// 删除待办
const removeTodo = (id: number) => {
const index = todos.findIndex(t => t.id === id)
if (index > -1) todos.splice(index, 1)
}
</script>
2️⃣ 模板部分
<template>
<div class="app">
<h1>Vue3 TodoList</h1>
<TodoInput
v-model:value="inputValue"
@add="addTodo"
/>
<TodoList
:todos="todos"
@remove="removeTodo"
/>
<p>共 {{ todos.length }} 条待办</p>
</div>
</template>
四、TodoInput.vue:输入组件
<script setup lang="ts">
defineProps<{
value: string
}>()
const emit = defineEmits<{
(e: 'update:value', value: string): void
(e: 'add'): void
}>()
</script>
<template>
<div class="todo-input">
<input
:value="value"
@input="e => $emit('update:value', (e.target as HTMLInputElement).value)"
placeholder="请输入待办事项"
/>
<button @click="$emit('add')">添加</button>
</div>
</template>
📌 重点:
-
使用
v-model的拆解写法 -
子组件不直接改 props
五、TodoList.vue:列表容器
<script setup lang="ts">
defineProps<{
todos: Array<{
id: number
text: string
done: boolean
}>
}>()
const emit = defineEmits<{
(e: 'remove', id: number): void
}>()
</script>
<template>
<ul class="todo-list">
<li v-for="todo in todos" :key="todo.id">
<TodoItem
:todo="todo"
@remove="emit('remove', todo.id)"
/>
</li>
</ul>
</template>
六、TodoItem.vue:单条待办
<script setup lang="ts">
defineProps<{
todo: {
id: number
text: string
done: boolean
}
}>()
const emit = defineEmits<{
(e: 'remove'): void
}>()
</script>
<template>
<div class="todo-item">
<span :class="{ done: todo.done }">
{{ todo.text }}
</span>
<button @click="$emit('remove')">删除</button>
</div>
</template>
<style scoped>
.done {
text-decoration: line-through;
color: gray;
}
</style>
七、模板语法快速复习表
|
语法 |
作用 |
|---|---|
|
|
插值表达式 |
|
|
双向绑定 |
|
|
属性绑定 |
|
|
事件绑定 |
|
|
循环渲染 |
|
|
条件渲染 |
八、ref 与 reactive 实战小结
|
API |
适用场景 |
|---|---|
|
|
基本类型、DOM 引用 |
|
|
对象、数组 |
|
|
|
📌 记住一句话:
基本类型用 ref,复杂对象用 reactive。
📢 下期预告
第 05 篇:Vue3 响应式原理初探 —— 从 Object.defineProperty 到 Proxy
更多推荐
所有评论(0)