javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./css/index.css" />
<title>记事本</title>
</head>
<body>
<!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input v-model="todoName" placeholder="请输入任务" class="new-todo" />
<button @click="add" class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item, index) in list" :key="item.id">
<div class="view">
<span class="index">{{ index + 1 }}.</span> <label>{{ item.name }}</label>
<button @click="del(item.id)" class="destroy"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 → 如果没有任务了,底部隐藏掉 → v-show -->
<footer class="footer" v-show="list.length > 0">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{ list.length }} </strong></span>
<!-- 清空 -->
<button @click="clear" class="clear-completed">
清空任务
</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 添加功能
// 1. 通过 v-model 绑定 输入框 → 实时获取表单元素的内容
// 2. 点击按钮,进行新增,往数组最前面加 unshift
const app = new Vue({
el: '#app',
data: {
todoName: '',
list: [
{ id: 1, name: '跑步一公里' },
{ id: 2, name: '跳绳200个' },
{ id: 3, name: '游泳100米' },
]
},
methods: {
del (id) {
// console.log(id) => filter 保留所有不等于该 id 的项
this.list = this.list.filter(item => item.id !== id)
},
add () {
if (this.todoName.trim() === '') {
alert('请输入任务名称')
return
}
this.list.unshift({
id: +new Date(),
name: this.todoName
})
this.todoName = ''
},
clear () {
this.list = []
}
}
})
</script>
</body>
</html>
一、HTML 视图层:页面结构与交互绑定
HTML 部分负责展示页面内容,并通过 Vue 指令与逻辑层的数据和方法绑定,实现 "数据驱动视图"。
1. 主体容器(<section id="app">)
javascript
<section id="app"> ... </section>
- 作用:这是整个 Vue 应用的 "根容器",通过 Vue 实例的
el: '#app'配置,将 Vue 的作用范围限定在这个元素内。 - 所有需要 Vue 控制的标签(如绑定数据、事件)都必须放在这个容器内部。
2. 输入框区域(<header class="header">)
javascript
<header class="header">
<h1>小黑记事本</h1>
<input v-model="todoName" placeholder="请输入任务" class="new-todo" />
<button @click="add" class="add">添加任务</button>
</header>
- 功能:负责接收用户输入的新任务,并触发 "添加" 操作。
- 核心细节:
<h1>:显示应用标题 "小黑记事本"。<input v-model="todoName">:通过v-model实现双向数据绑定 ------ 输入框的内容会实时同步到 Vue 实例的todoName变量中,反之todoName的变化也会实时反映到输入框(后续添加任务后清空输入框就是利用这一点)。<button @click="add">:通过@click(Vue 的事件绑定语法,等同于v-on:click)绑定add方法,点击按钮时会执行add方法(添加新任务)。
3. 任务列表区域(<section class="main">)
javascript
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item, index) in list" :key="item.id">
<div class="view">
<span class="index">{{ index + 1 }}.</span>
<label>{{ item.name }}</label>
<button @click="del(item.id)" class="destroy"></button>
</div>
</li>
</ul>
</section>
- 功能:展示所有任务列表,并支持删除单个任务。
- 核心细节:
v-for="(item, index) in list":Vue 的列表渲染指令,遍历list数组(存储所有任务),每次循环将当前任务对象赋值给item,索引赋值给index。:key="item.id"::key(等同于v-bind:key)是 Vue 优化列表渲染的关键,通过任务的唯一标识item.id(每个任务的id不重复),让 Vue 能精准识别每个列表项,避免更新时的 DOM 复用错误。<span class="index">{``{ index + 1 }}.</span>:通过{``{ }}(插值表达式)显示任务序号,index是数组索引(从 0 开始),index + 1将序号转为从 1 开始。<label>{``{ item.name }}</label>:显示任务名称,item.name是当前任务对象的name属性(如 "跑步一公里")。<button @click="del(item.id)">:删除按钮,点击时调用del方法,并传入当前任务的id(item.id),用于精准删除该任务。
4. 统计与清空区域(<footer class="footer">)
javascript
<footer class="footer" v-show="list.length > 0">
<span class="todo-count">合 计:<strong> {{ list.length }} </strong></span>
<button @click="clear" class="clear-completed">清空任务</button>
</footer>
- 功能:显示当前任务总数,提供 "清空所有任务" 的按钮,且当任务为空时自动隐藏。
- 核心细节:
v-show="list.length > 0":条件渲染指令,当list.length > 0(即有任务)时,footer显示;当list为空时,footer隐藏(通过 CSS 的display: none实现)。{``{ list.length }}:通过插值表达式显示任务总数,list.length是数组的长度(即任务数量)。<button @click="clear">:清空按钮,点击时调用clear方法,删除所有任务。
二、Vue 逻辑层:数据与方法(<script>部分)
Vue 实例通过data定义数据,通过methods定义方法,实现对任务的添加、删除、清空等操作,是应用的 "大脑"。
1. Vue 实例初始化
javascript
const app = new Vue({
el: '#app', // 挂载到id为app的元素
data: { ... }, // 定义数据
methods: { ... } // 定义方法
})
el: '#app':指定 Vue 实例控制的 DOM 元素(即前面的<section id="app">),只有该元素内的 Vue 指令才会被解析。
2. 数据(data)
javascript
data: {
todoName: '', // 绑定输入框的任务名称
list: [ // 存储所有任务的数组
{ id: 1, name: '跑步一公里' },
{ id: 2, name: '跳绳200个' },
{ id: 3, name: '游泳100米' },
]
}
todoName: '':初始为空字符串,通过v-model与输入框绑定,实时存储用户输入的新任务名称。list:数组类型,存储所有任务对象。每个任务对象包含:id:唯一标识(用于删除时精准定位),初始值为 1、2、3(后续新增任务会自动生成唯一id)。name:任务名称(如 "跑步一公里")。
3. 方法(methods)
方法是实现交互逻辑的核心,通过修改data中的数据(如list、todoName),驱动视图自动更新。
(1)del(id):删除单个任务
javascript
del(id) {
this.list = this.list.filter(item => item.id !== id)
}
- 作用:根据传入的
id,从list数组中删除对应的任务。 - 逻辑:
filter是数组方法,用于过滤数组,返回一个 "符合条件的新数组"。item => item.id !== id:保留所有id不等于传入id的任务(即排除要删除的任务)。this.list = ...:将过滤后的新数组赋值给list,Vue 会检测到list的变化,自动更新列表视图(删除对应的li)。
(2)add():添加新任务
javascript
add() {
// 1. 验证输入:如果输入为空(或仅空格),提示并终止
if (this.todoName.trim() === '') {
alert('请输入任务名称')
return
}
// 2. 向list数组开头添加新任务
this.list.unshift({
id: +new Date(), // 生成唯一id(当前时间戳)
name: this.todoName // 任务名称为用户输入的内容
})
// 3. 清空输入框(通过双向绑定同步到页面)
this.todoName = ''
}
- 作用:将用户输入的内容添加到任务列表中。
- 逻辑分步:
- 输入验证:
this.todoName.trim() === ''判断用户是否输入了有效内容(trim()去除前后空格,避免纯空格被当作有效任务),如果无效则弹出提示并通过return终止方法(不执行后续添加逻辑)。 - 添加任务:
unshift是数组方法,向数组开头 添加新元素(新任务会显示在列表最上方)。新任务的id用+new Date()生成(当前时间的时间戳,毫秒级,确保唯一),name为用户输入的this.todoName。 - 清空输入框:
this.todoName = ''将绑定的输入框变量设为空,通过v-model双向绑定,页面输入框会同步清空,方便用户继续输入。
- 输入验证:
(3)clear():清空所有任务
javascript
clear() {
this.list = []
}
- 作用:删除所有任务,清空列表。
- 逻辑:将
list数组赋值为空数组[],Vue 检测到list变化后,会自动清空列表视图;同时list.length变为 0,footer会通过v-show自动隐藏。
三、整体功能流程总结
- 初始加载:页面显示初始任务列表(3 条预设任务),底部显示统计(合计:3)和清空按钮。
- 添加任务 :用户在输入框输入内容(如 "读书 30 分钟"),点击 "添加任务",
add方法验证输入有效后,将新任务添加到list开头,输入框自动清空,列表实时显示新任务,统计数量 + 1。 - 删除任务 :点击某任务后的删除按钮,
del方法根据该任务的id从list中移除它,列表实时移除该任务,统计数量 - 1。 - 清空任务 :点击 "清空任务",
clear方法将list设为空数组,列表清空,底部统计区域因list.length = 0自动隐藏。
扩展:this指向
在你提供的 Vue 代码中,this 是一个非常核心的概念,它的指向和作用直接关系到代码的正常运行。我们结合代码具体分析:
一、this 的指向:当前的 Vue 实例
在 Vue 实例的 methods 方法(如 add、del)中,this 始终指向 当前的 Vue 实例对象 (即代码中 const app = new Vue(...) 创建的那个 app 实例)。
可以简单理解为:this 就是 app 本身。例如在 add 方法中,this 和外部的 app 是同一个对象。
二、this 在代码中的具体作用
this 的核心作用是 让 Vue 实例的方法(如 add、del)能够访问和操作实例内部的数据(data)和其他资源,是连接方法与实例数据的 "桥梁"。
具体看代码中的用法:
1. 访问 data 中的数据
data 中定义的属性(如 todoName、list)会被 Vue 代理到实例上,因此可以通过 this.属性名 直接访问。
-
例 1:
this.todoName在add方法中,this.todoName访问的是data中定义的todoName(与输入框通过v-model绑定的变量),用于获取用户输入的任务名称。 -
例 2:
this.list``this.list访问的是data中定义的list数组(存储所有任务),用于后续添加或删除任务。
2. 修改 data 中的数据
通过 this.属性名 = 新值 可以修改 data 中的数据,而 Vue 会自动监听数据变化,同步更新页面视图(这是 Vue "数据驱动视图" 的核心特性)。
-
例 1:
this.list.unshift(...)``unshift是数组的方法,this.list.unshift(...)向list数组的开头添加新任务对象,修改后 Vue 会自动重新渲染列表,展示新任务。 -
例 2:
this.todoName = ''在添加任务后,通过this.todoName = ''清空data中的todoName,由于输入框与todoName双向绑定(v-model),页面上的输入框会同步被清空。 -
例 3:
this.list = this.list.filter(...)在del方法中,通过filter生成新数组并赋值给this.list,修改后 Vue 会自动更新列表,移除被删除的任务。
3. 调用实例中的其他方法(如果有)
如果实例中定义了多个方法,this 也可以用来调用其他方法。例如:
javascript
methods: {
add() {
// 调用另一个方法
this.validateInput();
},
validateInput() {
// 验证输入的逻辑
}
}
三、关键注意点:为什么 this 能正确指向实例?
Vue 在初始化时,会对 methods 中的方法进行 "绑定处理",确保方法内部的 this 固定指向当前 Vue 实例,而不会因调用方式改变(例如避免在事件回调中 this 指向 window 或 DOM 元素)。
反例:如果用箭头函数定义方法(如 add: () => {}),this 会指向外部作用域(通常是 window),导致无法访问 data 中的属性(会报错 this.todoName is undefined)。因此,methods 中的方法必须用普通函数定义。
感谢大家的阅读 制作不易 麻烦各位点点关注和收藏 每天8点持续更新!!!