Vue3.x 中 hooks 函数封装和使用

news2024/12/24 18:16:31

一、hooks 是什么

vue3 中的 hooks 就是函数的一种写法,就是将文件的一些单独功能的 js 代码进行抽离出来进行封装使用。

它的主要作用是 Vue3 借鉴了 React 的一种机制,用于在函数组件中共享状态逻辑和副作用,从而实现代码的可复用性。

注意:其实 hooks 和 vue2 中的 mixin 有点类似,但是相对 mixins而言, hooks 更清楚复用功能代码的来源, 更清晰易懂。

二、hooks 的优点

  • hooks 作为独立逻辑的组件封装,其内部的属性、函数等和外部组件具有响应式依附的作用。
  • 自定义 hook 的作用类似于 vue2 中的 mixin 技术,使用方便,易于上手。
  • 使用 Vue3 的组合 API 封装的可复用,高内聚低耦合。

三、自定义 hook 需要满足的规范

  1. 具备可复用功能,才需要抽离为 hooks 独立文件
  2. 函数名/文件名以 use 开头,形如: useXX
  3. 引用时将响应式变量或者方法显式解构暴露出来;

示例如下:

const{ nameRef, Fn } = useXX()

四、hooks 和 utils 区别

  • 相同点:

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

  • 异同点:
  1. 表现形式不同:hooks 是在 utils 的基础上再包一层组件级别的东西(钩子函数等);utils 一般用于封装相应的逻辑函数,没有组件的东西;
  2. 数据是否具有响应式:hooks 中如果涉及到 ref,reactive,computed 这些 api 的数据,是具有响应式的;而 utils 只是单纯提取公共方法就不具备响应式;
  3. 作用范围不同:hooks 封装,可以将组件的状态和生命周期方法提取出来,并在多个组件之间共享和重用;utils 通常是指一些辅助函数或工具方法,用于实现一些常见的操作或提供特定功能。
  • 总结:

        utils 是通用的工具函数,而 hooks 是对 utils 的一种封装,用于在组件中共享状态逻辑和副作用。

        通过使用 hooks,您可以简化代码,并使其更具可读性和可维护性。

五、hooks 和 mixin 区别

  • 相同点:

        hooks 和 mixin,都是常用代码逻辑抽离手段,方便进行代码复用;

  • 异同点:
  1. 语法和用法不同Hooks 是在 Vue 3 的 Composition API 中引入的一种函数式编程的方式,而 Mixins 是在 Vue 2 中的一种对象混入机制。Hooks 使用函数的方式定义和使用,而 Mixins 则是通过对象的方式进行定义和应用。
  2. 组合性和灵活性不同:Hooks 允许开发者根据逻辑功能来组合代码,封装为自定义 Hook 函数,提高代码复用率。而 Mixins 在组件中的属性和方法会与组件本身的属性和方法进行合并,可能会导致命名冲突或不可预料的行为。
  3. 响应式系统不同:Vue 3 的 Composition API 使用了一个新的响应式系统,可以通过 reactive 和 ref 来创建响应式数据,可以更精确地控制组件的更新和依赖追踪。而 Mixins 使用 Vue 2 的响应式系统,对数据的追踪和更新较为简单,可能存在一些性能上的问题。
  4. 生命周期钩子不同:在 Vue 3 的 Composition API 中,可以使用 onMounted、onUpdated 等钩子函数来替代 Vue 2 中的生命周期钩子,可以更灵活地管理组件的生命周期。Mixins 依然使用 Vue 2 的生命周期钩子。
  • mixins 的优缺点

优点:组件中相同代码逻辑复用;

缺点:

  1. 变量来源不明确:变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
  2. 命名冲突:多个 mixins 的生命周期会融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突。
  3. 滥用会造成维护问题:mixins 和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个 mixins,一个 mixins 也可以被多个组件引用)。

注:VUE3 提出的 Composition API 旨在解决这些问题。mixins 的缺点是 Composition API 背后的主要动因之一,Composition API 受到 React Hooks 的启发。

HooksMixins
定义hook是通过Composition API引入的一种新特性,类似于React的hook。mixin是一种对Vue组件进行扩展的方式。
功能可以组织和重用逻辑。在组件中,我们可以创建和重用复杂的逻辑代码,使得组件的逻辑更加清晰和可维护它可以将组件的代码封装到一个可复用的模块。常用于将公用的代码片段进行抽离,实现复用,使得组件的逻辑更加清晰和可维护。
使用使用setup方法,可以组织和复用各类逻辑使用mixin属性,加载公用的代码片段。
组织代码效果使用Hooks,我们可以让组件的逻辑函数按功能组织,使得组件的逻辑结构更加清晰。使用Mixins,我们可以将组件的各个生命周期的相关函数统一放在一起,但这样做可能会使得组件的逻辑函数分散在各个生命周期中。
冲突问题Hooks允许我们命名冲突的功能,从而避免了各种命名冲突。Mixins可能会导致函数名冲突。如果两个mixin中包含相同的函数,会导致后一个mixin的函数覆盖先前的函数。
难以追踪的来源Hooks使用的是函数,所以如果不加注释,可能不太容易找到其来源。在Mixin中,我们可以在每个使用了公用代码片段的地方都用注释表明这段代码的来源,有助于我们更好地追踪和维护代码。
Debug困难度Hooks有更好的Stack Trace,可以提供更优秀的debug体验。对mixins的支持可能会出现在运行时错误的情况下,无法找到那块代码出错的问题,从而导致调试困难。

hooks 代码:

useCount.ts 函数示例:

import{ ref, onMounted, computed } from'vue';

export default function useCount{

    constcount = ref(0);

    constdoubleCount = computed(
        ()=>count.value * 2
    );

    constincrease = (delta) =>{
        return count.value + delta;
    }

    return{
        count,
        doubleCount,
        increase
    };

}

useCount 在组件中调用:

import useCount from"@/hooks/useCount";
const {(count, doubleCount, increase)} = useCount;
const newCount = increase(10); // 输出: 10

 Mixins 的代码:

export default const countMixin = {
    data() {
        return{
            count: 0
        };
    },
    computed: {
        doubleCount() {
            return this.count * 2;
        }
    },
    methods: {
        increase(delta){
            return this.count + delta;
        }
    };
}

Mixins 在组件中调用:

<scriptsetuplang="ts">
import count Mixin from'@/mixin/countMixin'
export default{
    mixins: [countMixin],
    mounted() {
        console.log(this.doubleCount) // 输出: 0
        constnewCount = this.setIncrease(10) // 输出: 10
    },
    methods: {
        setIncrease(count) {
            this.increase(count)
        },
    },
}
</script>

这两个示例展示了使用 Hooks 和 Mixins 的代码风格和组织方式的不同。Hooks 使用函数式的方式来定义逻辑和状态,而 Mixins 则是通过对象的方式进行组合和共享代码。

Vue3 自定义 Hooks 是组件下的函数作用域的,而 Vue2 时代的 Mixins 是组件下的全局作用域。全局作用域有时候是不可控的,就像 var 和 let 这些变量声明关键字一样,const 和 let 是 var 的修正。Composition Api 正是对 Vue2 时代 Option Api 高耦合和随处可见 this 的黑盒的修正,Vue3 自定义 Hooks 是一种进步。

六、hooks 函数封装示例

  • 示例 1:数据导出(useDownload)

useDownload 函数封装:

import{ ElNotification } from'element-plus'
/**
* @description 接收数据流生成 blob,创建链接,下载文件
* @param {any} data 导出的文件blob数据 (必传)
* @param {String} tempName 导出的文件名 (必传)
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
* */
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: '温馨提示',
            message: '如果数据庞大会导致下载缓慢哦,请您耐心等待!',
            type: 'info',
            duration: 3000,
        })
    }
try{
    constblob = newBlob([data])
    // 兼容 edge 不支持 createObjectURL 方法
    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()
    // 去除下载对 url 的影响
    document.body.removeChild(exportFile)
    window.URL.revokeObjectURL(blobUrl)
} catch(error) {
    console.log(error)
}
}

useDownload 在组件中使用:

<scriptsetuplang="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:"用户列表"
            });
            resolve(res);
    });
});
};
</script>
  • 示例 2:获取鼠标触发点坐标(useMousePosition)

useMousePosition 函数封装:

import { ref, onMounted, onUnmounted, Ref } from'vue'

interface MousePosition {
    x: Ref<number>;
    y: Ref<number>;
}

export default function useMousePosition(): MousePosition{
constx = ref(0)
consty = 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 }
}

useMousePosition 在组件中使用:

<template>
<div>
<p>X: {{ x }}</p>
<p>Y: {{ y }}</p>
</div>
</template>
<scriptlang="ts">
import useMousePosition from'@/hooks/useMousePosition'
const{ x, y } = useMousePosition();
</script>
  • 示例 3:封装一个发送短信验证码倒计时 hooks

useMousePosition 函数封装:

<template>
    <div>
        <input type="text" placeholder="请输入验证码" v-model="code">
        <button @click="sendCode">{{ sendBtnText }}</button>
    </div>
</template>

<script lang='ts' setup>
import { ref } from 'vue';

const code = ref('')
const sendBtnText = ref('发送验证码')
const countDownNum = ref(60)
const sendCode = () => {
    //这里省略调用发送短信接口逻辑,省略禁止点击逻辑
    sendBtnText.value = countDownNum.value + 's'
    const timer = setInterval(() => {
        countDownNum.value--
        sendBtnText.value = countDownNum.value + 's'
        if (countDownNum.value === 0) {
            clearInterval(timer)
            sendBtnText.value = '发送验证码'
            countDownNum.value = 60
        }
    }, 1000)
}
</script>
<style lang="css">
button {
    font-size: 14px;
    background: #23A7F2;
    color: #fff;
}

input {
    height: 30px;
}
</style>

这里逻辑很简单,就是点击发送按钮开启定时器出现倒计时的功能

假如我们还有其它地方用到发送短信页面,我们可以将短信发送封装成一个组件。但是如果其它页面想要使用的发送短信页面和这个组件不一样的话,我们就需要将它的逻辑抽离封装成一个 hooks 函数了,下面我们就将这个倒计时功能封装成一名为 useCountDown 的 hooks

import { Ref, ref } from "vue";
export default (
  downNum: number
): ({ sendBtnText: Ref<string>, sendCode: () => void }) => {
  const sendBtnText = ref("发送验证码");
  const countDownNum = ref(downNum);

  //这里省略调用发送短信接口逻辑,省略禁止点击逻辑
  const sendCode = () => {
    sendBtnText.value = countDownNum.value + "s";
    const timer = setInterval(() => {
      countDownNum.value--;
      sendBtnText.value = countDownNum.value + "s";
      if (countDownNum.value === 0) {
        clearInterval(timer);
        sendBtnText.value = "发送验证码";
        countDownNum.value = 60;
      }
    }, 1000);
  };
  return { sendBtnText, sendCode };
};

逻辑和上面一样,只不过是返回了一个按钮要显示的文本以及一个点击发送验证码调用的函数。然后在组件中使用

import { ref } from "vue";
import useCountDown from "../hooks/useCountDown";
const code = ref("");
const { sendBtnText, sendCode } = useCountDown(60);

七、hooks 函数封装细节归纳

1.hooks 函数接收参数写法;

写法 1:参数通过 props 接收,先定义参数类型,内部再解构;

export function commonRequest(params: Axios.AxiosParams) {
    let{ url, method, data, responseType = 'json'} = params
}

写法 2:接收传参对象,先设置默认值,再定义参数类型

interface DeprecationParam {
    from: string;
    replacement: string;
    type: string;
}
export const useDeprecated = ({ from, replacement, type= 'API' }: DeprecationParam) =>{}

2.解构重命名写法

// setup中

const { list:goodsList, getList:getGoodsList } = useList(axios.get('/url/get/goods'))
const { list:recommendList, getList:getRecommendList } = useList(
axios.get('/url/get/recommendGoods')
)

3.KeyboardEvent 为鼠标按键类型

export const useEscapeKeydown = (handler: (e: KeyboardEvent) => void) =>{}

八、总结

Vue2 时代 Option Api ,data、methos、watch.....分开写,这种是碎片化的分散的,代码一多就容易高耦合,维护时来回切换代码是繁琐的!

Vue3 时代 Composition Api,通过利用各种 Hooks 和自定义 Hooks 将碎片化的响应式变量和方法按功能分块写,实现高内聚低耦合。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1274202.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[笔记]dubbo发送接收

个人笔记 consumer 主要使用ThreadlessExecutor实现全consumer的全双工通讯。consumer创建本次请求的requestId用于将response和request匹配。 然后分以下几步完成一次请求发送并接收结果&#xff1a; 槽&#xff1a;发送消息前将用于接收结果的executor放到一个map中存储 发…

AI落地现状:没有mission、业务零碎、连2B还是2C都在摇摆

最近我私下问某TOP AI 2.0公司的核心产品负责人&#xff0c;你们现在主要是2C还是2B&#xff1f; 他说的第一句话是&#xff0c;“整体没有misson”。 结合近期的各种信息&#xff0c;给大家说下行业最前沿的内幕。 1、据说&#xff0c;某1、2位大佬的AI认知&#xff0c;其实并…

最大公约数的C语言实现xdoj31

时间限制: 1 S 内存限制: 1000 Kb 问题描述: 最大公约数&#xff08;GCD&#xff09;指某几个整数共有因子中最大的一个&#xff0c;最大公约数具有如下性质&#xff0c; gcd(a,0)a gcd(a,1)1 因此当两个数中有一个为0时&#xff0c;gcd是不为0的那个整数&#xff…

Linux的dev/vda1文件满了导致MySQL无法写入

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、dev/vda1文件介绍 二、排查过程 三、总结 前言 今天查看两个月前上线的小项目&#xff0c;发现运行非常慢&#xff0c;而且增…

正则表达式及文本三剑客grep sed awk

正则表达式 1.元字符 . //匹配任意单个字符&#xff0c;可以是个汉字 [yang] //匹配范围内的任意单个字符 [^y] //匹配处理指定范围外的任意单个字符 [:alnum:] //字母和数字 [:alpha:] //代表…

周报:css相关扩展知识

目录 1. 扩展知识&#xff1a;浮动盒子的排列位置 浮动盒子常见排列特点&#xff1a; 浮动盒子扩展特点&#xff1a; 2.扩展知识:行高的取值 line-height常见取值&#xff1a; 行高的取值的方式&#xff1a; 两个方式的区别&#xff1a; 3.扩展知识&#xff1a;body背景…

无公网IP环境如何远程访问本地内网搭建的Emby影音库服务器

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

kubeadm快速搭建k8s高可用集群

1.安装及优化 1.1基本环境配置 1.环境介绍 &#xff08;1&#xff09;.高可用集群规划 主机名ip地址说明k8s-master01192.168.2.96master节点k8s-master02192.168.2.97master节点k8s-master03192.168.2.98master节点k8s-node01192.168.2.99node节点k8s-node02192.168.2.100n…

uc_12_进程间通信IPC_有名管道_无名管道

1 内存壁垒 进程间天然存在内存壁垒&#xff0c;无法通过交换虚拟地址直接进行数据交换&#xff1a; 每个进程的用户空间都是0~3G-1&#xff08;32位系统&#xff09;&#xff0c;但它们所对应的物理内存却是各自独立的。系统为每个进程的用户空间维护一张专属于该进程的内存映…

JavaWeb服务器详解和后端分层解耦

JavaWeb HTTP协议请求数据格式响应数据格式协议解析 Web服务器请求响应请求参数的接收响应 分层解耦IOC&DI入门IOC详解 HTTP协议 超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 特点&#xff1a; 基于TCP协议&#xff1a;面向连接&#xff0c;安全 …

第三方实验室LIMS管理系统源码,asp.net LIMS源码

LIMS实验室信息管理系统源码 LIMS系统的功能根据实验室的规模和任务而有所不同&#xff0c;其系统主要功能包括:系统维护、基础数据编码管理&#xff0c;样品管理、数据管理、报告管理、报表打印、实验材料管理、设备管理等。它可以取代传统的手工管理模式而给检测实验室带来巨…

aspera传输方案怎么样,需要选择aspera替代方案吗

Aspera传输方案是一种高速、可靠的文件传输解决方案&#xff0c;适用于需要大规模传输大文件或数据集的企业和组织。Aspera采用UDP协议及自己开发的FASP协议进行加速传输&#xff0c;能够在高延迟、高丢包网络环境下实现稳定快速的传输。 Aspera传输方案具有以下优点&#xff1…

【【带Micro Blaze的 AXI GPIO 控制LED实验】】

带Micro Blaze的 AXI GPIO 控制LED实验 AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。AXI GPIO 是一个软核&#xff08;Soft IP&#xff09;&#xff0c;是由用户通过配置芯片的逻辑资源来实现的一个功能模块。 实验任务 &#xff1a; 本章的实验任务是通过调用…

颠覆性语音识别:单词级时间戳和说话人分离

vbenjs/vue-vben-admin[1] Stars: 19.7k License: MIT Vue Vben Admin 是一个免费开源的中后台模板&#xff0c;使用最新的 vue3、vite4 和 TypeScript 等主流技术进行开发。该项目提供了现成的中后台前端解决方案&#xff0c;并可用于学习参考。 使用先进的前端技术如 Vue3/…

11 款顶级的免费 iPhone 数据恢复软件

iPhone 拥有巨大的存储容量。您可以在 iPhone 设备上存储图像、文档和视频等数据。有时&#xff0c;您的 iPhone 会发生许多意外事件&#xff0c;例如意外删除&#xff0c;从而导致数据丢失。这里有 11 个最好的免费 iPhone 数据恢复软件&#xff0c;您可以免费下载&#xff0c…

基于社区电商的Redis缓存架构-用户分享内容的分页列表缓存延迟构建以及异步通知缓存重建

分页列表缓存的延迟构建 首先&#xff0c;先来讲一下业务场景&#xff0c;用户会在 APP 中去分享内容&#xff0c;那么假如用户分享的是美食菜谱内容&#xff0c;在用户分享之后&#xff0c;先将这个美食菜谱的内容作为 k-v 进行缓存&#xff0c;但是呢&#xff0c;其实对于用…

Microsoft Remote Desktop高效、安全、稳定的远程办公解决方案

在今天的数字化时代&#xff0c;Remote Desktop远程办公已成为许多人的日常生活。无论你是因为工作需要&#xff0c;还是因为在家中需要访问公司服务器&#xff0c;微软远程连接软件都是一个理想的选择。 微软远程连接软件Remote Desktop是一款高效、安全、稳定的远程办公解决…

Clickhouse Join

ClickHouse中的Hash Join, Parallel Hash Join, Grace Hash Join https://www.cnblogs.com/abclife/p/17579883.html https://clickhouse.com/blog/clickhouse-fully-supports-joins-full-sort-partial-merge-part3 总结 本文描述并比较了ClickHouse中基于内存哈希表的3种连接…

Nginx基线检查

扩展知识: Nginx主配置文件:/etc/nginx/nginx.conf 这是Nginx的主要配置文件,用于配置全局的设置、HTTP块、事件处理、邮件等内容。 打开并编辑配置文件 vim /etc/nginx/nginx.conf 一、关于禁止显示服务器版本号和操作系统版本信息: 简介: 在错误页面和响应头中显示…

2024年软考高级信息系统项目管理师备考攻略

软考高级信息系统项目管理师是一项合格性考试&#xff0c;考试内容相对有限&#xff0c;因此真题的重复率较高。下一次考试与上一次相比&#xff0c;重复率不高&#xff0c;但与之前所有年份的真题相比&#xff0c;重复率较高。在这几次考试中&#xff0c;我认为最重要的是务必…