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

一、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) // 输出: 0constnewCount = 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 = blobUrldocument.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.mzph.cn/news/196739.shtml

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

相关文章

Python类型注解必备利器:typing模块解读指南

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python 3.5版本后引入的typing模块为Python的静态类型注解提供了支持。这个模块在增强代码可读性和维护性方面提供了帮助。本文将深入探讨typing模块&#xff0c;介绍其基本概念、常用类型注解以及使用示例&am…

Web前端 ---- 【vue】vue 组件传值(props、全局事件总线、消息的订阅与发布)

目录 前言 父子组件 父传子 子传父 全局事件总线 什么叫全局事件总线 如何创建全局事件总线 如何在组件上获取到这个全局vc对象 最常用的创建全局事件总线 兄弟组件 消息订阅与发布 安装 使用 爷孙组件 前言 在上篇文章我们介绍了父子组件之间的传值通信&#xff…

写 SVG 动画必看!SVG系列文章3-动画标签

1、SMIL animation概览 SMIL不是指「水蜜梨」&#xff0c;而是Synchronized Multimedia Integration Language&#xff08;同步多媒体集成语言&#xff09;的首字母缩写简称&#xff0c;是有标准的。本文所要介绍的SVG动画就是基于这种语言。 SMIL允许你做下面这些事情&#…

单基因也可以这么做,经典生信文章思路,简单易复现更可升级

今天给同学们分享一篇生信文章“High Expression of PSRC1 Predicts Poor Prognosis in Lung Adenocarcinoma”&#xff0c;这篇文章发表在J Cancer期刊上&#xff0c;影响因子为3.9。 结果解读&#xff1a; LUAD和LUSC中PSRC1表达高 从TCGA数据集中&#xff0c;有535名LUAD患…

Redis实战篇笔记(最终篇)

Redis实战篇笔记&#xff08;七&#xff09; 文章目录 Redis实战篇笔记&#xff08;七&#xff09;前言达人探店发布和查看探店笔记点赞点赞排行榜 好友关注关注和取关共同关注关注推送关注推荐的实现 总结 前言 本系列文章是Redis实战篇笔记的最后一篇&#xff0c;那么到这里…

集成开发环境PyCharm的使用【侯小啾python基础领航计划 系列(三)】

集成开发环境 PyCharm 的使用【侯小啾python基础领航计划 系列(三)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

从零开始实现神经网络(二)_CNN卷积神经网络

参考文章: 介绍卷积神经网络1 介绍卷积神经网络2 在过去的几年里&#xff0c;关于卷积神经网络&#xff08;CNN&#xff09;的讨论很多&#xff0c;特别是因为它们彻底改变了计算机视觉领域。在这篇文章中&#xff0c;我们将建立在神经网络的基本背景知识的基础上&#xff0c;探…

2023/12/4JAVAmysql

流程控制函数 部门不存在,这个不存在交集,所有没出现

【Docker实操】创建一个Node服务

一、安装node 请查看阿里云官网教程&#xff1a;如何快速部署Node.js项目。&#xff08;注意要在根目录操作&#xff09;apt install nodejs、apt install npm执行上面两个命名来安装 安装完成后&#xff0c;执行node -v、npm -v&#xff0c;如果出现版本&#xff0c;就是安装…

你好!斐波那契查找【JAVA】

1.有幸遇见 斐波那契查找算法&#xff0c;也称黄金分割查找算法&#xff0c;是一种基于斐波那契数列的查找算法。与二分查找类似&#xff0c;斐波那契查找也是一种有序查找算法&#xff0c;但它的查找点不是中间位置&#xff0c;而是根据斐波那契数列来确定&#xff0c;因此又称…

工程师业余生活之制作蔬菜盆景

工程师业余生活陶冶情操之制作蔬菜盆景 &#xff08;蔬 果 盆 景 裝 點 家 居&#xff09; 市場上好多蔬菜瓜果,稍用一些心思,將一些價廉的蔬果製成別致的盆景, 便能使家居充滿自然氣息&#xff0c;增添生活情趣。以下介紹幾種製作方法&#xff1a; 【番薯盆景】 (番薯又名地…

4K-Resolution Photo Exposure Correction at 125 FPS with ~8K Parameters

MSLTNet开源 | 4K分辨率125FPS8K的参数量&#xff0c;怎养才可以拒绝这样的模型呢&#xff1f; 错误的曝光照片的校正已经被广泛使用深度卷积神经网络或Transformer进行广泛修正。尽管这些方法具有令人鼓舞的表现&#xff0c;但它们通常在高分辨率照片上具有大量的参数数量和沉…

avue页面布局 api 引用

展示 index.vue <template><basic-container><avue-crud :option"option":table-loading"loading":data"data":page"page":permission"permissionList":search.sync"search":before-closebefore…

GitHub项目推荐-Deoldify

有小伙伴推荐了一个老照片上色的GitHub项目&#xff0c;看了简介&#xff0c;还不错&#xff0c;推荐给大家。 项目地址 GitHub - SpenserCai/sd-webui-deoldify: DeOldify for Stable Diffusion WebUI&#xff1a;This is an extension for StableDiffusions AUTOMATIC1111 w…

多多情报通:助力拼多多商家选品运营的数据分析工具

多多情报通&#xff08;原名多多参谋&#xff09;是一款专为拼多多商家设计的数据分析工具&#xff0c;旨在帮助商家进行选品、运营优化和提高销售业绩。通过多多情报通&#xff0c;商家可以更好地了解市场趋势、消费者需求和竞争对手状况&#xff0c;从而制定有效的运营策略。…

RH850P1X芯片学习笔记-Pin Functions

文章目录 Pin Connection Diagrams术语定义 Pin ListPort OverviewIntroductionFunctional OverviewPort CategoryOperation Mode运行模式 Port Function寄存器地址映射 Port寄存器描述Pn/JP0 — Port RegisterPPRn/JPPR0 — Port Pin Read RegisterPMn/JPM0 — Port Mode Regi…

VSCode 开发C/C++实用插件分享——koroFileHeader

相关文章 VSCode 开发C/C实用插件分享——codegeex VSCode 开发C/C实用插件分享——koroFileHeader 一、koroFileHeader二、使用步骤1.安装2.头文件注释配置3.函数注释配置 一、koroFileHeader 在有些场景下&#xff0c;我们需要在文件头添加一些作者、文件描述、时间和版权描述…

Redis主从复制实现RCE

文章目录 前置知识概念redis常用命令redis module 利用条件利用工具思路例题 [网鼎杯 2020 玄武组]SSRFMe方法一方法二 总结 前置知识 概念 背景是多台服务器要保存同一份数据&#xff0c;如何实现其一致性呢&#xff1f;数据的读写操作是否每台服务器都可以处理&#xff1f;这…

线性规划问题

线性规划问题&#xff1a; 将约束条件及目标函数都是决策变量的线性函数的规划问题称为线性规划问题 一般线性规划问题的描述&#xff1a; 为了解决这类问题&#xff0c;首先需要确定问题的决策变量:然后确定问题的目标&#xff0c;并将目标表示为决策变量的线性函数;最后找出问…

python之ddddocr快速识别

1. 安装模块 pip install ddddocr -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com2. 编写代码 import ddddocr # 导入orc模块 import logging # 导入日志 logging.getLogger().setLevel(logging.INFO) # 设置日志级别 def ComputeCode(path):try:logg…