Vue案例 - TodoMVC

507 阅读8分钟

官网地址:todomvc.com/

准备工作

  1. 在项目目录下右键, 选择在集成终端中打开:

image.png

  1. 从 github 克隆项目模板。
git clone https://github.com/tastejs/todomvc-app-template.git

首先要确保电脑安装了git, git的安装可以参考: juejin.cn/post/700549…

image.png

安装完成后, 会出现如下结构:

image.png

  1. 进入项目目录,安装项目依赖
cd 项目目录
npm install

image.png

  1. 安装 Vue.js
npm install vue

image.png

安装完后这里出现了vue:

image.png

需求分析

  1. 事项列表展示

• 有事项的情况

• 没有事项的情况

  1. 状态栏展示

• 个数展示

• 单位处理

  1. 事项状态切换

• 单个事项切换

• 多个事项切换

  1. 事项新增

• 内容检测

• 回车新增

  1. 事项删除

• 单个事项删除

• 已完成事项删除

  1. 事项编辑

• 触发编辑

• 取消编辑

• 保存编辑

  1. 事项筛选

• 点击切换显示类别

• 更新渲染所有事项

  1. 事项数据持久化

• 读取本地存储

• 更新本地存储

功能实现

模板中我们书写js的文件是app.js, 书写首页html的页面是index.html image.png

结构展示:

image.png

事项列表展示

• 引入 vue.js 文件,创建 Vue 实例设置挂载元素, 绑定的el是 #app

• 在 data 中设置 todos 存储初始数据

new Vue({
		el:"#app",
		data: {
			// todos 用于村粗所有事项信息
			todos: [
				{id: 1, title: "实例内容1", completed: true},
				{id: 2, title: "实例内容2", completed: true},
				{id: 3, title: "实例内容3", completed: false},
				{id: 4, title: "实例内容4", completed: true},
			]
		}
	})

在main中设置事项的视图:

  • 先写一个<ul>, 然后通过 v-for 决定生成多少个<li>

  • 使用:key绑定唯一的id

  • :class可以绑定对象, 在模板中如果class="complated"表示已经完成的事项.

  • 这个<input>表示的是实力内容前面的复选框(打√部分), 我们可以使用v-model与事件的选中状态绑定. 单个复选框选中的状态是用布尔值表示的, 所以我们这里使用todo.completed可以实现数据的双向绑定.

image.png

image.png

设置有无事项时的显示状态:

  • 事项主体部分main 和下面的状态栏footer都要设置有无事项的显示.

  • v-show 适合于频繁切换状态的场景.

  • todos没有事件时, todos.length为0, 可以隐式转换为false, 这时就会隐藏.

image.png

状态栏信息展示

需要实现的功能为:

• 个数展示

• 单位处理

image.png

个数展示:

两种方式:

  • 使用计算属性是因为: 如果选中的事项个数没有发生变化, 可以使用之前的缓存, 提高效率.

  • 计算属性必须有返回值, 所以使用return !todo.completed;可以获得todo.completed = false的事项.

  • 思想是遍历todos, 对每一个传入的todo获取它的completed属性, 如果是false就返回这个事项, 最后使用length方法获得返回的所有事项的长度, 也就是个数.

image.png

image.png

最后在响应的地方调用这个计算属性的属性名就可以了.

<span class="todo-count"><strong>{{ remaining }}</strong> item left</span>

image.png

单位处理:

image.png

image.png

视图中调用时:

<span class="todo-count"><strong>{{ remaining }}</strong> {{ pluralize() }} left</span>

或者

<span class="todo-count"><strong>{{ remaining }}</strong> {{ pluralize('item') }} left</span>

注意后面的部分要加括号, 否则会有问题.

注意传参的时候要加引号, 否则会undefined

image.png

事项状态切换

点击下面的事项实现全选

如果剩下的事项个数为0, 就选中.

image.png

image.png

点击全选选中所有的事项

设置 v-model 后,主动操作 toggle-all 相当于设置 allDone数据,这时需要给 allDone 设置 setter 来处理。

  • value是toggle-all的状态, 选中或未选中.

image.png

事项新增

需要实现的功能为:

• 输入框内容绑定

• 回车新增事项

输入框内容绑定

在 data 中设置 newTodo 用于存储数据,并绑定给新增输入框。

image.png

<input class="new-todo" placeholder="What needs to be done?" autofocus v-model="newTodo">

回车新增事项

输入框回车时检测内容,并根据输入内容新增事项到 todos。

在methods中添加以下方法:

image.png

  • 这里id只做了简单处理, 不一定能够保证完全不重复. 如果需要完全不重复, 可以采用随机数或者时间戳的方式.

  • if用于判断数据是否为空

  • this.newTodo = "";是为了清空输入框的内容

在视图部分添加:

  • 给刚才的v-model.trim="newTodo"添加.trim. .trim鼠标修饰符用于自动过滤用户输入内容首尾两端的空格

  • 添加一个回车事件, 绑定addTodo方法.

<input class="new-todo" placeholder="What needs to be done?" 
autofocus v-model.trim="newTodo" @keyup.enter="addTodo">

提高效率的改进

如果在视图页面直接使用v-model.trim="newTodo", 那么每次在输入框中进行输入的时候(还没点击回车的情况下)都会调用trim方法去除首尾两端的空格, 实际上我们只需要在按下回车添加数据的时候去除首尾两段的空格就可以了, 因此可以将trim方法移到addTodo方法的内部:

image.png

<input class="new-todo" placeholder="What needs to be done?" 
autofocus v-model="newTodo" @keyup.enter="addTodo">

事项删除

需要实现的功能为:

• 单个事项删除

• 已完成事项删除

单个事项删除

点击单个事项中的删除按钮时,删除 todos 中对应的对象数据。

image.png

<button class="destroy" @click="reomoveTodo(todo)"></button>

已完成事项删除

  1. 已完成事项按钮的需要在具有已完成事项时显示。
  • todos.length > remaining表示只有当事项总数大于剩余事项数的时候才显示. 比如一共有5个事项, 剩余了2个, 说明有3个完成了.
<!-- Hidden if no completed items are left ↓ -->
<button class="clear-completed" v-show=" todos.length > remaining">Clear completed</button>
  1. 操作后,事项列表中只保留未完成事项即可。
  • filter方法的返回值是一个包含了符合条件的所有元素的数组. 返回的是所有completed属性为false的元素组成的数组, 这个数组又重新赋值给this.todos.

image.png

  1. 给相关按钮添加一个点击事件: @click="removeCompleted"
<button class="clear-completed" v-show=" todos.length > remaining" 
@click="removeCompleted">Clear completed</button>

事项编辑

需要实现的功能为:

• 触发编辑 (双击)

• 取消编辑 (点击esc)

• 保存编辑 (回车/失去焦点)

触发编辑 (双击)

双击时进行记录正在编辑的 todo,并保存原始 todo 内容。

  • editingTodo记录的是正在编辑的todo的内容, 因为数据是双向绑定的, 因此在调试面板中会看到它随着获取的输入编辑内容的改变而更新.

  • todo.title获取的是未改变前的title

  • titleBeforeEdit 记录的是改动之前的todo的title

image.png

image.png

比如这里将 实例内容2 改成 实例内容8, 每个部分发生的变化如下:

image.png

正在被编辑的 li 需要设置类名 editing

为了保证在双击之后获取的文字是当前显示的文字, 需要设置给编辑框设置v-model="todo.title"

image.png

自动获取焦点

这里直接给输入框设置autofoucs是不行的, 因为在未双击之前输入框是隐藏的, 即使获取了焦点也看不到.

<input class="edit" v-model="todo.title" autofocus>

所以我们需要使用自定义指令. 因为这里只有一个实例, 也不需要区分全局还是局部自定义指令了, 我们就用一个局部的作为例子:

image.png

  • el 指的是元素
  • binding 是给元素传递的一些参数
  • binding.value 是给元素传递的值, 如果这个值为true的话就聚焦

image.png

视图中根据当前的todo是否是正在编辑的todo来判断是否聚焦:

image.png

取消编辑 (点击esc)

点击 f2 键取消编辑,还原事项内容与状态

image.png

<input class="edit" v-model="todo.title" 
v-todo-focus="todo === editingTodo"  @keyup.f2="cancelEdit(todo)">

保存编辑 (回车/失去焦点)

点击回车键或失去焦点时保存编辑

  • 当元素失去焦点时触发blur事件

image.png

image.png

注意:

当编辑内容为空时保存,应当删除 todo, 所以应该在上面的基础上增加一条判断语句:

image.png

回车会导致失去焦点(但失去焦点不会导致回车消失), 因此会出现重复删除的情况(当前事项和最后一个事项会被一起删除), 改进:

image.png

事项筛选

需要实现的功能为:

• 记录筛选类别

• 点击更改类别

在 data 中声明数据存储当前显示的事项类别,并在点击筛选按钮时更改显示的事项类别:

image.png

image.png

设置用于筛选不同类别事项的函数,并统一存储。

image.png

设置计算属性处理 todoType,并设置给视图。

image.png

设置计算属性处理 todoType,并设置给视图。

image.png

之前使用过的数据筛选函数也可以通过 filters 进行统一设置。

image.png

image.png

事项数据持久化

需要实现的功能为:获取本地存储和更新本地存储

  • localStorage 和 sessionStorage 属性允许在浏览器中存储 key/value 对的数据。

  • localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。

  • localStorage.getItem的返回值是一个存储对象

保存数据语法:

localStorage.setItem("key", "value");

读取数据语法:

var lastname = localStorage.getItem("key");

删除数据语法:

localStorage.removeItem("key");

封装函数,用于进行本地存储数据读取。

image.png

将事项数据更改为本地存储数据。

image.png

封装本地存储的更新功能

image.png

由于多种事项操作都需要更新本地存储,单个设置十分繁琐,可以通过侦听器统一设置。

watch的书写方式(handler函数): www.jianshu.com/p/2ed125e95…

  • 处理函数handler的第一个参数是更改为的新值, 对于对象来说,新值和旧值是一样的.
  • handler 的参数值就等于传递给了set

image.png

// 书写方式 ()
watch: {
  someValue: {
    handler (newValue, oldValue) {
      console.log(newValue)
    }
  }
}