这是我参与2022首次更文挑战的第20活动详情查看:2022首次更文挑战」。
vue2.0
父类
父类提供给一个改变传给子类,测试子类watch监听的反应 子类
子类通过实现一个入门必备项目todoList进行增删改查的功能。
<template>
<div class="main">
<div class="header">---------------子类功能-------------------</div>
<div>
<input type="text" v-model="currentValue" />
<button @click="handleSearch">搜索</button>
<button @click="handleAdd">添加</button>
</div>
<ul class="list">
<li v-for="(item, index) in respository" :key="index">
{{ item }}
<button @click="handleDelete(index)">删除</button>
</li>
</ul>
</div>
</template>
<script>
import { fetchTodoList } from './api.js';
export default {
props: {
user: {
type: Number,
required: true,
},
},
data() {
return {
currentValue: '',
respository: [],
searchQuery: '1232',
};
},
computed: {
repositoriesMatchingSearchQuery() {
return this.respository.find(item => item.name === this.searchQuery);
},
},
watch: {
user: 'getUserRespository',
},
mounted() {
this.getUserRespository();
},
methods: {
async getUserRespository() {
this.respository = await fetchTodoList();
},
handleSearch() {
const target = this.respository.find(item => item === this.currentValue);
this.respository = [target];
},
handleAdd() {
this.respository.push(this.currentValue);
this.currentValue = '';
},
handleDelete(key) {
this.respository = this.respository.filter((item, index) => key !== index);
},
},
};
</script>
Vue3.0
Vue2.0中,随着功能的增加,组件变得越来越复杂,越来越难维护,而难以维护的根本原因是Vue的API设计迫使开发者使用watch,computed,methods选项组织代码,而不是实际的业务逻辑。
另外Vue2.0缺少一种较为简洁的低成本的机制来完成逻辑复用,虽然可以minxis完成逻辑复用,但是当mixin变多的时候,会使得难以找到对应的data、computed或者method来源于哪个mixin,使得类型推断难以进行。
看上面的2.0代码你发现数据,方法,属性并且将多个功能写在一起后,写起来超级简单
2.1 get请求模块改造
首先改造第一阶段,获取todoList的请求数据,并且设置在respository的仓库列表里面,牵涉到请求数据,生命周期,data数据,方法。将这几个vue2.0散在不同地方的组织成一个复用可抽离的模块。
首先是请求使用Ts改造成js请求api方法,主要是添加类型控制
import axios from "axios";
interface DataType {
userId: number;
id: number;
title: string;
completed: boolean;
}
export async function fetchTodoList(): Promise<string[]> {
const res = await axios.get("https://jsonplaceholder.typicode.com/todos");
return res?.data?.slice(0, 4).map((item: DataType) => item.title);
}
其次是功能setup的改造,可以理解为setup 选项是一个接收 props 和 context 的函数,。此外,我们将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板,也就是说他可以喝vue2.0的其他特性共存,也就是computed可以单独写,也可以在setup里面写。首先使用ref创建一个响应式对象,vue3.0不是所有的属性都自动响应式,需要自动设置,然后包装成一个响应式对象暴露出来,使用.value的形式调用,主要是为了防止值类型不能被响应监听到。
<script lang="ts">
import { fetchTodoList } from "../api";
import { defineComponent, onMounted, Ref, ref } from "vue";
export default defineComponent({
setup() {
const respository: Ref<string[]> = ref([]);
const getUserRespository = async () => {
respository.value = await fetchTodoList();
};
onMounted(getUserRespository);
return {
respository,
getUserRespository,
};
},
});
</script>
2.2 添加备忘录信息功能模块改造
同样的改造方法,定义所需要的data属性,复用上面的仓库属性
//添加备忘录
const currentValue: Ref<string> = ref("");
const handleAdd = () => {
respository.value.push(currentValue.value);
currentValue.value = "";
};
return {
currentValue,
handleAdd,
}
2.3删除备忘录功能模块改造
//删除备忘录功能模块
const handleDelete = (key: number) => {
respository.value = respository.value.filter(
(item, index) => key !== index
);
};
2.4 搜索功能模块改造
搜索所需要的find会有个undefined返回值类型,这时候我们直接将值断言成string即可。
//搜索功能模块
const handleSearch = () => {
const target: string | undefined = respository.value.find(
(item) => item === currentValue.value
);
respository.value = [target as string];
};
2.4 setup模块
setup就被分成4个相对独立的功能模,在下一步我们将继续独立拆分模块
<script lang="ts">
import { fetchTodoList } from "../api";
import { defineComponent, onMounted, Ref, ref, toRefs, watch } from "vue";
export default defineComponent({
props: {
user: {
type: Number,
required: true,
},
},
setup(props) {
//请求模块
const { user } = toRefs(props);
const respository: Ref<string[]> = ref([]);
const getUserRespository = async () => {
respository.value = await fetchTodoList();
};
onMounted(getUserRespository);
watch(user, getUserRespository);
//添加备忘录
const currentValue: Ref<string> = ref("");
const handleAdd = () => {
respository.value.push(currentValue.value);
currentValue.value = "";
};
//删除备忘录功能模块
const handleDelete = (key: number) => {
respository.value = respository.value.filter(
(item, index) => key !== index
);
};
//搜索功能模块
const handleSearch = () => {
const target: string | undefined = respository.value.find(
(item) => item === currentValue.value
);
respository.value = [target as string];
};
return {
respository,
getUserRespository,
currentValue,
handleAdd,
handleDelete,
handleSearch,
};
},
});
</script>
2.6 终极拆分方案
对于其他的逻辑关注点我们也可以这样做,但是你可能已经在问这个问题了——这不就是把代码移到 setup 选项并使它变得非常大吗?嗯,确实是这样的。这就是为什么我们要在继续其他任务之前,我们首先要将上述代码提取到一个独立的组合式函数中。
新建四个功能模块的ts文件方法
分别为
useGetRespository
import { toRefs, Ref, ref, onMounted, watch } from "vue";
import { fetchTodoList } from "../api";
export default function useGetRespository(props: any): any {
//请求模块
const { user } = toRefs(props);
const respository: Ref<string[]> = ref([]);
const getUserRespository = async () => {
respository.value = await fetchTodoList();
};
onMounted(getUserRespository);
watch(user, getUserRespository);
return {
respository,
getUserRespository,
};
}
useAddRespository
import { Ref, ref } from "vue";
export default function useAddRespository(respository: Ref<string[]>): any {
//添加备忘录
const currentValue: Ref<string> = ref("");
const handleAdd = () => {
respository.value.push(currentValue.value);
currentValue.value = "";
};
return {
currentValue,
handleAdd,
};
}
useDeleteRespository
import { Ref } from "vue";
export default function useDeleteRespository(respository: Ref<string[]>): any {
//删除备忘录功能模块
const handleDelete = (key: number) => {
respository.value = respository.value.filter(
(item, index) => key !== index
);
};
return {
handleDelete,
};
}
useAddRespository
import { Ref } from "vue";
export default function useAddRespository(
respository: Ref<string[]>,
currentValue: Ref<string>
): any {
//搜索功能模块
const handleSearch = () => {
const target: string | undefined = respository.value.find(
(item) => item === currentValue.value
);
respository.value = [target as string];
};
return {
handleSearch,
};
}
现在我们有了四个个单独的功能模块,接下来就可以开始在组件中使用它们了
<script lang="ts">
import { defineComponent } from "vue";
import useAddRepository from "../hooks/useAddRepository";
import useDeleteRepository from "../hooks/useDeleteRespository";
import useGetRepository from "../hooks/useGetRespository";
import useSearchRepository from "../hooks/useSearchRespository";
export default defineComponent({
props: {
user: {
type: Number,
required: true,
},
},
setup(props) {
const { respository, getUserRespository } = useGetRepository(props);
const { currentValue, handleAdd } = useAddRepository(respository);
const { handleDelete } = useDeleteRepository(respository);
const { handleSearch } = useSearchRepository(respository, currentValue);
return {
respository,
getUserRespository,
currentValue,
handleAdd,
handleDelete,
handleSearch,
};
},
});
</script>