当前位置:Gxlcms > css > Vue的todoMVC使用详解

Vue的todoMVC使用详解

时间:2021-07-01 10:21:17 帮助过:55人阅读

这次给大家带来Vue的todoMVC使用详解,Vue的todoMVC使用注意事项有哪些,下面就是实战案例,一起来看一下。

这个示例是模仿官网示例样式和功能用我自己的方式写的,基本上没有看官网的源码,只参考自定义指令。让我们一步步来探讨一下。官网demo

要实现的功能

  1. 单条添加todo

  2. 单条删除todo

  3. 双击编辑todo

  4. 单条todo已完成相应样式状态改变

  5. 全部todo是已完成相应样式状态改变

  6. 清除全部已完成todos

  7. 待办todos数量显示

  8. 所有todos,已完成todos,未完成todos筛选

单条添加todo

  1. <input type="text" class="todos_add" placeholder="What needs to be done?"
  2. @keyup.enter="addTodo($event.target)" //绑定enter事件
  3. ref="currentInput">//操作input元素使enter一下之后清空输入框内容
  1. data() {//一些初始化数据
  2. return {
  3. todolists: [],
  4. dataStatus: ["All", "Active", "Completed"],
  5. dataStatusIndex: 0,
  6. whichShow: true,
  7. defaultShow: true
  8. }
  9. },
  10. addTodo(e) { //添加todo
  11. var val = e.value
  12. if (val === "") {return} //如果输入内容为空则立即返回
  13. this.todoLists = this.todoLists.concat({//使用concat这个api添加todo
  14. value: val, //输入内容
  15. isEditing: false, //是否在编辑状态
  16. isActive: false, //删除X图标是否激活
  17. isChecked: false //是否已完成
  18. })
  19. this.$refs.currentInput.value = "" //按下enter添加todo之后把输入框value清零
  20. window.localStorage.setItem("content",JSON.stringify(this.todoLists))//使用localStorage以JSON格式存储数据
  21. },

把每条todo的对应状态都存在同一个对象当中,在操作改变todo状态的时候不会被统一处理,使得每条todo都有单独的状态。

单条删除todo

  1. <li class="content_todoList"
  2. v-for="(list,index) in todoLists"
  3. @mouseover="list.isActive = true" //鼠标移入todo改变对应todo的isActive状态
  4. @mouseleave="list.isActive=false" //鼠标移出todo改变对应todo的isActive状态
  5. <span class="el-icon-close content_todoList_delete"
  6. :class="{show: list.isActive}" //动态绑定class使鼠标移动到某一todo显示X图标
  7. @click="deleteTodo(index)"> //绑定删除单条todo事件
  8. </span>
  9. </li>
  1. deleteTodo(index) { //删除单条todo
  2. this.todoLists.splice(index, 1)//使用splice的api
  3. window.localStorage.setItem("content",JSON.stringify(todoLists)) //以JSON格式存储数据//localStorage存储数据
  4. },

在每个li元素上绑定了鼠标移入和移除的事件来动态的改变每个todo的isActive,然后再使用isActive动态显示class。

双击编辑todo&&单条todo已完成

  1. <input type="checkbox" class="checkBox"
  2. v-model="list.isChecked">//双向绑定isChecked
  3. <p class="content_todoList_main"
  4. @dblclick="toEdit(list)" //双击事件
  5. v-show="!list.isEditing" //切换元素
  6. :class="{deleted:list.isChecked}"> //动态绑定class该表已完成todo样式
  7. {{list.value}}
  8. </p>
  9. <input type="text" class="content_todoList_main main_input"
  10. v-model="list.value" //双向绑定可输入value
  11. v-show="list.isEditing" //切换元素
  12. v-todo-focus="list.value" //自定义指令,双击之后自动focus对焦
  13. @blur="unEdit(list)"> //绑定blur失去焦点事件
  1. methods: {
  2. toEdit(obj) { //使添加的todothing可编辑
  3. obj.isEditing = true
  4. },
  5. unEdit(obj) { //使添加的todothing不可编辑
  6. obj.isEditing = false
  7. },
  8. }
  9. directives: { //自定义focus指令,需要一个binding参数做判断
  10. "todo-focus": function (el, binding) {
  11. if (binding.value) {
  12. el.focus()
  13. }
  14. }
  15. }

通过每个todo里的isEditing属性控制show和是否可编辑两个状态,双击p之后改变当前todo的isEditing为true,从而显示为input,input失去焦点之后再通过blur事件改为false。

通过todo的idChecked来控制是否已完成,从而动态改变样式。

全部todos已完成

  1. <span
  2. class="icon-down el-icon-arrow-down" //使用element库做向下箭头icon
  3. v-show="todoLists.length>0" //通过todoLists控制是否显示向下箭头icon
  4. @click="selectAllTodos"></span> //全部已完成事件
  1. selectAllTodos() { //设置所有todo为已完成,使用map的api通过todo的isChecked属性操作
  2. this.todoLists.map(todo => todo.isChecked = todo.isChecked ? false : true)
  3. }

待办todos数量显示

  1. <p class="data_times" v-show="times === 0"> //times为0显示item,大于0显示items,细节注定成败
  2. <span>{{times}}</span>&nbsp item left
  3. </p>
  4. <p class="data_times" v-show="times > 0">
  5. <span>{{times}}</span>&nbsp items left</p>
  1. computed: {
  2. times() { //使用计算属性计算待办todos的次数
  3. let todoArr = this.todoLists
  4. let times = 0
  5. for (let i = 0; i < todoArr.length; i++) {
  6. if (todoArr[i].isChecked === false) {
  7. times++
  8. }
  9. }
  10. return times
  11. }
  12. },

使用了计算属性对todoLists计算,用for循环刷选出idChecked为true的累加,最后返回times。

清除全部已完成todos

  1. <p class="data_clearTodos"
  2. @click="clearTodos"
  3. v-show="times < todoLists.length"> //如果待办事件次数小于总todoLists长度就显示“clear completed”
  4. <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >clear completed</a>
  5. </p>
  6. <p class="data_clearTodos"
  7. @click="clearTodos"
  8. v-show="times === todoLists.length"> //如果待办事件次数等于总todoLists长度就显示“clear completed”
  9. <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></a>
  10. </p>
  1. clearTodos() { //清空已完成的todoLists,使用filter的api进行筛选
  2. this.todoLists = this.todoLists.filter(todo => todo.isChecked === false)
  3. window.localStorage.setItem("content",JSON.stringify(this.todoLists)) //以json格式存储数据
  4. },

如果待办todos的times小于todoLists长度,就证明有已完成的todo,就可以显示“clear completed”,如果相等就说明没有已完成的todo。

三种状态筛选

所有todos,已完成todos,未完成todos筛选

  1. <li class="content_todoList"
  2. v-show="defaultShow || (whichShow?list.isChecked:!list.isChecked)">
  3. <p class="data_status">
  4. <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow"
  5. :class="{active: index === dataStatusIndex}" //动态class实现tab切换
  6. v-for="(item ,index) in dataStatus" v-for循环
  7. @click="switchStatus(index)" //切换不同tab显示内容
  8. :key="index">
  9. {{item}}
  10. </a>
  11. </p>
  1. switchStatus(index) { //通过if条件判断操作
  2. this.dataStatusIndex = index
  3. if (this.dataStatus[index] === "Active") {
  4. this.defaultShow = false
  5. this.whichShow = false
  6. } else if (this.dataStatus[index] === "Completed") {
  7. this.defaultShow = false
  8. this.whichShow = true
  9. } else if (this.dataStatus[index] === "All") {
  10. this.defaultShow = true
  11. }
  12. },

我这里是同时if条件句判断操作,有点麻烦,目前还没有想出来其他方案。在li元素使用三元运算符和或运算符进行操作显示不同状态的todos。

完整代码

  1. <style>
  2. * {
  3. padding: 0;
  4. margin: 0;
  5. box-sizing: border-box;
  6. }
  7. input {
  8. outline: none;
  9. }
  10. ul,
  11. li,
  12. ol {
  13. list-style: none;
  14. }
  15. #app {
  16. width: 800px;
  17. height: 900px;
  18. margin: 0 auto;
  19. text-align: center;
  20. background-color: rgb(245, 245, 245);
  21. padding: 24px 0;
  22. }
  23. #app .header {
  24. font-size: 48px;
  25. }
  26. .content {
  27. width: 72%;
  28. margin: 0 auto;
  29. box-shadow: 0 3px 3px 2px rgba(0, 0, 0, 0.25);
  30. position: relative;
  31. }
  32. .icon-down {
  33. position: absolute;
  34. font-size: 24px;
  35. top: 16px;
  36. left: 16px;
  37. cursor: pointer;
  38. }
  39. #app .content .todos_add {
  40. width: 100%;
  41. height: 56px;
  42. padding: 16px 56px;
  43. font-size: 24px;
  44. border: 1px solid transparent;
  45. }
  46. .content_todoLists {
  47. position: relative;
  48. z-index: 3;
  49. }
  50. .content_todoList {
  51. display: flex;
  52. flex-direction: row;
  53. border-top: 1px solid #ccc;
  54. font-size: 24px;
  55. padding: 8px;
  56. background-color: white;
  57. align-items: center;
  58. }
  59. .checkBox {
  60. width: 20px;
  61. height: 20px;
  62. margin-left: 10px;
  63. }
  64. .content_todoList_main {
  65. flex: 1;
  66. text-align: left;
  67. margin-left: 16px;
  68. font-size: 20px;
  69. padding: 6px 0;
  70. }
  71. .main_input {
  72. position: relative;
  73. z-index: 1;
  74. }
  75. .content_todoList_delete {
  76. position: absolute;
  77. right: 16px;
  78. color: rgb(252, 55, 55);
  79. font-weight: 500;
  80. display: none;
  81. cursor: pointer;
  82. }
  83. .show {
  84. display: block;
  85. }
  86. .deleted {
  87. text-decoration-line: line-through;
  88. color: #bbb;
  89. }
  90. .show:hover {
  91. color: rgb(255, 0, 0);
  92. font-weight: 700;
  93. }
  94. ::-moz-placeholder {
  95. color: rgb(221, 218, 218);
  96. }
  97. ::-webkit-input-placeholder {
  98. color: rgb(221, 218, 218);
  99. }
  100. :-ms-input-placeholder {
  101. color: rgb(221, 218, 218);
  102. }
  103. .data {
  104. display: flex;
  105. justify-content: space-between;
  106. padding: 8px;
  107. font-size: 14px;
  108. font-weight: 300;
  109. color: rgb(145, 145, 145);
  110. }
  111. a {
  112. text-decoration: none;
  113. color: rgb(145, 145, 145);
  114. }
  115. .data_times {
  116. width: 73px;
  117. }
  118. .data_clearTodos {
  119. width: 142px;
  120. }
  121. .data_status a {
  122. display: inline-block;
  123. border: 1px solid transparent;
  124. border-radius: 2px;
  125. padding: 1px 4px;
  126. margin: 0 2px;
  127. }
  128. .data_status a:hover {
  129. border-color: #bbb;
  130. }
  131. .data_clearTodos a:hover {
  132. text-decoration-line: underline;
  133. }
  134. .active {
  135. box-shadow: 0 0 1px black;
  136. }
  137. </style>
  1. <body>
  2. <p id="app">
  3. <header class="header">todos</header>
  4. <p class="content">
  5. <span class="icon-down el-icon-arrow-down"
  6. v-show="todoLists.length>0"
  7. @click="selectAllTodos">
  8. </span>
  9. <input type="text" class="todos_add" placeholder="What needs to be done?"
  10. @keyup.enter="addTodo($event.target)"
  11. ref="currentInput">
  12. <ul class="content_todoLists">
  13. <li v-for="(list,index) in todoLists" class="content_todoList"
  14. @mouseover="list.isActive = true"
  15. @mouseleave="list.isActive=false"
  16. v-show="defaultShow || (whichShow?list.isChecked:!list.isChecked)">
  17. <input type="checkbox" class="checkBox" v-model="list.isChecked">
  18. <p class="content_todoList_main" @dblclick="toEdit(list)" v-show="!list.isEditing" :class="{deleted:list.isChecked}">
  19. {{list.value}}
  20. </p>
  21. <input type="text" class="content_todoList_main main_input"
  22. v-model="list.value"
  23. v-show="list.isEditing"
  24. v-todo-focus="list.value"
  25. @blur="unEdit(list)">
  26. <span class="el-icon-close content_todoList_delete" :class="{show: list.isActive}" @click="deleteTodo(index)"></span>
  27. </li>
  28. </ul>
  29. <p class="data" v-show="todoLists.length>0">
  30. <p class="data_times" v-show="times === 0">
  31. <span>{{times}}</span>&nbspitem left
  32. </p>
  33. <p class="data_times" v-show="times > 0">
  34. <span>{{times}}</span>&nbspitems left
  35. </p>
  36. <p class="data_status">
  37. <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" :class="{active:index === dataStatusIndex}" v-for="(item,index) in dataStatus" @click="switchStatus(index)" :key="index">
  38. {{item}}
  39. </a>
  40. </p>
  41. <p class="data_clearTodos" @click="clearTodos" v-show="times < todoLists.length">
  42. <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >clear completed</a>
  43. </p>
  44. <p class="data_clearTodos" @click="clearTodos" v-show="times === todoLists.length">
  45. <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></a>
  46. </p>
  47. </p>
  48. </p>
  49. </p>
  50. </body>
  51. <script>
  52. let vm = new Vue({
  53. el: "#app",
  54. data() {
  55. return {
  56. todoLists: [],
  57. dataStatus: ["All", "Active", "Completed"],
  58. dataStatusIndex: 0,
  59. whichShow: true,
  60. defaultShow: true
  61. }
  62. },
  63. computed: {
  64. times() { //使用计算属性计算待办todos的次数
  65. let todoArr = this.todoLists
  66. let times = 0
  67. for (let i = 0; i < todoArr.length; i++) {
  68. if (todoArr[i].isChecked === false) {
  69. times++
  70. }
  71. }
  72. return times
  73. }
  74. },
  75. methods: {
  76. toEdit(obj) { //使添加的todo可编辑
  77. obj.isEditing = true
  78. },
  79. unEdit(obj) { //使添加的todo不可编辑
  80. obj.isEditing = false
  81. },
  82. addTodo(e) { //添加todo
  83. var val = e.value
  84. if (val === "") {
  85. return
  86. } //如果输入内容为空则立即返回
  87. this.todoLists = this.todoLists.concat({ //使用concat这个api添加todo
  88. value: val, //输入内容
  89. isEditing: false, //是否在编辑状态
  90. isActive: false, //删除X图标是否激活
  91. isChecked: false //是否已完成
  92. })
  93. this.$refs.currentInput.value = "" //按下enter添加todo之后把输入框value清零
  94. window.localStorage.setItem("content", JSON.stringify(this.todoLists)) //使用localStorage以JSON格式存储数据
  95. },
  96. deleteTodo(index) { //删除todo
  97. this.todoLists.splice(index, 1)
  98. window.localStorage.setItem("content", JSON.stringify(this.todoLists)) //以json格式存储数据
  99. },
  100. switchStatus(index) { //试下下方三个状态切换,略麻烦
  101. this.dataStatusIndex = index
  102. if (this.dataStatus[index] === "Active") {
  103. this.defaultShow = false
  104. this.whichShow = false
  105. } else if (this.dataStatus[index] === "Completed") {
  106. this.defaultShow = false
  107. this.whichShow = true
  108. } else if (this.dataStatus[index] === "All") {
  109. this.defaultShow = true
  110. }
  111. },
  112. clearTodos() { //清空已完成的todoLists
  113. this.todoLists = this.todoLists.filter(todo => todo.isChecked === false)
  114. window.localStorage.setItem("content", JSON.stringify(this.todoLists)) //以json格式存储数据
  115. },
  116. selectAllTodos() { //设置所有todo为已完成
  117. this.todoLists.map(todo => todo.isChecked = todo.isChecked ? false : true)
  118. }
  119. },
  120. directives: { //自定义focus指令
  121. "todo-focus": function (el, binding) {
  122. if (binding.value) {
  123. el.focus()
  124. }
  125. }
  126. },
  127. created() {
  128. let myStorage = window.localStorage.getItem('content')
  129. this.todoLists = JSON.parse(myStorage) || [] //因为todoLists初始值是null,使用或运算符,如果为null设为空数组
  130. }
  131. })
  132. </script>

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

Angular2 父子组件通信方式

javascript的代码优化详解

以上就是Vue的todoMVC使用详解的详细内容,更多请关注Gxl网其它相关文章!

人气教程排行