IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue2学习day05—Todo-list案例 -> 正文阅读

[JavaScript知识库]Vue2学习day05—Todo-list案例

Vue2学习day05—Todo-list案例

本篇博客为跟着尚硅谷Vue课程做的Todo-list案例,目的是了解熟悉组件化编码流程
开始之前先对组件化编码流程做下说明:

  • 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
  • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
    1).一个组件在用:放在组件自身即可。
    2). 一些组件在用:放在他们共同的父组件上(状态提升)。
  • 实现交互:从绑定事件开始。

拆分静态组件

首先我们来完成静态组件先来分析下页面
在这里插入图片描述
经过分析我们可建立如下项目组件结构:
在这里插入图片描述

静态页面实现

各组件页面静态代码如下

  • TodoHeader.vue

    <template>
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
      </div>
    </template>
    
    <script>
        export default {
            name:'TodoHeader',
            
        }
    </script>
    <style scoped>
        /*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>
    
  • TodoItem.vue

    <template>
      <li>
        <label>
            <input type="checkbox"/>
            <span>xxxxx</span>
        </label>
        <button class="btn btn-danger" style="display:none">删除</button>
      </li>
    </template>
    
    <script>
        export default {
            name:'TodoItem',
        }
    </script>
    <style scoped>
        /*item*/
        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;
        }    
    </style>
    
  • TodoList.vue

    <template>
      <ul class="todo-main">
          <TodoItem />
          
      </ul>
    </template>
    
    <script>
        import TodoItem from './TodoItem'
        
        export default {
            name:'TodoList',
            components:{
              TodoItem
            }
        }
    </script>
    <style scoped>
        /*main*/
        .todo-main {
        margin-left: 0px;
        border: 1px solid #ddd;
        border-radius: 2px;
        padding: 0px;
        }
    
        .todo-empty {
        height: 40px;
        line-height: 40px;
        border: 1px solid #ddd;
        border-radius: 2px;
        padding-left: 5px;
        margin-top: 10px;
        }    
    </style>
    
  • TodoFooter.vue

    <template>
      <div class="todo-footer">
          <label>
          <input type="checkbox"/>
          </label>
          <span>
          <span>已完成0</span> / 全部2
          </span>
          <button class="btn btn-danger">清除已完成任务</button>
      </div>
    </template>
    
    <script>
        export default {
            name:'TodoFooter',
            
        }
    </script>
    <style scoped>
        /*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>
    
  • App.vue

    <template>
        <div id="root">
            <div class="todo-container">
                <div class="todo-wrap">
                    <TodoHeader />
                    <TodoList />
                    <TodoFooter />
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import TodoHeader from './components/TodoHeader'
        import TodoFooter from './components/TodoFooter'
        import TodoList from './components/TodoList'
        	
        export default {
            name:'App',
            components:{
                TodoHeader,
                TodoFooter,
                TodoList
            }
        }
    </script>
    
    <style>
        /*base*/
        body {
        background: #fff;
        }
    
        .btn {
        display: inline-block;
        padding: 4px 12px;
        margin-bottom: 0;
        font-size: 14px;
        line-height: 20px;
        text-align: center;
        vertical-align: middle;
        cursor: pointer;
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
        border-radius: 4px;
        }
    
        .btn-danger {
        color: #fff;
        background-color: #da4f49;
        border: 1px solid #bd362f;
        }
    
        .btn-danger:hover {
        color: #fff;
        background-color: #bd362f;
        }
    
        .btn:focus {
        outline: none;
        }
    
        .todo-container {
        width: 600px;
        margin: 0 auto;
        }
        .todo-container .todo-wrap {
        padding: 10px;
        border: 1px solid #ddd;
        border-radius: 5px;
        }
    </style>
    

实现动态组件

初始化列表

一堆数据使用数组存储,数据项如果较为复杂,数组中的数据项使用对象实现
首先动态的拿到列表中的数据进行展示

  • TodoList.vue中添加数据项,并对TodoItem使用v-for遍历,并将数据传递到TodoItem
    data() {
      return {
        todos:[
          {id:'001',title:"敲代码",done:false},
          {id:'002',title:"吃早饭",done:true},
          {id:'003',title:"出门",done:false}
        ]
      }
    },
    
    <TodoItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/>     
    
  • 在TodoItem中接收数据,并显示到页面
    // 声明接收todo
    props:['todo']
    
    <label>
        <input type="checkbox" :checked="todo.done" />
        <span>{{todo.title}}</span>
    </label>
    

实现交互

添加

在TodoHeader给输入框添加数据绑定并绑定一个键盘事件v-model="title" @keyup.enter="add"在add方法中我们将用户的输入绑定成一个todo对象,并将其传递给TodoList
我们可以使用uuid为我们生成全球唯一的标识符,但由于uuid包比较大,这里我们使用简化的nanoid

npm install nanoid

nanoid使用的是分别暴露,所以我们使用如下方式引入

import {nanoid} from 'nanoid'

将用户的输入包装成一个todo对象

const todoObj = {id:nanoid(),title:this.title,done:false}
data(){
  return{
    title:''
  }
},
methods:{
  add(e){
    // 校验数据
    if(!this.title.trim()) return alert('输入不能为空')
    // 将用户的输入包装成一个todo对象
    const todoObj = {id:nanoid(),title:this.title,done:false}
    console.log(todoObj)
    // 清空输入
    this.title=''
  }
} 

如何将TodoHeader中的数据传递给到TodoList中呢?
二者的关系为兄弟,兄弟之前传递数据以我们目前所学的知识是办不到的,我们之前学的一直是父亲给给孩子传递数据,我们先用最初级的方式来解决下这个问题,既然两个兄弟之间无法传递数据,那我们可以将数据给他们共同的父亲来间接的传递,如下图:
在这里插入图片描述
孩子给父亲传递数据,需要父亲提前给孩子一个函数,孩子在自己里面调用这个函数,而函数的定义是在父亲中的,故父亲即可收到参数
将我们之前在TodoList组件中定义的todos数据拿到App组件中

data() {
  return {
    todos:[
      {id:'001',title:"敲代码",done:false},
      {id:'002',title:"吃早饭",done:true},
      {id:'003',title:"出门",done:false}
    ]
  }
},

拿过来后我们需要将App组件中的todos传递给TodoList组件

<TodoList :todos="todos"/>

在TodoList组件中接收

props:['todos']

编写addTodo函数,以能接收到其孩子TodoHeader传递的数据

methods:{
    addTodo(todoObj){
        //console.log(x)
        this.todos.unshift(todoObj)
    }
}
<TodoHeader :addTodo="addTodo" />

TodoHeader组件调用addTodo函数
先接收

props:['addTodo'],

之后在add方法中清空输入前去通知App组件去添加一个todo对象

this.addTodo(todoObj)

勾选

当我们勾选每个列表项的check框时需要更改todos中的数据项的done属性,对其进行取反操作,这里的数据传递是孙子->爷爷,以目前所学,我们可以借助其父亲做个过渡
在这里插入图片描述
两对父子还是采用函数的形式,即父亲提前给孩子一个函数,孩子在自己里面调用这个函数。逻辑在添加操作已进行分析,这里不再缀述。

  • ‘爷爷’:App组件
    • 勾选处理函数
      // 数据在哪,操作数据的方法就在哪个组件
      // 勾选或者取消勾选一个todo
      checkTodo(id){
          this.todos.forEach((todo)=>{
              if(todo.id === id) todo.done = !todo.done
          })
      },
      
    • 传递给其孩子TodoList组件
      <TodoList :todos="todos" :checkTodo="checkTodo" />
      
  • ‘父亲’:TodoList组件
    • 接收其父亲App组件传递的checkTodo
      props:['todos','checkTodo']
      
    • 传递给其孩子TodoItem组件
      <TodoItem v-for="todoObj in todos" :key="todoObj.id" 
      :todo="todoObj" :checkTodo="checkTodo" />
      
  • ‘孙子’:TodoItem组件
    • 接收其父亲TodoList组件传递的checkTodo函数
      props:['todo','checkTodo'],
      
    • 给checkbox框绑定事件
      <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)" />
      <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,修改了props -->
      <!-- <input type="checkbox" v-model="todo.done"> -->
      
    • 添加响应处理方法
      handleCheck(id){
          //console.log(id)
          // 通知App组件将对应的todo对象的done值取反
          this.checkTodo(id)
        },
      

删除

现在我们来看删除逻辑,其实很简单,和勾选类似,也是孙子给爷爷传递数据,组件间的函数传递接收这里不再缀述,仅把一些关键的逻辑写一下

  • App组件中的删除todo方法
    // 删除一个todo
    deleteTodo(id){
        this.todos = this.todos.filter((todo)=>{
            return todo.id !== id
        })
        // 简写
        //this.todos = this.todos.filter(todo => todo.id !== id)
    },
    
  • TodoItem组件调用
    handleDelete(id){
       if(confirm('确定删除吗')){
         this.deleteTodo(id)
       }
     }
    

全选or取消全选

  • ‘父亲’:App组件
    • 定义checkAllTodo函数
      // 全选or取消全选
      checkAllTodo(done){
          this.todos.forEach((todo)=>{
              todo.done = done
          })
      },
      
    • 将checkAllTodo函数传递给其孩子TodoFooter组件
      <TodoFooter :todos="todos" :checkAllTodo="checkAllTodo" />
      
  • ‘孩子’:TodoFooter组件
    • 接收其父亲传递的函数
      props:['todos','checkAllTodo'],
      
    • 给底部checkbox绑定点击事件
      <input type="checkbox" v-model="isAll"/>
      
    • 显示统计信息
      <span>已完成{{doneTotal}}</span> / 全部{{total}}
      
    • 计算属性doneToal,total,isAll
      computed:{
          total(){
              return this.todos.length
          },
          doneTotal(){
              // todos的长度为多少,则调用几次,第一次的pre为0,之后的每次为上次的返回值,最后一次的返回值为该函数的返回值
              // current为每个todo项
              /*const x =this.todos.reduce((pre,current)=>{
                  console.log('@',pre,current)
                  return pre + (current.done ? 1 : 0)
              },0)
              return x*/
      
              // 简写
              return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)
          },
          // 该计算属性为使用其他计算属性计算而来
          // 若使用v-model绑定,则不能使用简写形式
          isAll:{
              get(){
                  return this.doneTotal === this.total && this.total > 0
              },
              set(value){
                  this.checkAllTodo(value)
              }
          }
      },
      

清除所有已完成的todo

经过上面的学习,大家肯定已经掌握了数据传递方法,这里只写关键逻辑

  • App组件
    // 清除所有已完成的todo
     clearAllTodo(){
         this.todos = this.todos.filter((todo)=>{
             return !todo.done 
         })
     }
    
  • TodoFooter组件
    • 给按钮绑定点击事件
      <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
      
    • 定义相应方法
      clearAll(){
          if(confirm('您确定要清除所有已完成的任务吗')){
              this.clearAllTodo()
          }  
      }
      

效果图

在这里插入图片描述

注意点

  • props适用于:
    (1).父组件 ==> 子组件 通信
    (2).子组件 ==> 父组件 通信(要求父先给子一个函数)
  • 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
  • props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:23:40  更:2022-03-15 22:24:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:53:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码