Vue笔记(十六)--自定义Hook

这一节了解一下Vue3中的自定义Hook,它也叫组合式函数,本质就是抽离出来、可复用的setup逻辑函数,约定以use开头命名,简单总结如下:

自定义Hook 本身不是 API,但是内部一般会使用下面这些Vue API:

  1. ref:定义基础响应式数据

  2. reactive:对象类型响应式

  3. computed:计算属性

  4. watch/watchEffect:监听数据变化、执行副作用

  5. onMounted/onUnmounted:生命周期钩子(在Hook内部注册,组件销毁自动清理)

场景

1 通用DOM 交互:useMouse、useClickOutside、useScroll

2 异步请求封装:useRequest

3 定时器、事件监听

栗子:

TypeScript 复制代码
<template>
  <div>
    <h3>鼠标坐标Hook</h3>
    <p>X:{{ x }}</p>
    <p>Y:{{ y }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

// 自定义Hook:useMouse
function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function handleMousemove(e) {
    x.value = e.clientX
    y.value = e.clientY
  }

  onMounted(() => {
    window.addEventListener('mousemove', handleMousemove)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', handleMousemove)
  })

  return { x, y }
}

// 在组件中使用Hook
const { x, y } = useMouse()
</script>

Demo02

TypeScript 复制代码
<template>
  <div>
    <h3>useRequest 异步请求Hook</h3>
    <button @click="run">发起请求</button>
    <button @click="retry" :disabled="!error">重试</button>
    <p v-if="loading">加载中...</p>
    <p v-if="error" style="color:red">错误:{{ error.message }}</p>
    <p v-if="data">返回数据:{{ data }}</p>
  </div>
</template>

<script setup>
import { ref, onUnmounted } from 'vue'

function useRequest(apiFn) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)

  let abortController = null

  async function run() {
    if (abortController) {
      abortController.abort()
    }
    abortController = new AbortController()

    loading.value = true
    error.value = null
    try {
      const res = await apiFn(abortController.signal)
      data.value = res
    } catch (err) {
      if (err.name !== 'AbortError') {
        error.value = err
      }
    } finally {
      loading.value = false
    }
  }

  function retry() {
    run()
  }

  onUnmounted(() => {
    if (abortController) {
      abortController.abort()
    }
  })

  return {
    data,
    loading,
    error,
    run,
    retry
  }
}

// 模拟接口函数
function mockApi(signal) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      resolve({ name: "张三", age: 20, id: 1001 })
    }, 1500)

    signal.addEventListener('abort', () => {
      clearTimeout(timer)
    })
  })
}

// 使用Hook
const { data, loading, error, run, retry } = useRequest(mockApi)
</script>

注意

1 Hook内的生命周期,归属调用它的组件;组件销毁,Hook里面的onUnmounted自动执行

2 每次组件调用useXxx()都会生成独立的状态副本(互不干扰);想要全局共享状态,需要单独抽离一份reactive/ref放到Hook外面

3 副作用(监听、定时器)一定要在onUnmounted清理,防止内存泄漏

4 尽量返回ref/toRefs,保证解构出来的值保持响应式

相关推荐
杭州领祺科技1 小时前
057 虚拟电厂聚合商工程链:准入 × 调度对接 × 结算口径 × 27 号令 4 区
储能·1024程序员节·虚拟电厂·电力·算电协同·电力现货
大陈子编程2 小时前
动态规划(C++):从入门到实战的完整指南
1024程序员节
Godspeed Zhao2 小时前
具身智能系统概论——边缘计算和云端协调
人工智能·边缘计算·1024程序员节
JavaPub-rodert2 小时前
不用 React、Vue,也不用后端:王仕宇是怎么把 JavaPub Tools 做成 Web + Electron 双端产品的
1024程序员节
桐盛科技2 小时前
基于边缘计算网关的 Modbus 与 DL/T 645 协议数据清洗与断网续传实现
边缘计算·1024程序员节
u1301302 小时前
GitHub 热榜项目:日榜(2026-10-24)
人工智能·github·1024程序员节
sbjdhjd2 小时前
从黑板事实到长链路剪枝:AI 渗透测试 Agent 的实验、失效与改造
人工智能·网络安全·数据挖掘·php·agent·剪枝·1024程序员节