【Vue 快速入门系列】todoList案例小总结

news2024/11/28 6:42:28

文章目录

    • 一、案例效果
    • 二、项目介绍
    • 三、版本更新迭代
    • 末、项目素材
      • 1.css样式
      • 2.html

一、案例效果

如下图所示,制作一个这样的记事本,可以使用这个记事本进行数据的存储以及管理,样式是天禹老师写好的我们直接使用就好了,主要在这个小案例中体会一下在Vue中如何维护页面展示的数据。在读懂这篇博客之前,需要有一定的前置知识前置知识传送门。
最初版本:
在这里插入图片描述
各项完善之后
在这里插入图片描述

二、项目介绍

这个小小案例主要页面主要分为三大部分头、身体、脚部。这三个部分的组件为兄弟组件均归App组件进行管理
通过bootstrap.css渲染实现上面GIF的效果。

三、版本更新迭代

在写这个小案例时,可以有以下几个版本的更新迭代,建议从最基础的功能实现做起,由于篇幅有限这里只放基础功能源码及其他版本的核心代码,如果有什么疑问的话欢迎评论区留言。
在进行写代码的时候遵循以下步骤:

  1. 搭建起网页框架
  2. 使用css装饰
  3. 写入静态数据
  4. 测试JavaScript事件可行性
  5. 静态数据替换
  6. 大工告成

TodoList案例各个版本应实现的功能

  • v1、实现基础功能
    • 整体页面布局
    • 使用props属性编程技巧实现父子间通信(父组件传递props数据,子组件调用传进来的函数以实现修改父组件属性)
    • 使用javascript中的列表操作方法及ES6语法规范
    • 使用模板语法渲染页面
  • v2、实现本地存储
    • 添加浏览器本地存储功能
    • 添加浏览器本地存储读取功能
    • 添加浏览器本地存储清空功能
  • v3、使用自定义事件绑定
    • 删除props属性传递函数的通信方式
    • 增加父组件自定义事件绑定并在子组件触发的通信方式
  • v4、使用全局事件总线
    • 将数据源从App组件移入MyList组件
    • 删除原有的自定义事件绑定
    • 使用全局事件总线进行兄弟间组件通信
  • v5、增加编辑功能
    • 使用nextTick小技巧实现编辑聚焦
    • @blur="handBlur(todo,$event)失焦回调,获取模板对应的数据及编辑框的dom元素本身

基础功能版本页面的整体布局及代码如下:
在这里插入图片描述

main.js 存放基础的配置

import App from "./App.vue"
import Vue from "vue"
new Vue({
    el:"#App",
    render:h=>h(App)
})

App.vue 管理其余组件,提供页面的布局

<template>
<div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
        <MyHead :lss="lss" :addobj="addtodo"></MyHead>
        <MyList :lss="lss" :alertDone="alertDone" :deltodo="deltodo"></MyList>
        <MyFoot :lss="lss" :alertall="alertall" :deld="deldoneall"></MyFoot>
    </div>
  </div>
</div>
</template>

<script>
import MyHead from "./components/MyHead.vue"
import MyFoot from "./components/MyFoot.vue"
import MyList from "./components/MyList.vue"
import {nanoid} from "nanoid"
export default {
    name:"App",
    data(){
      return {
        lss:[
          {id:1,title:"吃饭",done:false},
          {id:2,title:"喝水",done:true},
          {id:3,title:"睡觉",done:false}
        ]
      }
    },
    components:{
        MyFoot,
        MyHead,
        MyList
    },
    methods:{
      alertDone(id){
        this.lss.forEach((todo)=>{
          // alert("done")
          if (todo.id===id){todo.done=!todo.done}
        })
      },
      deltodo(id){
          this.lss=this.lss.filter((todo)=>{
              return todo.id!==id
          })
      },
      addtodo(titname){
          if (titname===""){
            return false;
          }
          var index = this.lss.findIndex(item => item.title === titname)
          console.log(index)
          if (index!==-1){
            return false
          }
          console.log(index)
          this.lss.unshift({"id":nanoid(),"title":titname,"done":false})
          return true
      },
      alertall(e){
        this.lss.forEach((todo)=>{
          todo.done=e
        })
      },
      deldoneall(){
        this.lss=this.lss.filter((todo)=>{
          return !todo.done
        })
      }
    }
}
</script>

<style>
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>

MyList.vue 作为MyItem的父组件,也就是页面的中间部分

<template>
    <ul class="todo-main">
       <!-- eslint-disable-next-line vue/valid-v-for -->
      <MyItem v-for="todo in lss" :todo="todo" :aleDone="alertDone" :deltodo="deltodo"></MyItem>
    </ul>
</template>

<script>
import MyItem from "./MyItem.vue"
export default {
    name:"MyList",
    components:{
      MyItem
    },
    props:["lss","alertDone","deltodo"]
}
</script>

<style scoped>
  .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>

MyItem.vue 展示一条代办事项信息

<template>
    <li>
        <label>
          <!-- 
            这里如果使用双向绑定的话。会修改掉props传过来的数据,虽然没有报错,但是一般不可以这么做。
          -->
            <input type="checkbox" :checked="todo.done" @change="alertdone(todo.id)"/>
            <span>{{todo.title}}</span>
        </label>
        <button class="btn btn-danger" @click="delthistodo(todo.id)">删除</button>
    </li>
</template>

<script>
export default {
    name:"MyItem",
    props:["todo","aleDone","deltodo"],
    computed:{
    },
    
    methods:{
      alertdone(id){
        if (id){
        // alert(id)
          this.aleDone(id)
        }
      },
      delthistodo(id){
        this.deltodo(id)
      }
    }
}
</script>

<style scoped>
    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>

MyHead.vue 页面的头部

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

<script>
export default {
    name:"MyHead",
    props:["lss","addobj"],
    data(){
      return {
        temp:""
      }
    },
    methods:{
      adddata(){
        if (this.addobj(this.temp)){
          alert("添加成功!")
        }else{
          alert("添加失败!")
        }
        this.temp=""
      }
    }
}
</script>

<style scoped>
  .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>

MyFoot.vue 页面的脚部

<template>
        <div class="todo-footer" v-if="lss.length">
        <label>
          <input type="checkbox" v-model="alerall"/>
        </label>
        <span>
          <span>已完成{{donenum}}</span> / 全部{{lss.length}}
        </span>
        <button class="btn btn-danger" @click="deldoneall">清除已完成任务</button>
      </div>
</template>

<script>
export default {
    name:"MyFoot",
    props:["lss","alertall","deld"],
    computed:{
      donenum(){
        // 进行计数统计常用的方法。函数调用过程为第一个参数为回调函数,第二参数为初始值
        // 回调函数中第一个参数是函数调用过程中的返回值,第二个为对象
        return this.lss.reduce((pre,todo)=>pre+(todo.done?1:0),0)
      },
      alerall:{
        get(){
          return (this.lss.length===this.donenum)&&this.lss.length!==0
        },
        set(e){
          // 这里如果是勾选的话就传进来true,否则传进来false
          // console.log(e)
          this.alertall(e)
        }
      }
    },
    methods:{
      deldoneall(){
        this.deld()
      }
    }
}
</script>

<style scoped>
  .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>

末、项目素材

可以先尝试将以下代码拆分为最初的v1版本,然后逐步向高版本迭代。

1.css样式

/*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;
  }
  
  /*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);
  }
  
  /*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;
  }
  /*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;
  }
  
  /*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;
  }
  

2.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>React App</title>

  <link rel="stylesheet" href="index.css">
</head>
<body>
<div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
      </div>
      <ul class="todo-main">
        <li>
          <label>
            <input type="checkbox"/>
            <span>xxxxx</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
        <li>
          <label>
            <input type="checkbox"/>
            <span>yyyy</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
      </ul>
      <div class="todo-footer">
        <label>
          <input type="checkbox"/>
        </label>
        <span>
          <span>已完成0</span> / 全部2
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
      </div>
    </div>
  </div>
</div>

</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[UE][UE5]零基础学习-学习记录1-UE5安装与基本使用方法

[UE5]学习1-UE5安装与基本使用方法写在前面01.作者碎碎念2.UE5安装方法01.UE5需要的电脑配置02.UE5安装方法001.Epic下载002.下载安装UE503.基本使用方法001.创建项目打开现有的项目&#xff1a;002.文件目录结构003.用户界面介绍1).3D画面视窗2).菜单栏3).内容浏览器4).属性面…

关于CM3/CM4位带操作的总结

1.位带操作定义 STM32的存储器映射中的内存区域和外设区域有一段地址空间&#xff08;都是最低1MB&#xff09;是位带区域&#xff0c;跟这个区域相对应的有一段位带别名区域&#xff0c;位带别名区的大小是位带区的32倍&#xff0c;位带别名区的每一个地址都对应位带区域的一个…

C. Hamiltonian Wall edu139 div2

Problem - C - Codeforces 题意是给你一个2*n的网格&#xff0c;让你一笔把所有的B涂满&#xff0c;并且只能涂一次&#xff0c;问你是否可行 分析&#xff1a; 其实分析的时候我想到了转移。每一次的结果是由上一次转移而来&#xff0c;所以如果前后矛盾的话&#xff0c;即…

人工智能:智能语音技术应用场景介绍

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️个人荣誉&#xff1a; 数据库领域优质创作者&#x1f3c6;&#x…

虹科QA | SWCF2022 12月6日演讲笔记:C波段卫星与5G之间的干扰排查及解决方案

虹科2022年度SWCF卫星通信与仿真测试研讨会正在进行中。昨日精彩演讲&#xff1a;C波段卫星与5G之间的干扰排查及解决方案&#xff0c;感谢大家的观看与支持&#xff01; 昨晚的直播间收到一些粉丝的技术问题&#xff0c;虹小科汇总了热点问题并请讲师详细解答&#xff0c;在此…

目前UI设计薪资待遇怎么样?工作好找吗?

UI设计的火爆&#xff0c;导致有很多年轻人都愿意投身于这个行业。有很多年轻的朋友都在问&#xff0c;UI设计的薪资待遇怎么样&#xff1f;工作难找吗&#xff1f;本文统一解答一下。 1、UI设计的薪资水平 UI设计的薪资待遇一直很好&#xff0c;学习UI设计之前没有任何相关基础…

PMP每日一练 | 考试不迷路-12.13(包含敏捷+多选)

被延期考试的宝子 一定要坚持刷题 每日5道PMP习题助大家上岸PMP&#xff01; ​题目1-2&#xff1a; ​1.一位主要相关方要求将每日站立会议的持续时间人15分钟增加到1小时。Scrum主管应该做什么? ( ) A.接受建议并建议团队更改会议时间表 B.安排与产品负责人和团队开…

窗口销毁消息 WM_DESTROY 的正确处理方式

上次&#xff0c;我提到了可能导致正常的消息循环被破坏的怪异之处。 有一位读者 Adrian 指出&#xff0c;WM_GETMINMAXINFO 消息在顶级窗口 WM_NCCREATE 之前到达。这确实很不幸&#xff0c;但&#xff08;无论是否错误&#xff09;十多年来一直如此&#xff0c;现在修改它会…

【实时数仓】实现用户行为日志相关功能(源码)

文章目录一 准备用户行为日志-DWD层1 代码实现&#xff08;1&#xff09;识别新老访客&#xff08;2&#xff09;利用侧输出流实现数据拆分&#xff08;3&#xff09;将不同流的数据推送到下游kafka的不同Topic&#xff08;分流&#xff09;a 封装方法b 程序中调用kafka工具类获…

数据链路层

文章目录数据链路层的功能ARP协议DNS-------域名解析&#xff08;浅浅的了解一下&#xff09;在浏览器中输入URL后&#xff0c;发生的事情&#xff08;经典面试题&#xff09;ICMP协议NAT技术代理服务器网络核心知识大总结数据链路层的功能 对比理解网络层。 网络层 &#xff…

飞控学习随记

常见指令 编译Arduplane程序 cd ardupilot/ ./waf plane 进入 Tools/autotest 文件夹中&#xff0c;启动3D flightgear ./fg_quad_view.sh 进入ArduPLane文件夹中&#xff0c;启动仿真 sim_vehicle.py --map --console -L KSFO&#xff08;-L 选择起飞位置&#xff09; 解锁…

字节女测试工程师万字总结的软件测试入门技巧

成为一个优秀的测试工程师需要具备哪些知识和经验&#xff1f; 针对这个问题&#xff0c;可以直接拆分以下三个小问题来详细说明&#xff1a; 1、优秀软件测试工程师的标准是什么&#xff1f; 2、一个合格的测试工程师需要具备哪些专业知识&#xff1f; 3、一个合格的测试工程…

前端vue项目部署到生产环境(包括nginx安装及配置)

一.vue3项目打包 vue3项目 使用vue-cli创建的&#xff0c;使用npm run build打包到dist 二.在服务器上安装nginx 1.去nginx的官网下载windows版本的nginx&#xff0c;下载地址&#xff1a;nginx: download 最好安装稳定版&#xff0c;下载完成后解压nginx压缩包&#xff1a…

Android Studio实现数独小游戏,休闲益智

文章目录一、项目概述二、开发环境三、详细设计3.1 界面设计3.2 逻辑设计四、运行演示一、项目概述 数独是一种逻辑解谜游戏&#xff0c;它规则稍复杂&#xff0c;解题过程富有挑战性。本次安卓数独小游戏&#xff0c;主页面有继续游戏、新游戏、关于和退出四个功能&#xff0…

【实训项目】教师工作量管理系统(超级详细)

目录 一、需求与分析 1. 项目概述 1.1 教师信息处理 1.2 教师工作量数据处理&#xff1a; 1.3 教师综合信息输出 2. 需求分析 3. 模块设计 3.1 功能模块 3.2 所有功能模块的流程图 二、设计与实现 1. 程序设计 1.1 教师工作量管理系统 1.2 登录系统 1.3 主函数…

初级算法之字符串

344. 反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 思路一: 从中间开始向两边遍历,然后两边交换位置,最终获得字符串的反转 // class Solution {public void reverseString(char[] s) {int len s.length,siz…

二、JavaScript——Hello World

1. 创建文件 提前在本地新建好文件夹用于存储项目代码&#xff0c;再通过VSode打开指定存储代码的指定文件夹&#xff0c;并新建HelloWorld.html文件 HelloWorld.html文件新建成功之后&#xff0c;输入“&#xff01;”点击自动生成标签 自动生成的标签如下&#xff1a; <!…

02Golang执行流程简介

Golang执行流程简介Golang执行流程的分析两种流程的方式区别什么是编译什么是运行Go程序开发注意事项Golang执行流程的分析 如果是对源码编译后&#xff0c;再执行&#xff0c;go的执行流程如下 如果对源码直接执行go run源码&#xff0c;go的执行流程如下 两种流程的方式区…

副业想做自媒体可以选择什么领域,适合宝妈的三个自媒体领域推荐

大家好&#xff0c;我是蝶衣王的小编&#xff0c;今天说说自媒体可以选择的领域 在过去的两年里&#xff0c;最受欢迎的职业之一必须属于自媒体。无论是全职还是副业&#xff0c;每个人都可以这样做。许多人经常在互联网上看到&#xff0c;通过自媒体&#xff0c;月收入数千或…

【汽车电子】can报文和can database(candbc)

1.can就是controller area network&#xff0c;是面向汽车的通信协议&#xff0c;通俗来讲就是在汽车电子控制领域中的不同部分进行通信的&#xff08;传输数据&#xff09;。 2.can报文有标准帧和扩展帧两种&#xff0c;也就是can和canfd&#xff0c;canfd是can的升级版&…