一个ToolList案例上一个例子组件化
抽取EditTodo
<template>
<input
type="text"
:value="modelValue"
@input="onInputChange"
v-bind="$attrs"
/>
</template>
<script>
export default {
// 属性声明
props: {
modelValue: {
type: String,
default: ''
},
},
methods:{
onInputChange(e){
// 派发事件
this.$emit('update:modelValue',e.target.value)
}
}
}
</script>
<style lang="scss" scoped>
</style>
抽取TodoItem
<template>
<li :class="{ completed: todo.completed, editing: todo === editedTodo }">
<div class="view">
<input type="checkbox" v-model="todo.completed" />
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button @click="removeTodo(todo)">X</button>
</div>
<!-- 编辑代办 -->
<input
type="text"
class="edit"
v-model="todo.title"
v-todo-foucs="todo === editedTodo"
@blur="doneEdit(todo)"
@keyup.enter="doneEdit(todo)"
@keyup.escape="cancelTodo(todo)"
/>
</li>
</template>
<script>
import {reactive,toRefs} from "vue"
export default {
props: { //输入
todo: {
type: Object,
required: true //必须为true
},
editTodo:Object
},
// 输出
emits:['remove-todo','update:edited-todo'],
setup(props,{emit}) {
const state=reactive({
beforeEditCache: '', //缓存编辑前的title
});
function editTodo(todo) {
state.beforeEditCache = todo.title;
// 告诉父亲去做
emit('update:edited-todo',todo)
}
function cancelTodo(todo) {
todo.title = state.beforeEditCache;
emit('update:edited-todo',null)
}
function doneEdit(todo) {
emit('update:edited-todo',null)
}
//派发事件
function removeTodo(todo) {
// emit事件
emit('remove-todo',todo)
}
return {
...toRefs(state),
removeTodo,
editTodo,
cancelTodo,
doneEdit
};
},
directives:{
"todo-foucs":(el,{value},vnode)=>{
if(value){
el.focus();
}
}
}
};
</script>
<style scoped>
.completed label {
text-decoration: line-through;
}
.edit,
.editing .view {
display: none;
}
.view,
.editing .edit {
display: block;
}
</style>
抽取Filter
<template>
<!-- 过滤 -->
<p class="filters">
<span
v-for="item in items"
:key="item.value"
@click="$emit('update:modelValue',item.value)"
:class="{selected:modelValue===item.value}">{{item.value}}</span>
</p>
</template>
<script>
export default {
props:['items','modelValue'],
emits:['update:modelValue']
}
</script>
<style scoped>
.filters >span{
padding:2px 4px;
margin-right: 4px;
border: 1px solid transparent;
}
.filters >span.selected{
border-color: rgba(173,47,47,0.2);
}
</style>
组件1逻辑提取 useFilter
import { reactive,computed } from "vue";
// 过滤器
const filters={
all(todos){
return todos
},
active(todos){
return todos.filter(todo=>!todo.completed)
},
completed(todos){
return todos.filter(todo=>todo.completed)
},
}
export function useFilter(todos){
const filterState=reactive({
filterItems:[
{label:'All',value:'all'},
{label:'Active',value:'active'},
{label:'Completed',value:'completed'},
],
visibility:'all',
filterdTodos:computed(()=>{
return filters[filterState.visibility](todos.value)
})
});
return filterState;
}
组件2逻辑提取useTodos
// 逻辑控制
// 缓存操作
import {ref,watchEffect} from "vue"
const todoStorage={
fetch(){
let todos=JSON.parse(localStorage.getItem('vue3-todos')||'[]')
todos.forEach((todo,index) =>{
todo.id=index+1
})
return todos
},
save(todos){
localStorage.setItem("vue3-todos",JSON.stringify(todos));
}
}
export function useTodos(state){
const todos= ref(todoStorage.fetch())//初始化
function addTodo() {
todos.value.push({
id: todos.value.length + 1,
title: state.newTodo,
completed: false,
});
// 状态置空
state.newTodo = "";
}
function removeTodo(todo) {
todos.value.splice(todos.value.indexOf(todo), 1);
}
watchEffect(()=>{
todoStorage.save(todos.value)
})
return{todos ,addTodo,removeTodo}
}