Vue3自定义Hooks函数的使用详解

 更新时间:2023年09月07日 16:47:49   作者:wdlhao  
vue3 中的 hooks 就是函数的一种写法,就是将文件的一些单独功能的js代码进行抽离出来进行封装使用,下面我们就来看看vue3中自定义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 的优缺点

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

缺点:

  • 变量来源不明确:变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
  • 命名冲突:多个 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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • vue3实战-子组件之间相互传值问题

    vue3实战-子组件之间相互传值问题

    这篇文章主要介绍了vue3实战-子组件之间相互传值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Vue常用实例方法示例梳理分析

    Vue常用实例方法示例梳理分析

    在了解vue的常用的实例方法之前,我们应该先要了解其常用的实例属性,你能了解到的vue实例属性有哪些呢?小编在这里就列举了几个常用的vue实例的属性。大家可以一起参考学习一下
    2022-08-08
  • Vue项目打包压缩的实现(让页面更快响应)

    Vue项目打包压缩的实现(让页面更快响应)

    这篇文章主要介绍了Vue项目打包压缩的实现(让页面更快响应),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • 使用vue3+ts打开echarts的正确方式

    使用vue3+ts打开echarts的正确方式

    这篇文章主要给大家介绍了关于使用vue3+ts打开echarts的正确方式,在Vue3中使用ECharts组件可以方便地创建各种数据可视化图表,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • vue 组件数据加载解析顺序的详细代码

    vue 组件数据加载解析顺序的详细代码

    Vue.js的解析顺序可以概括为:模板编译、组件创建、数据渲染、事件处理和生命周期钩子函数执行,接下来通过本文给大家介绍vue 组件数据加载解析顺序的完整代码,感兴趣的朋友跟随小编一起看看吧
    2024-03-03
  • element step组件在另一侧加时间轴显示

    element step组件在另一侧加时间轴显示

    本文主要介绍了element step组件在另一侧加时间轴显示,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • 如何让别人访问本地运行的vue项目

    如何让别人访问本地运行的vue项目

    这篇文章主要介绍了如何让别人访问本地运行的vue项目,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Vue实现路由过渡动效的4种方法

    Vue实现路由过渡动效的4种方法

    Vue 路由过渡是对 Vue 程序一种快速简便的增加个性化效果的的方法,这篇文章主要介绍了Vue实现路由过渡动效的4种方法,感兴趣的可以了解一下
    2021-05-05
  • 解决vue-router 嵌套路由没反应的问题

    解决vue-router 嵌套路由没反应的问题

    这篇文章主要介绍了解决vue-router 嵌套路由没反应的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • vue-cli3单页构建大型项目方案

    vue-cli3单页构建大型项目方案

    这篇文章主要介绍了vue-cli3单页构建大型项目方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04

最新评论