VUE3学习第三天 学以致用 —-todolist 需求开发(二)

承接昨天编辑todo,接下来切换代办事项状态,对todolist进行完成,和未完成状态进行筛选!

切换代办事项状态

这件事情还是比较麻烦,分好几个子任务

分析:
第一个子任务,点击左上角的折线(checkbox,我们点击的是checkbox的label,为了美观这个checkbox不让他显示出来,给他一像素安排,点击label的时候就是点击checkbox,小妙招。),会对所有的待办事项标记完成或者未完成
第二个子任务:

  • 点击Active的时候会显示未完成的待办事项,
  • 点击Completed的时候会显示已完成的待办事项,
  • 点击All的时候会像是所有的待办事项,

第三个子任务:

  • 左下角显示条目数,
  • 右下角的按钮是清除已完成待办事项,
  • 当条目数为空时,隐藏线面操作栏

image.png

1. 子任务一:点击checkbox,改变所有待办事项完成状态

设置checkbox的id为toggle-all,然后label 的 for绑定checkbox id。完成点击label就是点击checkbox的操作。小技巧 label 绑定 input

<input id="toggle-all" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
复制代码

这个全选的checkbox会做两件事,一是他的是否选中状态,和todos里面所有待办事项的选中状态相关,这就是todos状态会影响全选状态,所以这里的全选状态(allDone)可以使用计算属性。二是,这个全选状态的变化,还会去设置todos里面的状态,所以说我们这里可以用到我们计算属性get set的写法。完成交互

这个全选在写很多全选控制的交互有很多,以后大家记得使用计算属性来做。checkbox table等

知识点: computed (get set) 使用场景

const useFilter = todos => {
  // 声明全选状态计算属性 allDone
  const allDone = computed({
    get () {
      // 当todos全部为true时u,计算属性为true
      return !todos.value.filter(todo => !todo.completed).length
    },
    set (value) {
      // 当计算属性被设置时,同步todos里面所有状态与计算属性保持一致
      todos.value.forEach(todo => {
        todo.completed = value
      })
    }
  })
  
  

  return {
    allDone
  }
}

复制代码

实现一个小细节,当待办事项选中是,todo文字加一条删除线,设置一个complated类,是否有此类由每条todo的complated的属性值决定,实现样式可以去源码查看实现

知识点: ** v-for,绑定class**

···
<ul class="todo-list">
    <li
      v-for="todo in todos"
      :key="todo"
      :class="{ editing: todo === editingTodo, completed: todo.completed }" // 这里使用complated类画横线
    >
 ···
复制代码

2. 子任务二:点击all active complated三个超链接的筛选

思路: 把过滤待办事项的函数定义到一个对象中,然后根据hash的值去对象中取不同状态的数据,当然对象的函数名称和hash值是一样的,这样就避免使用过多的if语句。

知识点:

  • computed, ref, onMounted注册事件 onUnmounted注销事件
  • 使用方法名和状态key有关联关系,也是一个值得学习的小技巧,当处于什么状态就可以直接执行该方法不需要太多if判断,很值得学习
    // 注册响应式数据:筛选状态类型,
    const type = ref('all')
    // 当类型改变,计算属性filteredTodos计算属性跟着发生改变
    const filteredTodos = computed(() => filter[type.value](todos.value))

    // 定义hash变化处理函数
    const onHashChange = () => {
      // 获取地址的hash值
      const hash = window.location.hash.replace('#/', '')
      // 当hash在filter对象中有值则赋值给type,没有则显示全部,因为链接上hash可以随便输入所以有一个if判断
      if(filter[hash]) {
        type.value = hash
      } else {
        type.value = 'all'
        window.location.hash = ''
      }
    }

    // 页面首次加载的时候,也要去获取onHashChange里面的数据,并注册hashchange事件
    onMounted(() => {
      window.addEventListener('hashchange', onHashChange)
      onHashChange()
    })
    // 组件卸载时注销事件
    onUnmounted(() => {
      window.removeEventListener('hashchange', onHashChange)
    })

复制代码

3. 子任务三,

  • 显示未完成待办事项的个数,

    定义一个计算属性remainingCount,获得filter.active的长度即可

    const remainingCount = computed(() => filter.active(todos.value).length)
    // 显示在左下角
    <strong>{{ remainingCount }}</strong> {{ remainingCount > 1 ? 'items' : 'item' }} left
    复制代码
  • 点击按钮删除已完成的事项,
    给点击按钮注册点击事件,点击的时候执行清除已完成事件方法removeCompleted,还有一个小功能,当待办事项里面没有已完成事项时,清除按钮不显示。使用v-show绑定数据控制按钮显示和隐藏。
    因为这个操作也是删除操作,所以应该在useRemove里面编写代码

   const useRemove = todos => {
       ···
       const removeCompleted = () => {
         todos.value = todos.value.filter(todo => !todo.completed)
       }

       return {
           remove,
           removeCompleted
       }
   }
复制代码
  • 如果没有待办事项就只显示待办事项文本框 和 如果没有已完成的待办事项 清除已完成按钮也要隐藏

计算属性count表示所有待办事项条目
使用v-show绑定计算属性count 隐藏main 和 footer,如果count > remianingCount则表示待办事项中有已完成待办事项,显示清除按钮

知识点: v-show

<section class="main" v-show="count">
···
<footer class="footer" v-show="count">
···
<button class="clear-completed" v-show="count > remainingCount" @click="removeCompleted">
Clear completed
</button>
复制代码

最后一个任务,把todoList存储到localStorage里面

我们可以创建一个utils文件夹,创建一个useLocalStorage.js文件,对localStorage进行封装,方便使用!

知识点: use函数,模块抽取,方便更多的组件复用

// useLocalStorage.js
// 转化为json对象
function parse (str) {
  let value
  try {
    value = JSON.parse(str)
  } catch {
    value = null
  }
  return value
}
// 转化为json字符串
function stringify (obj) {
  let value
  try {
    value = JSON.stringify(obj)
  } catch {
    value = null
  }
  return value
}

export default function useLocalStorage () {
    // 存操作
  function setItem (key, value) {
    value = stringify(value)
    window.localStorage.setItem(key, value)
  }
   // 去操作
  function getItem (key) {
    let value = window.localStorage.getItem(key)
    if (value) {
      value = parse(value)
    }
    return value
  }
    // 返回存取操作
  return {
    setItem,
    getItem
  }
}
复制代码

定义完工具函数,我们应该怎么使用呢?
我们知道在添加,删除,编辑待办事项todos都会发生变化,如果我们在这些use函数里面都使用存储操作,这样代码耦合性太高,太麻烦了,还记得第一天学习的watchEffect么,对,可以使用它,监听todos变化,进行数据存储。

import useLocalStorage from './utils/useLocalStorage'
···
const storage = useLocalStorage()
const useStorage = () => {
  const KEY = 'TODOKEYS'
  // 在本地存储中取数据完成数据todos的初始化
  const todos = ref(storage.getItem(KEY) || [])
  // 当todos变化事存储todos
  watchEffect(() => {
    storage.setItem(KEY, todos.value)
  })
  return todos
}
复制代码

总结

项目Todolist: github.com/zelixag/tod…

使用一个简单的todolist的需求,对vue核心API ref, computed, watchEffect,computed的get set使用场景等等…的熟悉,最关键的是compositionAPI的使用,我们使用五个use函数完成todolist的需求,践行compositionAPI思想。我们把不同的功能分别放到不同函数中去实现,这样代码逻辑更清晰。最后还做了一个useLocalStorage,把use函数封装到了一个单独的模块中,将来可以在其他组件中重用。你可以看一下下面compositionAPI 和 optionsApi对比图,哪个代码结构清晰,好维护一幕了然。

我们把不同逻辑的代码拆分到use 函数中,同一功能代码放到一个函数中,而且更方便组件之间重用代码,这是compositionAPI比options好的地方,好了todolist的结束了,vue学习第三天,感受vue的使用的方便就到这,接下来我们去认识一下 vue3的响应式系统原理,敬请期待!!!

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享