当前位置: 首页 > news >正文

Vue3 + TypeScript 的 Hooks 实用示例

示例 1: 防抖 Hook(useDebounce

typescript

// hooks/useDebounce.ts
import { ref, watch, onUnmounted, type WatchSource } from 'vue';

/**
 * 防抖 Hook
 * @param source 监听的响应式数据源
 * @param callback 防抖后执行的回调函数
 * @param delay 防抖延迟时间(毫秒,默认 300ms)
 */
export function useDebounce<T>(
  source: WatchSource<T>,
  callback: (value: T) => void,
  delay: number = 300
) {
  let timeoutId: number | null = null;

  // 监听数据源变化
  watch(source, (newValue) => {
    // 清除之前的定时器
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    // 设置新的定时器
    timeoutId = setTimeout(() => {
      callback(newValue);
    }, delay);
  });

  // 组件卸载时清除定时器
  onUnmounted(() => {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
  });
}

// 组件中使用示例:
// const searchQuery = ref('');
// useDebounce(searchQuery, (value) => {
//   console.log('防抖后的搜索值:', value);
// });

示例 2: 本地存储 Hook(useLocalStorage

typescript

// hooks/useLocalStorage.ts
import { ref, watchEffect, type Ref } from 'vue';

/**
 * 本地存储 Hook
 * @param key 存储的键名
 * @param defaultValue 默认值
 * @returns 返回响应式变量和更新函数
 */
export function useLocalStorage<T>(
  key: string,
  defaultValue: T
): [Ref<T>, (value: T) => void] {
  // 从 localStorage 读取初始值
  const storedValue = localStorage.getItem(key);
  const value = ref<T>(
    storedValue ? JSON.parse(storedValue) : defaultValue
  ) as Ref<T>;

  // 监听变化并保存到 localStorage
  watchEffect(() => {
    localStorage.setItem(key, JSON.stringify(value.value));
  });

  // 提供更新函数(可选,直接修改 value 也有效)
  const updateValue = (newValue: T) => {
    value.value = newValue;
  };

  return [value, updateValue];
}

// 组件中使用示例:
// const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'light');

示例 3: 网络请求 Hook(useFetch

typescript

// hooks/useFetch.ts
import { ref, type Ref } from 'vue';

interface UseFetchReturn<T> {
  data: Ref<T | null>;
  error: Ref<Error | null>;
  loading: Ref<boolean>;
  execute: () => Promise<void>;
}

/**
 * 网络请求 Hook
 * @param url 请求地址
 * @param options 请求配置(可选)
 */
export function useFetch<T = unknown>(
  url: string,
  options?: RequestInit
): UseFetchReturn<T> {
  const data = ref<T | null>(null);
  const error = ref<Error | null>(null);
  const loading = ref(false);

  // 执行请求的函数
  const execute = async () => {
    loading.value = true;
    try {
      const response = await fetch(url, options);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      data.value = await response.json();
      error.value = null;
    } catch (err) {
      error.value = err as Error;
      data.value = null;
    } finally {
      loading.value = false;
    }
  };

  return { data, error, loading, execute };
}

// 组件中使用示例:
// const { data, error, loading, execute } = useFetch<User[]>('/api/users');
// execute(); // 手动触发请求

示例 4: 倒计时 Hook(useCountdown

typescript

// hooks/useCountdown.ts
import { ref, onUnmounted } from 'vue';

/**
 * 倒计时 Hook
 * @param seconds 倒计时总秒数
 * @returns 剩余时间(秒)和开始/重置函数
 */
export function useCountdown(seconds: number) {
  const count = ref(seconds);
  let timer: number | null = null;

  // 开始/重置倒计时
  const start = () => {
    reset(); // 重置倒计时
    timer = setInterval(() => {
      if (count.value > 0) {
        count.value--;
      } else {
        clearInterval(timer!);
      }
    }, 1000);
  };

  // 重置倒计时
  const reset = () => {
    if (timer) {
      clearInterval(timer);
      timer = null;
    }
    count.value = seconds;
  };

  // 组件卸载时清除定时器
  onUnmounted(reset);

  return { count, start, reset };
}

// 组件中使用示例:
// const { count, start } = useCountdown(60);
// start(); // 开始倒计时

示例 5: 事件监听 Hook(useEventListener

typescript

// hooks/useEventListener.ts
import { onMounted, onUnmounted } from 'vue';

type EventTarget = Window | Document | HTMLElement;

/**
 * 事件监听 Hook
 * @param target 目标元素(默认 window)
 * @param event 事件名称
 * @param listener 事件回调
 */
export function useEventListener(
  event: string,
  listener: EventListener,
  target: EventTarget = window
) {
  // 挂载时添加监听
  onMounted(() => {
    target.addEventListener(event, listener);
  });

  // 卸载时移除监听
  onUnmounted(() => {
    target.removeEventListener(event, listener);
  });
}

// 组件中使用示例:
// useEventListener('click', (e) => {
//   console.log('全局点击事件', e);
// });

最佳实践建议:

  1. 类型安全

    • 使用 TypeScript 接口 (interface) 明确函数参数和返回值类型。

    • 用泛型 (<T>) 处理动态数据类型(如 useFetch 中的响应数据)。

  2. 响应式处理

    • 优先使用 ref 替代 reactive(更适合类型推导)。

    • 使用 watch 或 watchEffect 处理副作用。

  3. 资源清理

    • 在 onUnmounted 中清除定时器、事件监听等资源。

  4. 组合复用

    • 多个简单 Hook 可以组合成复杂逻辑(如 useCountdown + useEventListener)。

  5. 推荐工具库

    • VueUse 提供了大量高质量的类型安全 Hooks。


通过这些示例,你可以逐步掌握如何编写类型安全、可复用的 Vue3 Hooks。实际开发中,根据需求灵活组合这些基础 Hooks,能显著提升代码质量和开发效率!

相关文章:

  • SpringCloud Alibaba 之分布式全局事务 Seata 原理分析
  • GSO-YOLO:基于全局稳定性优化的建筑工地目标检测算法解析
  • 闭包的理解
  • 算法刷题记录——LeetCode篇(1.9) [第81~90题](持续更新)
  • JavaScript防抖与节流
  • Cloudflare教程:免费优化CDN加速配置,提升网站访问速度 | 域名访问缓存压缩视频图片媒体文件优化配置
  • Payoneer(P卡)会关联吗?如何有效防止P卡关联?
  • 第十四天 - Docker容器管理 - docker-py模块实践 - 练习:容器生命周期管理工具
  • Python中的字典
  • ZYNQ笔记(三):GPIO 中断
  • 3DMAX笔记-UV知识点和烘焙步骤
  • Kotlin 学习-集合
  • Stable Diffusion+Pyqt5: 实现图像生成与管理界面(带保存 + 历史记录 + 删除功能)——我的实验记录(结尾附系统效果图)
  • 【C++基础-关键字】:extern
  • 深入理解Java Optional:告别NullPointerException的优雅方式
  • PMP 考试以及学习资料
  • 艾尔登法环地图不能使用鼠标移动或点击传送点原因和设置方法
  • 计算机视觉与深度学习 | 视觉里程计(Visual Odometry, VO)学习思路总结
  • K8S学习之基础七十五:istio实现灰度发布
  • 探秘Transformer系列之(25)--- KV Cache优化之处理长文本序列