- Published on
Vue3 - 自定义Hooks函数
- Authors
- Name
- Deng Hua
目录
什么是hooks?
在Vue3中,Hook是一种函数编写风格,本质上是从文件中封装某些独立的函数代码。
Vue3借鉴了React中的这一特性,用于复用函数组件中的状态逻辑和副作用,从而提高代码的可重用性。
Hook的优点
Hooks 将组件封装为独立的逻辑。其内部的状态、逻辑可以与外部组件共享响应式效果。
自定义hook函数类似于Vue2中的mixin,方便易用。
具有高内聚性和松散耦合性。
自定义Hooks具有的特性
它们的功能是重复的,这样它们需要被提取为独立的hook文件。
函数名或文件名应以use开头,如:useXX。
导入时,应显式解构并公开响应式变量或方法。
const { nameRef,Fn }= useXX()
hooks 和 utils 之间的区别
相同点:通过hooks和utils函数的封装,可以实现组件之间的代码共享和复用,提高了代码的复用性和可维护性。
差异:
- 表现方式不同
Hooks在组件层面功能包装状态或函数(Hook函数等)。utils一般用来封装逻辑功能,没有组件相关的东西。
- 数据的响应式
如果涉及到ref
、reactive
、compute
、hooks
中这些API,则数据是reactive
的;而 utils 只是普通方法的简单提取,因此不是响应式的。
- 适用范围不同
Hooks封装可以提取组件状态和生命周期方法,可以跨多个组件共享和复用; utils一般指用于实现通用操作或提供特定功能的辅助函数或工具方法。
概括
utils 是通用的实用函数,而 hooks 是 utils 的一种封装,用于共享组件中的状态逻辑和副作用。
通过使用Hook,您可以简化代码并使其更具可读性和可维护性。
Hooks函数封装示例
示例1:数据导出(useDownload)
import { ElNotification } from "element-plus";
/**
@description 接收数据流以生成blob,创建链接,下载文件
@param {any} data 导出的文件blob数据
@param {String} tempName 导出的文件名
@param {Boolean} isNotify 是否提示导出消息
@param {String} fileType 导出文件格式
*/
interface useDownloadParam {
data: any;
tempName: string;
isNotify?: boolean;
fileType?: string;
}
export const useDownload = async ({ data, tempName, isNotify = true, fileType = ".xlsx" }: useDownloadParam) => {
if (isNotify) {
ElNotification({
title: "Kind reminder",
message: "Large data size might slow down the downloading process, please be patient!",
type: "info",
duration: 3000
});
}
try {
const blob = new Blob([data]);
if ("msSaveOrOpenBlob" in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType);
const blobUrl = window.URL.createObjectURL(blob);
const exportFile = document.createElement("a");
exportFile.style.display = "none";
exportFile.download = ${tempName}${fileType};
exportFile.href = blobUrl;
document.body.appendChild(exportFile);
exportFile.click();
document.body.removeChild(exportFile);
window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.log(error);
}
};
在组件中使用:
<script setup lang="ts">
import { useDownload } from "@/hooks/useDownload";
const userForm = reactive({})
const userListExport = () => {
new Promise(resolve => {
$Request({
url: $Urls.userListExport,
method: "post",
data: userForm,
responseType: "blob"
}).then((res: any) => {
useDownload({
data: res.data,
tempName:"User List",
});
resolve(res);
});
});
};
</script>
示例2:加减计数(useCount)
import { computed, ref, Ref } from "vue"
type CountResultProps = {
count:Ref<number>;
multiple:Ref<number>;
increase:(delta?:number)=>void;
decrease:(delta?:number)=> void;
}
export default function useCount(initValue = 1):CountResultProps{
const count = ref(initValue)
const multiple = computed(
() => count.value * 2
)
const increase = (delta?:number):void =>{
if(typeof delta !== 'undefined'){
count.value += delta
}else{
count.value += 1
}
}
const decrease = (delta?:number):void=>{
if(typeof delta !== "undefined"){
count.value -= delta
}else{
count.value -= 1
}
}
return {
count,
increase,
decrease,
multiple
}
}
组件中使用的useCount
:
<template>
<p>count:{{count}}</p>
<p>times:{{multiple}}</p>
<div>
<button @click="increase(1)">Increase 1</button>
<button @click="decrease(1)">Decrease 1</button>
</div>
</template>
<script setup lang="ts">
import useCount from "@/hooks/useCount"
const { count, multiple, increase, decrease } = useCount(10)
</script>
示例3:获取鼠标触发点坐标(useMousePosition)
import { Ref, onMounted, onUnmounted, ref } from "vue";
interface MousePosition {
x: Ref<number>,
y: Ref<number>
}
export default function useMousePosition(): MousePosition {
const x = ref(0);
const y = ref(0);
const updateMouse = (e: MouseEvent) => {
x.value = e.pageX;
y.value = e.pageY;
}
onMounted(() => {
document.addEventListener('click', updateMouse);
})
onUnmounted(() => {
document.removeEventListener('click', updateMouse);
})
return { x, y }
};
<template>
<div>
<p>X: {{ x }}</p>
<p>Y: {{ y }}</p>
</div>
</template>
<script lang="ts">
import useMousePosition from '@/hooks/useMousePosition'
const { x, y } = useMousePosition();
</script>
End.