列表展示功能
(1)在App.vue中提供数据
<script setup> import TodoHeader from './components/TodoHeader.vue' import TodoMain from './components/TodoMain.vue' import TodoFooter from './components/TodoFooter.vue' // 提供数据 const list = ref([ { id: 1, name: '吃饭', done: true, }, { id: 2, name: '睡觉', done: false, }, { id: 3, name: '打豆豆', done: false, }, ]) </script>
(2)传递给Main组件
<TodoMain :list="list"></TodoMain>
(3)子组件接收
<script setup> defineProps({ list: { type: Array, default: () => [], }, }) </script>
(4)子组件渲染
<ul class="todo-list"> <li :class="{ completed: item.done }" v-for="item in list" :key="item.id"> <div class="view"> <input class="toggle" type="checkbox" :checked="item.done" /> <label>{{ item.name }}</label> <button class="destroy"></button> </div> <input class="edit" value="Create a TodoMVC template" /> </li> </ul>
修改任务状态功能
(1)子组件注册事件
<input class="toggle" type="checkbox" :checked="item.done" @change="changeFn(item.id)" />
(2)子传父
<script setup> const emit = defineEmits(['changeDone']) const changeFn = (id) => { emit('changeDone', id) } </script>
(3)父组件
const changeDone = (id) => { const todo = list.value.find((item) => item.id === id) todo.done = !todo.done } <TodoMain :list="list" @changeDone="changeDone"></TodoMain>
删除功能
(1)子组件
const emit = defineEmits(['changeDone', 'delTodo']) <button class="destroy" @click="emit('delTodo', item.id)"></button>
(2)父组件
const delTodo = (id) => { list.value = list.value.filter((item) => item.id !== id) } <TodoMain :list="list" @changeDone="changeDone" @delTodo="delTodo" ></TodoMain>
添加功能
(1)子组件
<script setup> import { ref } from 'vue' const todoName = ref('') const emit = defineEmits(['addTodo']) const add = (e) => { if ( todoName.value) { emit('addTodo', todoName.value) todoName.value = '' } } </script> <template> <header class="header"> <h1>todos</h1> <input class="new-todo" placeholder="What needs to be done?" autofocus v-model="todoName" @keydown.enter="add" /> </header> </template> <style lang="less" scoped></style>
(2)父组件
const addTodo = (name) => { list.value.unshift({ id: Date.now(), name, done: false, }) } <TodoHeader @addTodo="addTodo"></TodoHeader>
底部功能 (计算属性)
(1)子组件
<script setup> import { computed } from 'vue' const props = defineProps({ list: { type: Array, default: () => [], }, }) const leftCount = computed(() => { return props.list.filter((item) => !item.done).length }) </script> <template> <footer class="footer"> <span class="todo-count"> <strong>{{ leftCount }}</strong> item left </span> <ul class="filters"> <li> <a class="selected" href="#/">All</a> </li> <li> <a href="#/active">Active</a> </li> <li> <a href="#/completed">Completed</a> </li> </ul> <button class="clear-completed">Clear completed</button> </footer> </template> <style lang="less" scoped></style>
全选反选功能
(1)提供计算属性
const isCheckAll = computed(() => { return props.list.every((item) => item.done) })
(2)注册事件
<input id="toggle-all" class="toggle-all" type="checkbox" :checked="isCheckAll" @change="emit('checkAll', !isCheckAll)" />
(3)父组件全选或者反选
const checkAll = (value) => { list.value.forEach((item) => (item.done = value)) } <TodoMain :list="list" @changeDone="changeDone" @delTodo="delTodo" @checkAll="checkAll" ></TodoMain>
watch 监视存到本地
watch( list, (value) => { localStorage.setItem('todos', JSON.stringify(value)) }, { deep: true, } )