前置知识:生命周期函数在vue3不再是配置式的,而是一个普通函数。
步骤:
- 确认筛选条件是通过hash值
- 声明filter函数:通过hash改变的筛选得到对应的任务列表
- userFilter.js文件:用于任务的筛选。
- 使用一个响应式变量visibilityRef 来取得筛选条件hash
- 使用声明周期函数onMounted,监听hash的变化
- 使用声明周期函数onUnmounted,销毁hash的监听
- 明确筛选后的任务列表,是需要通过初始的任务列表todosRef和筛选条件visibilityRef 来计算得到的。而todosRef在另外一个js,因此需要在App.vue文件中传递过来。
- 根据页面需要展示的信息来计算出对应的内容。
filter函数
/**
* 任务筛选
* @param {*} todos 任务列表
* @param {*} visibility 筛选条件
*/
export function filter(todos,visibility = 'all') {
if(visibility === 'all'){
return todos;
}
else if (visibility === 'active'){
return todos.filter((it) => !it.completed);
}
else if(visibility === 'completed') {
console.log(todos.filter((it) => it.completed),'todos.filter((it) => it.completed)')
return todos.filter((it) => it.completed);
}
throw new Error ("invalid visibility value");
}
userFilter.js
// 生命周期函数在vue3不再是配置式的,而是一个普通函数
import {
computed,
onMounted,
onUnmounted,
ref
} from "vue";
import {
filter
} from "../util/todoStorage";
const validHash = ['all', 'active', 'completed']
export default function useFilter(todosRef) {
const visibilityRef = ref("all") // 筛选条件
// 根据哈希值取得筛选条件
const onHashChange = () => {
// 通过调用 replace(/#\/?/, ""),我们使用正则表达式将 # 或 #/ 替换为空字符串,从而得到去除 # 或 #/ 的结果。
const hash = location.hash.replace(/#\/?/, "");
if (validHash.includes(hash)) {
// 存在的
visibilityRef.value = hash;
} else {
location.hash = '';
visibilityRef.value = 'all';
}
}
// 1. 组件挂载完成的生命周期函数
onMounted(() => {
// hashchange: 此方法是用于监听hash的变化
return window.addEventListener('hashchange', onHashChange);
});
// 2. 组件销毁过后的生命周期函数
onUnmounted(() => {
window.removeEventListener("hashchange", onHashChange);
});
// 根据筛选条件去筛选列表任务,进而计算出应该展示的任务列表
const filteredTodosRef = computed(() => {
return filter(todosRef.value, visibilityRef.value);
})
// 计算出未完成的数量:用于页面左下角的待完成的数量
const remainingRef = computed(() => {
return filter(todosRef.value, 'active').length;
})
// 计算已完成的数量:用于页面判断是否展示右下角的 Clear completed按钮
const completedRef = computed(() => {
return filter(todosRef.value, 'completed').length;
})
return {
visibilityRef,
filteredTodosRef,
remainingRef,
completedRef
}
}
App.vue
<template>
<link rel="stylesheet" href="./style.css" />
<div id="app">
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" autofocus="" autocomplete="off" placeholder="What needs to be done?" v-model="newTodoRef"
@keyup.enter="addTodo" />
</header>
<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox"/>
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li class="todo" v-for="todo in filteredTodosRef" :key="todo.id" :class="{completed:todo.completed}">
<div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed" />
<label>{{todo.title}}</label>
<button class="destroy"></button>
</div>
<input class="edit" type="text" />
</li>
</ul>
</section>
<footer class="footer">
<span class="todo-count">
<strong>{{remainingRef}}</strong>
<span>item{{remainingRef === 1 ?'' : 's'}} left</span>
</span>
<ul class="filters">
<li><a href="#/all" :class="{selected: visibilityRef === 'all'}">All</a></li>
<li><a href="#/active" :class="{selected: visibilityRef === 'active'}">Active</a></li>
<li><a href="#/completed" :class="{selected: visibilityRef === 'completed'}">Completed</a></li>
</ul>
<button class="clear-completed" v-show="completedRef">
Clear completed
</button>
</footer>
</section>
</div>
</template>
<script>
import useTodoList from "./composition/useTodoList"
import useNewTodo from "./composition/useNewTodo"
import useFilter from "./composition/useFilter";
export default {
setup() {
const { todosRef } = useTodoList();
return {
todosRef,
...useNewTodo(todosRef),
...useFilter(todosRef)
}
}
}
</script>