基础案例:TodoMVC

学习目标:

  • 掌握 Vue 中常用的实例选项
  • 掌握 Vue 常用指令

案例介绍


需求说明


开始

下载模板

  1. # 下载模板到本地,重命名为 todomvc-vue
  2. # --depth=1 表示只下载最后一次的 commit,其它历史记录不要,这样可以提高下载速度
  3. git clone https://github.com/tastejs/todomvc-app-template.git todomvc-vue --depth=1
  4. # 切换到 todomvc-vue 目录中,安装依赖项
  5. cd todomvc-vue
  6. npm install
  7. # 打开 todomvc-vue 中的 index.html 预览模板

任务列表

  1. ...
  2. <template v-if="todos.length > 0">
  3. <section class="main">
  4. <input
  5. id="toggle-all"
  6. class="toggle-all"
  7. type="checkbox"
  8. v-bind:checked="getToggleAllStatus()"
  9. @change="handleToggleAll">
  10. <label for="toggle-all">Mark all as complete</label>
  11. <ul class="todo-list">
  12. <!-- These are here just to show the structure of the list items -->
  13. <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
  14. <li
  15. v-for="(item, index) in todos"
  16. v-bind:class="{completed: item.done}">
  17. <div class="view">
  18. <input class="toggle" type="checkbox" v-model="item.done">
  19. <label>{{ item.title }}</label>
  20. <button @click="handleRemoveTodo(index)" class="destroy"></button>
  21. </div>
  22. <input class="edit" value="Rule the web">
  23. </li>
  24. </ul>
  25. </section>
  26. <!-- This footer should hidden by default and shown when there are todos -->
  27. <footer class="footer">
  28. <!-- This should be `0 items left` by default -->
  29. <span class="todo-count"><strong>0</strong> item left</span>
  30. <!-- Remove this if you don't implement routing -->
  31. <ul class="filters">
  32. <li>
  33. <a class="selected" href="#/">All</a>
  34. </li>
  35. <li>
  36. <a href="#/active">Active</a>
  37. </li>
  38. <li>
  39. <a href="#/completed">Completed</a>
  40. </li>
  41. </ul>
  42. <!-- Hidden if no completed items are left ↓ -->
  43. <button class="clear-completed">Clear completed</button>
  44. </footer>
  45. </template>
  46. ...

添加任务

  1. ...
  2. <input
  3. class="new-todo"
  4. placeholder="What needs to be done?"
  5. v-on:keyup.enter="handleAddTodo">
  6. ...
  1. ...
  2. methods: {
  3. ...
  4. handleAddTodo (e) {
  5. const {target} = e // target 就是触发该事件的 DOM
  6. const {value} = target
  7. const {todos} = this
  8. // 拿到数组最后一个元素的 id + 1 就可以得到一个唯一不重复的 id
  9. // 当数组是空的时候根本就没有最后一项,所以 todos[todos.length - 1] 的结果就是 undefined
  10. // undefined.id 不就报错了吗?
  11. const lastTodo = todos[todos.length - 1]
  12. // 如果有最后一个元素,则让该元素.id + 1,否则默认给个 1
  13. const id = lastTodo ? lastTodo.id + 1 : 1
  14. if (value.trim().length !== 0) {
  15. this.todos.push({
  16. id, // 当 key 和 value 名字一样的时候,可以简写, id 等价于 id: id
  17. title: value,
  18. done: false
  19. })
  20. // 操作 DOM 清空文本框
  21. target.value = ''
  22. }
  23. },
  24. ...
  25. }
  26. ...

切换所有任务的完成状态

  1. ...
  2. <input
  3. id="toggle-all"
  4. class="toggle-all"
  5. type="checkbox"
  6. v-bind:checked="getToggleAllStatus()"
  7. @change="handleToggleAll">
  8. ...
  1. ...
  2. methods: {
  3. ...
  4. handleToggleAll (e) {
  5. const checked = e.target.checked
  6. this.todos.forEach((item) => {
  7. item.done = checked
  8. })
  9. },
  10. getToggleAllStatus () {
  11. let status = true
  12. this.todos.forEach(item => {
  13. if (item.done === false) {
  14. status = false
  15. }
  16. })
  17. return status
  18. },
  19. ...
  20. }
  21. ...

删除单个任务

  1. ...
  2. <button @click="handleRemoveTodo(index)" class="destroy"></button>
  3. ...
  1. ...
  2. methods: {
  3. ...
  4. handleRemoveTodo (delIndex) {
  5. this.todos.splice(delIndex, 1)
  6. },
  7. ...
  8. }
  9. ...

删除所有已完成任务

  1. ...
  2. <button
  3. class="clear-completed"
  4. @click="handleClearAllDone">Clear completed</button>
  5. ...
  1. ...
  2. methods: {
  3. ...
  4. handleClearAllDone () {
  5. for (let i = 0; i < this.todos.length; i++) {
  6. const item = this.todos[i]
  7. if (item.done === true) {
  8. this.todos.splice(i, 1)
  9. i-- // 在遍历过程中删除元素之后,让索引减一次,防止有漏网之鱼
  10. }
  11. }
  12. },
  13. ...
  14. }
  15. ...

显示所有剩余未完成任务数

方法方式:

  1. ...
  2. <span class="todo-count"><strong>{{ getRemaining() }}</strong> item left</span>
  3. ...
  1. ...
  2. methods: {
  3. ...
  4. getRemaining () {
  5. let count = 0
  6. this.todos.forEach(item => {
  7. if (item.done === false) {
  8. count++
  9. }
  10. })
  11. return count
  12. }
  13. ...
  14. }
  15. ...

计算属性方法:

  1. ...
  2. <strong>{{ remaining }}</strong> item left</span>
  3. ...
  1. ...
  2. computed: {
  3. ...
  4. remaining () {
  5. let count = 0
  6. this.todos.forEach(item => {
  7. if (item.done === false) {
  8. count++
  9. }
  10. })
  11. return count
  12. }
  13. ...
  14. }
  15. ...

持久化存储

  1. ...
  2. new Vue({
  3. ...
  4. data: {
  5. ...
  6. todos: JSON.parse(window.localStorage.getItem('todos') || '[]')
  7. ...
  8. },
  9. ...
  10. watch: {
  11. ...
  12. todos: {
  13. handler () { // 固定的 handler ,当 todos 发生改变会自动调用 handler 方法
  14. // 本地存储只能存储字符串,所以这里要把数组转成字符串再存储
  15. window.localStorage.setItem('todos', JSON.stringify(this.todos))
  16. },
  17. deep: true // deep 配置为 true 表示深度监视
  18. }
  19. ...
  20. },
  21. ...
  22. })
  23. ...