快速使用Vue2实现记事本
2023-08-08 本文已影响0人
h2coder
![](https://img.haomeiwen.com/i1641428/237eacc9a78c0190.png)
项目地址
需求分析
布局分析
- 页面分为3个部分,头部、身体、尾部
- 头部:有输入框、添加任务按钮
- 身体:任务列表
- 尾部:总计任务数量、清空任务按钮
- 最后还需要一个根组件,包裹着3个部分
需求拆解
- 输入框输入事项内容,点击添加任务按钮,或回车,即可添加任务到任务列表。添加完毕后,清空输入框内容
- 渲染任务列表,点击X按钮,删除该任务
- 点击清空按钮,删除所有任务
- 任务数据,改变时,持久化到本地存储,打开页面时读取本地存储,回显数据
组件拆分
从上面的分析得知,组件要分拆为4个
-
TodoHeader.vue,头部组件
-
TodoMai.vue,身体组件
-
TodoFooter.vue,尾部组件
-
App.vue,根组件
-
组件职责划分
- App
- 持有任务列表数组,并通过
props
进行父子组件传参,子组件的UI操作行为触发时,通过自定义事件,通知父组件操作数据,遵循单向数据流的原则(数据在谁哪,谁来维护) - 监听任务数据变化,实时同步给
localStorage
本地存储
- 持有任务列表数组,并通过
- TodoHeader
- 自身维护一个
todoName
变量,与输入框进行v-model
双向绑定 - 监听按钮点击事件、输入框回车事件,事件触发时,判断变量是否不为空,不为空时,则发出
add
自定义事件,通知父组件添加任务
- 自身维护一个
- TodoMain
- 接收根组件传过来的
list
任务列表数组,通过v-for
渲染到页面上 - 给列表项目上的
X按钮
,绑定点击事件,事件触发时,通过delete
自定义事件,并且带上任务id
,通知父组件删除任务列表中对应的数据
- 接收根组件传过来的
- TodoFooter
- 接收根组件传过来的
list
任务列表数组,通过插值表达式,渲染任务个数 - 给清空任务按钮,绑定点击事件,事件触发时,通过
clear
自定义事件,通知父组件,删除所有任务数据
- 接收根组件传过来的
- App
代码时间
根组件(App.vue)
<template>
<section id="app">
<!-- 头部 -->
<TodoHeader @add="onAdd"></TodoHeader>
<!-- 身体 -->
<TodoMain :list="list" @delete="onDelete"></TodoMain>
<!-- 尾部 -->
<TodoFooter :list="list" @clear="onClear"></TodoFooter>
</section>
</template>
<script>
import TodoHeader from "./components/TodoHeader.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoFooter from "./components/TodoFooter.vue";
// 本地存储的Key
const dataKey = "xh-todos";
// 默认数据
const defaultList = [
{ id: 1, name: "打篮球" },
{ id: 2, name: "看电影" },
{ id: 3, name: "逛街" },
];
export default {
data() {
return {
// 优先从本地存储中,获取数据
list: JSON.parse(localStorage.getItem(dataKey)) || defaultList,
};
},
methods: {
// 处理添加
onAdd(todoName) {
this.list.unshift({
id: Date.now(),
name: todoName,
});
},
// 处理删除
onDelete(id) {
const index = this.list.findIndex((item) => item.id === id);
this.list.splice(index, 1);
},
// 清空
onClear() {
this.list = [];
},
},
watch: {
list: {
// 深度监听
deep: true,
handler(newValue) {
// 同步数据到本地存储
localStorage.setItem(dataKey, JSON.stringify(newValue));
},
},
},
components: {
TodoHeader,
TodoMain,
TodoFooter,
},
};
</script>
<style>
</style>
头部组件(TodoHeader.vue)
<template>
<!-- 输入框 -->
<header class="header">
<h1>记事本</h1>
<input
@keyup.enter="add"
v-model.trim="todoName"
placeholder="请输入任务"
class="new-todo"
/>
<button @click="add" class="add">添加任务</button>
</header>
</template>
<script>
export default {
data() {
return {
// 输入框的文本
todoName: "",
};
},
methods: {
// 添加
add() {
if (this.todoName === "") {
return;
}
// 发送事件,让父组件,添加数据到数组中
this.$emit("add", this.todoName);
// 清空输入框
this.todoName = "";
},
},
};
</script>
<style>
</style>
尾部(TodoFooter.vue)
<template>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count"
>合 计:<strong> {{ list.length }} </strong></span
>
<!-- 清空 -->
<button @click="$emit('clear')" class="clear-completed">清空任务</button>
</footer>
</template>
<script>
export default {
props: ["list"],
};
</script>
<style>
</style>