【超级新手向】用 Vue 实现 to-do list (只用了 v-for、v-on、v-bind 和 methods)

229 阅读3分钟

本篇文章起源于我在学习 Vue 过程中遇到的 to-do list 练手项目。当我学习了前端三大件后,就选择了 Vue 作为下一步的学习对象。我目前只读了 Vue 3.0 的官方文档之基础篇,对 Vue 的了解仅限于基础的数据绑定、methods 方法和 v 指令。因此在实现 to-do list 过程中,我所使用的相关技术栈也仅限于上述提到的这些。

阅读本文的前置条件:

  • 了解 HTML、CSS、JS 对象和基础函数;
  • 了解 Vue 中的数据与组件绑定、methods、v-for、v-on 和 v-bind 指令;

实现目标:

  1. 用户可输入多项今日待做任务;
  2. 今日待做任务显示在待做列表中;
  3. 今日待做任务完成后,待做任务移入已完成列表;
  4. 用户可重新将待做任务移入待做列表;
  5. 用户可清空待做列表和已完成列表;

实现路径:

  • 目标 1 要求一个输入框和提交按钮,当它提交后,数据进入待做列表;因此,我们需要一个 v-model 和 data 数据。
  • 目标 2 要求将数据显示在待做列表中,因此我们需要有一个数组(或者对象)、一个监控、当提交按钮点击后,用户所输入的内容能够 push 到数组中。同时我们需要一个 v-for, 将内容进行渲染。
  • 目标 3 要求待做任务完成后,可以移入已完成列表。因此我们需要一个监控和未完成数组,当点击完成时,该数据从已完成任务数组中删除,添入未完成数组。同时我们需要一个 v-for, 将内容进行渲染。
  • 目标 4 其实是重复了目标 3 ,只不过主次位置进行颠倒。
  • 目标 5 只需要将原先的数组、数值全部清空即可。

具体代码:

HTML:

<script src="https://unpkg.com/vue@next"></script>

<div id='todo'>
  <input v-model="context">
  <button @click="add">add</button>
  <button @click="clear">clear</button>
  <br>
  <br>
  <p>待完成</p>
  <template v-for="(item,key,i) in list" :key="item.id">
    <input value="item" type="checkbox" id="i"   @click="change(item)" >
    <label for="i" :class="{'del':item.isfalse}">{{item.context}}</label>
    
    <br>
  </template>
  <p>已完成</p>
    <template v-for="(item,key,i) in haslist" :key="item.id">
    <input value="item" type="checkbox" id="i"   @click="changeagain(item)" checked>
    <label for="i" :class="{'del':item.isfalse}">{{item.context}}</label>
    
    <br>
  </template>
</div>

CSS:

.list{
  float:left;
  margin-left:100px;
}
button {
  margin-left:10px;
}
.del{
  text-decoration:line-through;
}

JS:

const app = Vue.createApp({
  
  data(){
    return{
      context:"",
      list:[],
      id:1,
      haslist:[]
    }
  },
  
  methods:{
  
    add(){
      this.list.push({context:this.context,id:this.id,isfalse:false});
      this.id ++
    },
    clear(){
      this.context = "";
      this.list=[];
      this.id=1;
      this.haslist=[]
    },
  
    change(item){
      let darray = this.list.splice(this.list.indexOf(item),1);
      this.haslist.push(darray[0]);
      item.isfalse =!item.isfalse
    },
    
    changeagain(item){
      let array = this.haslist.splice(this.haslist.indexOf(item),1);
      this.list.push(array[0]);
      item.isfalse =!item.isfalse
    },
   
  }
   
});

app.mount('#todo');

CodePen:

See the Pen PoWXpzm by 善宝橘 (@liuluffy) on CodePen.

存在难点:

作为一个菜鸟,首先难住我的就是思路,一开始我并未想到直接将数据删除,而是想通过 v-if 来实现单个元素是否渲染。但试了半天(是真的半天)也没有找到解决方案。因为我一直把 v-if 的值设置在 data(){} 里,导致全局的值都改变掉。

后来找网上其他人的解决方案,看到可以直接删除数据来解决该问题。但删除数据对我来说也有问题,即我删除数据是用 splice方法,因此需要一个起始值,我一开始用的是 v-for 中的索引,但是就遇到了 key 值问题。不过我并没有调三方库生成唯一id。

所以我的难点就在于,缺少唯一 id,缺少 false 来添加 class(我想让被删除对象加 line-through)。我还不知道怎么操纵 v-for 的单一 item。

后来还是查资料,才突然发现,其实我因为往数组里推入 obj,对象中可以添加包括 idisfalse 等判定条件。另外我在 methods 中设置的函数可以传入单个 item 作为参数。

但是后面还是遇到了一个难点,一开始我把 obj 设在了 data(){},结果全局都变了。每一个 item 都一模一样,后来我只在 data(){} 中放最基本的数值,而把 obj 转到了下面的 methods:{} 中,结果发现可行。