Let's VUE[3]

313 阅读2分钟

vue-todolist

功能列表

  • input输入文字按回车添加事项

  • 双击事项显示编辑输入框,按回车键或失去焦点确认修改,按ESC键取消修改

  • 点击x删除事项

  • 点击勾选切换事项状态,未完成/已完成

  • 全选/反选

  • 切换all、active、completed显示对应的事项

  • 保存数据到localstorage

结构样式

使用vue改写

  1. 引入vue.js

  2. 创建vue实例

let vm = new Vue({
  el: '.todoapp',
  data: {
    list: [],
    title: '',
    editTodoIndex: -1,
    beforeTitle: ''
  },
  methods: {
    addTodo(){},
    removeTodo(){},
    editTodo(){},
    editDone(){},
    editCancel(){}
  }
});
  1. 渲染数据
<ul class="todo-list" ref="todoList">
  //事项有3种状态
  //1.未完成没有class
  //2.已完成有class completed
  //3.编辑状态有class editing
  <li 
    v-for="(item,index) in list"
    :class="{completed: item.checked,editing: index === editTodoIndex}"
  >
    <div class="view">
      <input 
        class="toggle" 
        type="checkbox" 
        v-model="item.checked"
      />
      <label @dblclick="editTodo(index)">{{item.title}}</label>
      <button 
        class="destroy" 
        @click="removeTodo(item)"
      ></button>
    </div>
    <input 
      class="edit" 
      v-model='item.title'
      @blur="editDone(item)" 
      @keyup.13="editDone(item)" 
      @keyup.esc="editCancel(item)" 
    />
  </li>
</ul>
  1. 添加功能
<input class="new-todo" placeholder="请输入内容" @keyup.13 = 'addTodo' v-model='title' />

addTodo(){
  if(this.title.trim() === '') return;
  this.list.push({
    title: this.title,
    id: Date.now(),
    checked: false
  });
  this.title = '';
}
  1. 删除功能
removeTodo(todo){
  let index = this.list.findIndex(item => item === todo);
  this.list.splice(index,1)
}
  1. 编辑功能
//添加ref
<input 
  ref='edit' 
  class="edit" 
  v-model='item.title'
  @blur="editDone(item)" 
  @keyup.13="editDone(item)" 
  @keyup.esc="editCancel(item)" 
/>

editTodo(index){
  this.editTodoIndex = index;
  //编辑的时候显示修改输入框,使用refs获取dom设置焦点
  this.$nextTick( () => {
    this.$refs.edit[index].focus()   
  })
  // 编辑时候保存一下title
  this.beforeTitle = this.list[index].title;
}
editDone(todo){
  //当title为空删除
  if(!todo.title.trim()){
    this.removeTodo(todo);
  }
  this.editTodoIndex = -1;
  this.beforeTitle = '';
}
editCancel(todo) {
  this.editTodoIndex = -1;
  todo.title = this.beforeTitle;
}
  1. 全选功能
<input class="toggle-all" v-model='isCheckedAll' type="checkbox" />

computed: {
  isCheckedAll: {
    get(){
      return this.list.length ? this.list.every(item => item.checked) : false
    },
    set(newValue){
      this.list.forEach(item => item.checked = newValue)
    }
  }
}
  1. 计算未选中多少条
<footer class="footer">
  <span class="todo-count">
    <strong>{{uncheckedLen}}</strong>
    <span>条未选中</span>
  </span>
  ...
</footer>

computed: {
  ...
  uncheckedLen(){
    return this.list.filter(item => !item.checked).length;
  }
}
  1. 当todolist为空不显示footer
<footer class="footer" v-show="list.length">
...
</footer>
  1. todo类型切换
let filterActive = {
  all(list){
    return list
  },
  active(list){
    return list.filter(item => !item.checked)
  },
  completed(list){
    return list.filter(item => item.checked)
  }
}

// 当hash值改变时触发的事件
window.onhashchange = function (){
    let hashVal = window.location.hash;
    if(hashVal){
        hashVal = hashVal.slice(2);
        hashVal = filterActive[hashVal] ? hashVal : 'all'
    }else{
        hashVal = 'all';
    }
    // 改变实例下的过滤条件
    vm.hash = hashVal;
}
window.onhashchange();
//过滤todolist
computed: {
  isCheckedAll: {
    get(){
      return this.filterList.length ? this.filterList.every(item => item.checked) : false
    },
    set(newValue){
      this.filterList.forEach(item => item.checked = newValue)
    }
  },
  uncheckedLen(){
    return this.filterList.filter(item => !item.checked).length;
  },
  filterList(){
    return filterActive[this.hash](this.list);
  }
}
//修改渲染列表
<li
  :class="{completed: item.checked,editing: index === editTodoIndex}"
  v-for="(item,index) in filterList"
>

//添加hash
data: {
  ...
  hash: 'all'
}

//判断hash值添加selected样式
<footer class="footer" v-show="list.length">
  ...
  <ul class="filters">
    <li><a href="#/all" :class="{selected:hash === 'all'}">All</a></li>
    <li><a href="#/active" :class="{selected:hash === 'active'}">Active</a></li>
    <li><a href="#/completed" :class="{selected:hash === 'completed'}">Completed</a></li>
  </ul>
</footer>
  1. 把数据保存到localstorage
//添加修改删除修改数据都需要保存,所以使用watch深度监控list变化然后保存

watch: {
  list:{
    handler(){
      localStorage.setItem('todoListData', JSON.stringify(this.list));
    },
    deep:true
  }
}
//获取数据

let data = JSON.parse(localStorage.getItem('todoListData')) || [];

let vm = new Vue({
  ...
  data: {
    ...
    list: data
  }
})

完整示例

点击这里查看完整示例