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(更适合类型推导)。

    • 使用 watchwatchEffect 处理副作用。

  3. 资源清理

    • onUnmounted 中清除定时器、事件监听等资源。
  4. 组合复用

    • 多个简单 Hook 可以组合成复杂逻辑(如 useCountdown + useEventListener)。
  5. 推荐工具库

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

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

相关推荐
暗暗那18 分钟前
【面试】什么是回流和重绘
前端·css·html
小宁爱Python20 分钟前
用HTML和CSS绘制佩奇:我不是佩奇
前端·css·html
weifexie1 小时前
ruby可变参数
开发语言·前端·ruby
千野竹之卫1 小时前
3D珠宝渲染用什么软件比较好?渲染100邀请码1a12
开发语言·前端·javascript·3d·3dsmax
sunbyte1 小时前
初识 Three.js:开启你的 Web 3D 世界 ✨
前端·javascript·3d
半兽先生1 小时前
WebRtc 视频流卡顿黑屏解决方案
java·前端·webrtc
南星沐2 小时前
Spring Boot 常用依赖介绍
java·前端·spring boot
孙_华鹏3 小时前
手撸一个可以语音操作高德地图的AI智能体
前端·javascript·coze
zhangxingchao3 小时前
Jetpack Compose 动画
前端
@PHARAOH3 小时前
HOW - 缓存 React 自定义 hook 的所有返回值(包括函数)
前端·react.js·缓存