Vue3自定义Hooks函数的使用详解
作者:wdlhao
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 的优缺点
优点:组件中相同代码逻辑复用;
缺点:
- 变量来源不明确:变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
- 命名冲突:多个 mixins 的生命周期会融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突。
- 滥用会造成维护问题:mixins 和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个 mixins,一个 mixins 也可以被多个组件引用)。
注:VUE3 提出的 Composition API 旨在解决这些问题。mixins 的缺点是 Composition API 背后的主要动因之一,Composition API 受到 React Hooks 的启发。
hooks代码:
useCount.ts函数示例:
import { ref, onMounted, computed } from 'vue'; export default function useCount { const count = ref(0); const doubleCount = computed( () => count.value * 2 ); const increase = (delta) => { return count.value + delta; } return { count, doubleCount, increase }; }
useCount在组件中调用:
<script setup lang="ts"> import useCount from "@/hooks/useCount"; const { count,doubleCount,increase } = useCount; const newCount = increase(10); // 输出: 10 </script>
Mixins 的代码:
export default const countMixin = { data() { return { count: 0 }; }, computed: { doubleCount() { return this.count * 2; } }, methods: { increase(delta){ return this.count + delta; } };
Mixins在组件中调用:
import countMixin from '@/mixin/countMixin'; export default { mixins: [countMixin], mounted() { console.log(this.doubleCount);// 输出: 0 const newCount = this.setIncrease(10); // 输出: 10 }, methods: { setIncrease(count){ this.increase(count) } } }
这两个示例展示了使用 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 { const blob = new Blob([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在组件中使用:
<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:"用户列表" }); resolve(res); }); }); }; </script>
示例2:加减计数(useCount)
useCount函数封装:
import { computed, ref, Ref } from "vue" // 定义hook方法 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>倍数:{{multiple}}</p> <div> <button @click="increase(1)">加一</button> <button @click="decrease(1)">减一</button> // 在模版中直接使用hooks中的方法作为回调函数 </div> </template> <script setup lang="ts"> import useCount from "@/hooks/useCount" const {count,multiple,increase,decrease} = useCount(10) </script>
示例3:获取鼠标触发点坐标(useMousePosition)
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 } }
useMousePosition在组件中使用:
<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>
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将碎片化的响应式变量和方法按功能分块写,实现高内聚低耦合。
到此这篇关于Vue3自定义Hooks函数的使用详解的文章就介绍到这了,更多相关Vue3 Hooks内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!