Vue CLI Todo-List案例

本文介绍了如何在VueCLI项目中使用组件化开发,包括数据存储策略、组件间的通信方法(props和事件),以及组件内状态提升的最佳实践。通过TodoList案例展示了如何使用v-model和props进行父子组件间的数据交换和交互。
摘要由CSDN通过智能技术生成

Vue CLI Todo-List案例

1.效果展示

效果展示

2.项目结构&界面组件拆分

在这里插入图片描述
在这里插入图片描述

3.成品代码

思路:

  1. 将数据存放到APP.vue中,因为TodoHeader中需要添加数据,TodoList也要展示数据,所以将数据存放到APP中,便于组件之间的通信
  2. TodoList中,使用v-for =TodoItem 的形式展示所有数据
  3. 将数据定义为一个一个的数组对象,便于进行增删, {id:‘xxx’,title:‘xxx’,done:true},
  4. 使用 props的方式进行通讯
    父 -->子, 在组件标签中之间传输
    子–>父 ,父组件给子组件传入一个函数,让子组件调用,从而实现子组件与父组件的通讯

App.vue

<template>

  <div class="todo-container">
    <div class="todo-wrap">

      <TodoHeader :addTodo="addTodo"/>
      <TodoList :todos="todos" :deleteTodo="deleteTodo" :checkTodo="checkTodo"/>
      <TodoFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>

    </div>
  </div>

</template>

<script>
import TodoHeader from './components/TodoHeader.vue'
import TodoFooter from './components/TodoFooter.vue'
import TodoList from './components/TodoList.vue'
export default {
    name: 'App',
    components:{
    TodoHeader : TodoHeader,
    TodoFooter : TodoFooter,
    TodoList : TodoList
    },
    data(){
        return {
       todos:[
          {id:'001',title:'抽烟',done:true},
          {id:'002',title:'喝酒',done:false},
          {id:'003',title:'开车',done:true}
        ]
        }
    },
    methods:{
        addTodo(todo){
            this.todos.unshift(todo)
        },
        deleteTodo(id){
          this.todos=this.todos.filter((todo)=>{return todo.id!==id})
        },
        checkTodo(id){
          this.todos.forEach((todo)=>{
          if(todo.id === id) todo.done = !todo.done
        })
        },
        checkAllTodo(done){
         this.todos.forEach((todo)=>{
          todo.done=done
         })
        },
        clearAllTodo(){
          this.todos=this.todos.filter((todo)=> {return !todo.done})
        }
    }

}
</script>

<style>
/*app*/
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

TodoHeader.vue

<template>
   <div class="todo-header">
    <input type="text" v-model="title" placeholder="请输入你的任务名称,按回车键确认" @keydown.enter="add"/>
  </div>
</template>

<script>
export default {
 name: "TodoHeader",
 props:["addTodo"],
  data(){
    return {
      title: '',
    }
  },
 methods:{
    add(){
        const title =this.title.trim();
        if(!title){
             alert("请输入内容");
        }else{
            const todo = {title, done:false};
          this.addTodo(todo)
          this.title=""
        }
          
    }
 }

}
</script>

<style>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}

</style>

TodoList.vue

<template>
  <li>
    <label>
      <input type="checkbox" :checked="todo.done"  @change="handleCheck(todo.id)"/>
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)" >删除</button>
  </li>
</template>

<script>
export default {
 props:["todo","checkTodo","deleteTodo"],
 methods:{
     handleDelete(id){
       if(confirm('确定删除吗?')){
             this.deleteTodo(id)
       }

    },
    handleCheck(id){
      this.checkTodo(id)
    }
 }
}
</script>

<style>
 li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;
    border-bottom: 1px solid #ddd;}
  li label {float: left;cursor: pointer;}
  li label li input {vertical-align:middle; margin-right:6px; position:relative;top: -1px;}
  li button {float: right;display: none; margin-top: 3px;}
  li:before {content: initial;}
  li:last-child {border-bottom: none;}
  li:hover{background-color: #ddd;}
  li:hover button{display: block;}
</style>

TodoItem.vue

<template>
  <li>
    <label>
      <input type="checkbox" :checked="todo.done"  @change="handleCheck(todo.id)"/>
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)" >删除</button>
  </li>
</template>

<script>
export default {
 props:["todo","checkTodo","deleteTodo"],
 methods:{
     handleDelete(id){
       if(confirm('确定删除吗?')){
             this.deleteTodo(id)
       }

    },
    handleCheck(id){
      this.checkTodo(id)
    }
 }
}
</script>


<style>
 li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;
    border-bottom: 1px solid #ddd;}
  li label {float: left;cursor: pointer;}
  li label li input {vertical-align:middle; margin-right:6px; position:relative;top: -1px;}
  li button {float: right;display: none; margin-top: 3px;}
  li:before {content: initial;}
  li:last-child {border-bottom: none;}
  li:hover{background-color: #ddd;}
  li:hover button{display: block;}
</style>

TodoFooter.vue

<template>
     <div class="todo-footer" v-show="total">
        <label>
          <!-- <input type="checkbox" :checked="isAll" @change="checkAll" /> -->
            <input type="checkbox" v-model="isAll" />
        </label>
        <span>
          <span>已完成{{doneTotal}}</span> / 全部{{total}}
        </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
      </div>
</template>

<script>
export default {
   name :"TodoFooter",
   props:["todos","checkAllTodo","clearAllTodo"],
   computed:{
    total(){
     return this.todos.length;
    },
    isAll:{
      get(){
        return this.doneTotal===this.total && this.total >0
      },
      set(value){
        this.checkAllTodo(value)
      }
    },
    doneTotal(){
       return this.todos.reduce((pre,current)=>{return pre+(current.done? 1:0)},0)
    }
   },
   methods:{
    // checkAll(e){
    //   this.checkAllTodo(e.target.checked)
    // },
    clearAll(){
     this.clearAllTodo()
    }
   }
   

}
</script>

<style>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}

.todo-footer button {
  float: right;
  margin-top: 5px;
}

</style>

**

4.总结

**

  1. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

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

  2. props适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

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

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值