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,保证解构出来的值保持响应式

相关推荐
Agent世界2 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo2 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
某哈压力大3 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行
程序员大阳3 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半3 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css
程序员大阳3 小时前
副队长ES6+教程(2)--let 与 const
前端·javascript·es6·const·let·var
deli0073 小时前
写满 200GB 后 SSD 为什么掉到 95MB/s?SLC 缓存模型实测
前端
普通人64 小时前
WebGL与WebGPU入门指南
开发语言·webgl·1024程序员节
曦樂~4 小时前
【Web Development】03JavaScript 基础和事件
开发语言·前端·javascript
deli0074 小时前
HTTPS 的锁是怎么挂上的?TLS 1.3 握手 6 步实测
前端