摘要
vue3 中的 hooks就是函数的一种写法,就是将文件的一些单独功能的js代码进行抽离出来,放到单独的js文件中,或者说是一些可以复用的公共方法/功能。其实hooks 和 vue2 中的 mixin 有点类似,但是相对 mixins 而言, hooks 更清楚复用功能代码的来源, 更清晰易懂。
用hook改造组件
字典选择下拉框
<script setup name="DDemo" lang="ts">
import { onMounted, ref } from 'vue';
// 模拟调用接口
function getRemoteData() {
return new Promise<any[]>((resolve) => {
setTimeout(() => {
resolve([
{
key: 1,
name: '苹果',
value: 1,
},
{
key: 2,
name: '香蕉',
value: 2,
},
{
key: 3,
name: '橘子',
value: 3,
},
]);
}, 3000);
});
}
const optionsArr = ref<any[]>([]);
onMounted(() => {
getRemoteData().then((data) => {
optionsArr.value = data;
});
});
</script>
<template>
<div>
<a-select :options="optionsArr" />
</div>
</template>
<style lang="less" scoped></style>
看起来很简单是吧,忽略我们模拟调用接口的代码,我们用在ts/js部分的代码才只有6行而已,看起来根本不需要什么封装。
如果我们把所有的意外情况
都考虑到的话,代码就会变得很臃肿了。
<script setup name="DDemo" lang="ts">
import { onMounted, ref } from 'vue';
// 模拟调用接口
function getRemoteData() {
return new Promise<any[]>((resolve, reject) => {
setTimeout(() => {
// 模拟接口调用有概率出错
if (Math.random() > 0.5) {
resolve([
{
key: 1,
name: '苹果',
value: 1,
},
{
key: 2,
name: '香蕉',
value: 2,
},
{
key: 3,
name: '橘子',
value: 3,
},
]);
} else {
reject(new Error('不小心出错了!'));
}
}, 3000);
});
}
const optLoading = ref(false);
const optionsArr = ref<any[]>([]);
function initSelect() {
optLoading.value = true;
getRemoteData()
.then((data) => {
optionsArr.value = data;
})
.catch((e) => {
// 请求出线错误时将错误信息显示到select中,给用户一个友好的提示
optionsArr.value = [
{
key: -1,
value: -1,
label: e.message,
disabled: true,
},
];
})
.finally(() => {
optLoading.value = false;
});
}
onMounted(() => {
initSelect();
});
</script>
<template>
<div>
<a-select :loading="optLoading" :options="optionsArr" />
</div>
</template>
代码直接来到了22
行,虽说用户体验确实好了不少
封装下拉框hook
import { onMounted, reactive, ref } from 'vue';
// 定义下拉框接收的数据格式
export interface SelectOption {
value: string;
label: string;
disabled?: boolean;
key?: string;
}
// 定义入参格式
interface FetchSelectProps {
apiFun: () => Promise<any[]>;
}
export function useFetchSelect(props: FetchSelectProps) {
const { apiFun } = props;
const options = ref<SelectOption[]>([]);
const loading = ref(false);
/* 调用接口请求数据 */
const loadData = () => {
loading.value = true;
options.value = [];
return apiFun().then(
(data) => {
loading.value = false;
options.value = data;
return data;
},
(err) => {
// 未知错误,可能是代码抛出的错误,或是网络错误
loading.value = false;
options.value = [
{
value: '-1',
label: err.message,
disabled: true,
},
];
// 接着抛出错误
return Promise.reject(err);
}
);
};
// onMounted 中调用接口
onMounted(() => {
loadData();
});
return reactive({
options,
loading,
});
}
在组件中调用
<script setup name="DDemo" lang="ts">
import { useFetchSelect } from './hook';
// 模拟调用接口
function getRemoteData() {
return new Promise<any[]>((resolve, reject) => {
setTimeout(() => {
// 模拟接口调用有概率出错
if (Math.random() > 0.5) {
resolve([
{
key: 1,
name: '苹果',
value: 1,
},
{
key: 2,
name: '香蕉',
value: 2,
},
{
key: 3,
name: '橘子',
value: 3,
},
]);
} else {
reject(new Error('不小心出错了!'));
}
}, 3000);
});
}
// 将之前用的 options,loading,和调用接口的逻辑都抽离到hook中
const selectBind = useFetchSelect({
apiFun: getRemoteData,
});
</script>
<template>
<div>
<!-- 将hook返回的接口,通过 v-bind 绑定给组件 -->
<a-select v-bind="selectBind" />
</div>
</template>
代码行数直接又从20
行降到3
行
总结
日常开发任务中的一次案例分享