Vue2-TodoList案例

:觉得累是因为在走上坡路

TodoList案例

    • 组件化编码流程(通用)
    • 整体思路
      • 1、分析结构
      • 2、拆html和css
      • 3、初始化列表
      • 4、实现添加列表功能
      • 5、实现勾选功能
      • 6、实现删除功能
      • 7、实现底部统计功能
      • 8、实现全选框的交互
        • (1)每个todo控制全选框
        • (2)全选框控制todo全选或者取消全选:使用点击事件实现。
        • (3)全选框控制todo全选或者取消全选:使用v-model实现。
      • 9、点击右下角按钮删除全部已完成任务
    • 案例完整代码展示
      • 1、App.vue
      • 2、TodolistHeader.vue
      • 3、TodolistList.vue
      • 4、TodolistItem.vue
      • 5、TodolistFooter.vue
    • 总结

组件化编码流程(通用)

  1. 实现静态组件:抽取组件,使用组件实现静态页面效果

  2. 展示动态数据:

    ①数据的类型、名称是什么?

    ②数据保存在哪个组件?

  3. 交互——从绑定事件监听开始

整体思路

TodoList类似于我们手机里的待办事项或者备忘录

Vue2-TodoList案例_第1张图片

1、分析结构

确定组件名称TodolistHeader,TodolistList,TodolistItem,TodolistFooter(下文会简写成Header、List、Item、Footer)和个数,还有嵌套关系(TodolistList里面包含TodolistItem,TodolistList和TodolistHello\TodolistFooter为兄弟组件),然后引入相应的组件。

  • 一个命名问题:此处命名不直接用Header、Footer等简单的命名是因为这些命名本身在标签具有特殊的含义(类似于关键字),并且Vue推荐我们使用多单词命名。为了达到和Vue开发者工具里的名字一样,这里使用的“大驼峰”命名(在驼峰命名的基础上首字母大写)。

Vue2-TodoList案例_第2张图片

Vue2-TodoList案例_第3张图片

2、拆html和css

一般的项目是写好html和css的,我们要把他们拆到相应的组件里,可以先全部放到App.vue中,然后打开开发者工具,观察他们的结构,分别放到对应的组件里面,style标签中加上scoped属性(保证当前样式只在当前组件中使用,防止样式名字不能重复使用)

3、初始化列表

在List中定义一个数组todos存储每个item为对象,然后在List的标签中写并用v-for遍历数组中的每个对象生成结构,且通过自定义属性todo把每个对象传给子组件Item

在Item中使用props配置项接收(实现父子组件通信),然后input框里的checked绑定todo.done(可以初始化页面)

props配置项不熟悉的可以点击此处复习props

4、实现添加列表功能

上一步我们是在List中定义了todos数组,而用户的输入是在Header中,也就是说我们要在Header中收集数据,然后传给List,但是兄弟组件如何通信?全局事件总线、消息订阅与发布、Vuex都可以,但是现在还没学。

之前我们学的props配置项,可以实现父组件给子组件传数据(父组件里写子组件标签并配置属性,子组件使用props接收),现在我们想实现兄弟组件HeaderList的通信,可以借助App这个共同的父亲。

(1)把List中的数据todos定义在App里,然后传给List一份(通过props接收,由于是直接传到vc上,模板不会报错)。
(2)在App组件中定义一个函数,函数里面写个参数

	addTodo(x) {
            //借助这个函数,拿到Header中用户输入的东西
            this.todos.unshift(x);
       }

(3)把这个函数传给HeaderHeaderprops接收一下,然后在Header中调用这个函数,把用户的输入传给这个函数

const todoObj = { id: nanoid(), title: this.title, done: false };
this.addTodo(todoObj);

(4)由于addTodo是定义在App上的,所以App就直接拿到了函数的参数,然后就可以直接添加在todos中,这样的话todos改变,模板重新解析,传给Listtodos也改变,List模板重新解析,v-for一遍历,页面就多了一个。

5、实现勾选功能

选中:done=true,不选中:done=false。思路:简单来说就是让App拿到要修改的数据的id,找到这个数据然后把done属性取反。

我们先在App组件里定义一个函数,用来接收当前操作对象的id,然后函数里的逻辑是找到这个id,然后done属性取反(记住,数据源在哪里,修改数据的方法就写在哪里

changeTodo(id) {
            this.todos.forEach((todo) => {
                if (todo.id === id) todo.done = !todo.done;
            })
        }

然后把changeTodo这个函数通过标签和props传给List,再传给Item
然后在Item组件里定义一个函数handleChange,用来获取当前操作的多选框的id

handleChange(id) {
            // 不能像下面这样直接修改props的数据,数据源在哪里,我们就去哪里改
            // 如下代码也能实现功能,但是不建议这么写,理由就是不能直接改props传来的东西
            // this.todo.done = !this.todo.done;   
            //不报错是因为,改的不是整个对象(对象的地址),地址没变就不会报错

            //通知App要修改的数据的id是哪个
            this.changeTodo(id);
        }

handleChange函数通过点击事件来触发,实参就是当前input节点的todo的id

<input type="checkbox" :checked="todo.done" @click="handleChange(todo.id)" />

当然,这个勾选功能不用这么绕来绕去也可以实现,比如我直接在handleChange

this.todo.done = !this.todo.done;   

又或者我用v-model,一行代码就搞定了,不用在App再定义函数,然后传来传去

<input type="checkbox" v-model="todo.done" />

但是不建议这么写,理由就是不能直接改props传来的东西,记住,数据源在哪里,修改数据的方法就配置在哪里,千万记住props是只读的,如果改它,就会改源数据,这样所有用到数据的组件都会受到影响。

6、实现删除功能

和上面的类似,也是需要传递函数,不能动props。
首先App定义一个删除函数

deleteTodo(id) {
            this.todos = this.todos.filter((todo) => {
                return todo.id !== id;
            })
            // this.todos = this.todos.filter(todo => todo.id !== id);
        }

把这个函数传给List,再传给Item
Item中的按钮来个点击事件

<button class="btn btn-danger" @click="handleDelete(todo.id)">删除button>

把当前input的这个id传给App,调用App中的deleteTodo函数让App去操作

handleDelete(id) {
    if (confirm('确定要删除吗?'))  //点确定是true,取消是false
        this.deleteTodo(id);
}

7、实现底部统计功能

(1)App中的todos数据传给Footer一份
(2)全部比较简单,直接插值语法读数组todos的长度就行。已完成这里可以用计算属性

<span>已完成{{ doneTotal }}span> / 全部{{ todos.length }}

(3)计算属性这里有多种实现方式:

写法1:forEach遍历

let count = 0;
this.todos.forEach(todo => {
    if (todo.done) count++;
});
return count;

写法2:for of遍历

let count = 0;
//for in 拿到的是数组的索引,for of才能拿到元素
for (let todo of this.todos) {
    if (todo.done === true)
        count++;
}
return count;

写法3:filter过滤

let count = this.todos.filter(todo => todo.done)
return count.length;

写法4:ES6的reduce

  • 对reduce不熟悉的可以点击此处复习reduce(点击目录里的数组核心方法即可)
const count = this.todos.reduce((pre, todo) => {
    return pre + (todo.done ? 1 : 0)
}, 0);
return count;
//return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);

8、实现全选框的交互

(1)每个todo控制全选框

当已完成和全部相等且不等于0时,勾选全选框,写在计算属性中

<input type="checkbox" :checked="isAll" " />
isAll(){
  return this.total === this.doneTotal && this.total !== 0;
  }

如果全部是0个,就隐藏Footer(用v-show实现)

(2)全选框控制todo全选或者取消全选:使用点击事件实现。

App中定义一个函数,用来接收Footer中全选框的状态值,然后把每个数据的done值都改成和全选框的状态值一样(选择or不选择)

checkAllTodos(isDone) {
            this.todos.forEach(todo => todo.done = isDone);
        },

Footer的全选框中,加入点击事件并定义一个函数

<input type="checkbox" :checked="isAll" @click="handleCheckAll" />

在函数中调用App中的函数,把状态传过去

handleCheckAll(e) {
            // this.checkAllTodos(!this.isAll);  
            this.checkAllTodos(e.target.checked);  //直接拿dom的值比较合适
        },

(3)全选框控制todo全选或者取消全选:使用v-model实现。

第一步一样,在App中定义一个函数,用来接收Footer中全选框的状态值,然后把每个数据的done值都改成和全选框的状态值一样(选择or不选择)

checkAllTodos(isDone) {
            this.todos.forEach(todo => todo.done = isDone);
        },

在Footer的全选框中,加入v-model(没有value绑定的是checked)绑定isAll计算属性

<input type="checkbox" v-model="isAll" />

isAll中加入setter,把每个更新的值传给App中的函数。这样就不用再另外定义methods了,只用计算属性就能搞定:item影响全选框(getter读isAll)全选框影响item(setter改isAll)两种情况

isAll: {
            get() {
                return this.total === this.doneTotal && this.total !== 0;
            },
            set(val) {
                this.checkAllTodos(val);
            }
        },

9、点击右下角按钮删除全部已完成任务

App中定义一个方法并传给Footer

//5.删除已完成任务
clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done
      })
    },

Footer中调用这个方法

<button class="btn btn-danger" @click="clearAll">清除已完成任务button>
clearAll() {
      this.clearAllTodo()//点击调用App里边的方法,把所有true删掉
    },

案例完整代码展示

1、App.vue






2、TodolistHeader.vue






3、TodolistList.vue






4、TodolistItem.vue






5、TodolistFooter.vue






总结

1.组件化编码流程:
(1)拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

  • 一个组件在用:放在组件自身即可。
  • 一些组件在用:放在他们共同的父组件上(状态提升)。

(3)实现交互:从绑定事件开始。

2.props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)

3.使用v-model时要切记
v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

4.关于props
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

你可能感兴趣的:(Vue2+Vue3,前端实用案例,javascript,开发语言,前端,es6,vue.js)