Published on

Vue3 - 自定义Hooks函数

Authors
  • avatar
    Name
    Deng Hua
    Twitter

目录

什么是hooks?

在Vue3中,Hook是一种函数编写风格,本质上是从文件中封装某些独立的函数代码。

Vue3借鉴了React中的这一特性,用于复用函数组件中的状态逻辑和副作用,从而提高代码的可重用性。

Hook的优点

  • Hooks 将组件封装为独立的逻辑。其内部的状态、逻辑可以与外部组件共享响应式效果。

  • 自定义hook函数类似于Vue2中的mixin,方便易用。

  • 具有高内聚性和松散耦合性。

自定义Hooks具有的特性

  1. 它们的功能是重复的,这样它们需要被提取为独立的hook文件。

  2. 函数名或文件名应以use开头,如:useXX

  3. 导入时,应显式解构并公开响应式变量或方法。

const { nameRef,Fn }= useXX()

hooks 和 utils 之间的区别

相同点:通过hooks和utils函数的封装,可以实现组件之间的代码共享和复用,提高了代码的复用性和可维护性。

差异:

  1. 表现方式不同

Hooks在组件层面功能包装状态或函数(Hook函数等)。utils一般用来封装逻辑功能,没有组件相关的东西。

  1. 数据的响应式

如果涉及到refreactivecomputehooks中这些API,则数据是reactive的;而 utils 只是普通方法的简单提取,因此不是响应式的。

  1. 适用范围不同

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.