Npcink Blog
切换菜单

新手常见的 todo 列表渲染详细拆解 - Vue3 新手基础教程

本页目录8 节

代码

<script src="https://unpkg.com/vue@next"></script>
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">添加 todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="例如:明天早上跑步"
/>
<button>添加</button>
</form>
<ul>
<todo-item
v-for="(todo, index) in todos"
:key="todo.id"
:title="todo.title"
@remove="todos.splice(index, 1)"
></todo-item>
</ul>
</div>
<script>
const app = Vue.createApp({
data() {
return {
newTodoText: '',
todos: [
{
id: 1,
title: '看电影'
},
{
id: 2,
title: '吃饭'
},
{
id: 3,
title: '上 RUNOOB 学习'
}
],
nextTodoId: 4
}
},
methods: {
addNewTodo() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
app.component('todo-item', {
template: `
<li>
{{ title }}
<button @click="$emit('remove')">删除</button>
</li>
`,
props: ['title'],
emits: ['remove']
})
app.mount('#todo-list-example')
</script>

浏览器运行实例

原网页中的可执行演示已停用,以下完整保留为静态 HTML 代码。

<script src="https://unpkg.com/vue@next"></script>
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">添加 todo</label>
<input v-model="newTodoText" id="new-todo" placeholder="例如:明天早上跑步">
<button>添加</button>
</form>
<ul>
<todo-item v-for="(todo, index) in todos" :key="todo.id" :title="todo.title" @remove="todos.splice(index, 1)"></todo-item>
</ul>
</div>
<script>
const app = Vue.createApp({
data() {
return {
newTodoText: '',
todos: [
{
id: 1,
title: '看电影'
},
{
id: 2,
title: '吃饭'
},
{
id: 3,
title: '上 RUNOOB 学习'
}
],
nextTodoId: 4
}
},
methods: {
addNewTodo() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
app.component('todo-item', {
template: `
<li>
{{ title }}
<button @click="$emit('remove')">删除</button>
</li>
`,
props: ['title'],
emits: ['remove']
})
app.mount('#todo-list-example')
</script>

第一阶段:form 表单

<form v-on:submit.prevent="addNewTodo">
</form>

<form> 标签用于创建供用户输入的 HTML 表单。通常包含一个或多个如下表单元素,如

其中的v-on:submit.prevent="addNewTodo",绑定了一个 addNewTodo 事件,其中的submit.prevent指在该表单中的任何提交按钮都可以触发该事件(比如button按钮)。

label

<label for="new-todo">添加 todo</label>

<label> 标签为 input 元素定义标注(标记)。其中的for="new-todo"属性,表示与下面的<input id="new-todo">元素绑定。

当您点击<label>元素时,焦点会自动跳到 input 控件上。例子如下

第二阶段:addNewTodo 方法

addNewTodo() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}

this.todos.push - 拿到 todos 数组,并开始操作

  • push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
  • 注意: 新元素将添加在数组的末尾。
  • 注意: 此方法改变数组的长度。
  • https://www.runoob.com/jsref/jsref-push.html
id: this.nextTodoId++,
title: this.newTodoText

在数组 todos 中添加新的元素,

  • id 的值是(拿到nextTodoId的值并且自加 1)的值
  • title 的值是:v-model 双向绑定的newTodoText的值

然后,清空newTodoText的值:this.newTodoText = ''

第三阶段:子组件

app.component('todo-item', {
template: `
<li>
{{ title }}
<button @click="$emit('remove')">删除</button>
</li>
`,
props: ['title'],
emits: ['remove']
})

定义了一个组件todo-item,接受一个父组件传入的值title,通过props: ['title'],接收,没有限制和默认值。

组件中还有一个<button>按钮,绑定了一个自定义的单击事件$emit('remove'),触发父组件的remove监听,

即,当单击此“删除”按钮时,就会触发父组件:@remove="todos.splice(index, 1)"的执行。

用emits: ['remove']来声明remove,以免控制台报错。

  • 自定义事件详解
  • 实战代码
  • Vue 中$emit 的用法
  • 父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!
  • 我们可以使用 v-on 绑定自定义事件
  • 子组件可以使用$emit 触发父组件的自定义监听

核心难点 v-for 渲染

<todo-item v-for="(todo, index) in todos" :key="todo.id" :title="todo.title"
@remove="todos.splice(index, 1)"></todo-item>

v-for="(todo, index) in todos,很常见的 v-for 渲染,里面的index是列表项的索引值(从 0 开始)

v-for 指令需要以 todo in todos 形式的特殊语法, todos 是源数据数组并且 todo 是数组元素迭代的别名。

:key="todo.id" :title="todo.title",分别赋值,key是todos数组里面,id的值,title 是todos数组里面,title 的值。

根据子组件,这里只会渲染 title 的值。

@remove="todos.splice(index, 1)"

绑定一个自定义事件remove,当触发子组件的点击事件remove,就会触发这里的事件,

todos.splice,拿到数组todos并开始操作,

splice(index, 1),删掉 1 个,当前列表索引。(删掉当前这个 li(子组件)的数据)

跑一遍流程:添加数据

  1. 我单击“添加 tudo”,通过for="new-todo",焦点自动到 input 输入框
  2. 我开始输入文本“Npcink”,并单击“添加”按钮,提交 from 表单,通过v-on:submit.prevent="addNewTodo"全局按钮触发事件addNewTodo
  3. 通过v-model双向绑定,将我输入的值(npcink)传给了newTodoText,
  4. addNewTodo方法下,通过 push() 方法新增一条数组,id 的值是通过this获得nextTodoId的值并自增的,title 的值是通过 this 拿到newTodoText的值
  5. 在子组件todo-item中,通过{{title}}展示 title 的值,通过props: ['title']拿到父组件的值
  6. 父组件中通过v-for来循环渲染数组内容,通过:title="todo.title"拿到数组totds中title的值,并将其传给子组件的title
  7. 子组件todo-item将title渲染出来

跑一遍流程:删除数据

  1. 单击删除按钮,通过子组件的$emit('remove')触发父组件的todos.splice(index, 1)操作
  2. 删除了当前行的 li 数据

参考