Vue Hooks:让组件开发像搭积木一样简单有趣!🚀✨

906 阅读4分钟

想象一下,如果你能将每个小功能都封装成一个魔法盒子,然后只需轻轻一“use”,就能把这些魔法盒子的超能力注入到你的 Vue 组件中,是不是感觉组件开发就像搭积木一样既简单又充满乐趣?这就是 Vue Hooks 的魅力所在!

Vue 的 Composition API 提供了一种类似于 React Hooks 的编程风格,允许开发者将组件的逻辑和状态从模板中分离出来,从而提高了代码的可维护性和复用性。虽然 Vue 并没有直接复制 React 的 Hooks 概念,但我们可以利用 Composition API 实现相似的功能。本文将通过一个鼠标监听MouseMove组件带你探讨如何在 Vue 中创建和使用 Hooks,并解决与之相关的潜在问题,如内存泄漏等。

1. 内存泄漏

1.1 原因

在 Vue 中,如果我们在一个组件内添加了事件监听器(例如全局事件、定时器等),并且没有在组件卸载时正确地移除这些监听器,就可能会导致内存泄漏。当组件被销毁后,这些未取消的事件处理函数仍然占用着内存空间,无法被垃圾回收机制回收。

例如:

<template>
    <div>
      <p>Mouse X: {{ mousePos.x }}</p>
      <p>Mouse Y: {{ mousePos.y }}</p>
    </div>
  </template>
  
  <script setup>
  import { 
    reactive,
    onMounted
  } from 'vue'
  
  const mousePos = reactive({
    x: 0,
    y: 0
  })
  
  // 定义一个函数来更新mousePos对象
  const updateMousePosition = (e) => {
    mousePos.x = e.clientX;
    mousePos.y = e.clientY;
    console.log('Mouse moved');
  }
  
  // 在组件挂载时添加mousemove事件监听器
  onMounted(() => {
    window.addEventListener('mousemove', updateMousePosition);
  })
  </script>
  
  <style scoped>
  </style>

屏幕截图 2025-01-10 105204.png

屏幕截图 2025-01-10 105224.png

可以看到组件关闭后并未取消事件处理函数导致内存泄漏

1.2 解决

为了避免这种情况的发生,我们应该确保在组件卸载之前清理所有的副作用(side effects)。对于通过 onMountedonUnmounted 生命周期钩子添加的任何事件监听器或定时器,在组件即将卸载时都应该调用相应的清除方法。

<template>
    <div>
      <p>Mouse X: {{ mousePos.x }}</p>
      <p>Mouse Y: {{ mousePos.y }}</p>
    </div>
  </template>
  
  <script setup>
  import { 
    reactive,
    onMounted,
    onUnmounted // 添加此钩子以确保可以清理事件监听器
  } from 'vue'
  
  const mousePos = reactive({
    x: 0,
    y: 0
  })
  
  // 定义一个函数来更新mousePos对象
  const updateMousePosition = (e) => {
    mousePos.x = e.clientX;
    mousePos.y = e.clientY;
    console.log('Mouse moved');
  }
  
  // 在组件挂载时添加mousemove事件监听器
  onMounted(() => {
    window.addEventListener('mousemove', updateMousePosition);
  })
  
  // 在组件卸载前移除mousemove事件监听器
  onUnmounted(() => {
    window.removeEventListener('mousemove', updateMousePosition);
  })
  </script>
  
  <style scoped>
  </style>

2. 生命周期

Vue 3 引入了 Composition API,它提供了一套新的方式来组织逻辑,并且允许你以更灵活的方式使用生命周期钩子。Composition API 中的生命周期钩子函数包括:

onBeforeMount->onMounted->onBeforeUpdate->onUpdated->onBeforeUnmount->onUnmounted

Vue 组件的生命周期是理解何时以及如何执行某些操作的关键。特别是当我们想要在组件条件渲染(如 v-if)时进行一些清理工作,比如在组件卸载前释放资源。我们可以使用 onUnmounted 钩子来确保所有必要的清理工作都在组件卸载之前完成。

// 在组件卸载前清理资源
onUnmounted(() => {
  // 清理逻辑...
});

3. Hooks 编程

  • 函数式编程

Vue Hooks 编程风格通常以 use 开头命名,表示这是一个 Hook。这种方式不仅遵循了 React 的约定,也帮助开发者快速识别哪些函数是用来封装逻辑和状态的。此外,通过这种方式可以更好地将响应式的业务逻辑与 UI 分离开来,让组件变得更加专注于视图层的表现。

  • 组件拆分

采用 Hooks 编程风格的一个重要好处就是它可以促进组件的拆分。我们将复杂的业务逻辑提取到单独的 Hook 文件中,这样做的好处是可以让 UI 开发人员专注于界面设计,而不需要过多关心底层的实现细节。同时,这也提高了组件的复用性和可测试性。

屏幕截图 2025-01-10 110923.png

useMouse.js

// hooks/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue';

// export 关键字用于导出一个函数,以便在其他地方导入和使用
export function useMouse() {
  // 使用ref创建两个响应式的坐标变量
  let x = ref(0);
  let y = ref(0);

  // 定义更新坐标的函数
  const updateCoordinates = (event) => {
    x.value = event.clientX;
    y.value = event.clientY;
    console.log('useMouse')
  };

  // 在组件挂载时添加mousemove事件监听器
  onMounted(() => {
    window.addEventListener('mousemove', updateCoordinates);
  });

  // 在组件卸载前移除mousemove事件监听器
  onUnmounted(() => {
    window.removeEventListener('mousemove', updateCoordinates);
  });

  // 返回x和y,让调用者可以解构使用
  return { x, y };
}

MouseMove.app

<template>
    <div>
        <p>Mouse X: {{ x }}</p>
        <p>Mouse Y: {{ y }}</p>
    </div>
</template>

<script setup>
import {useMouse,useMemo} from '../hooks/useMouse' // ..跳到上一级目录
import Hook from '../hooks/useMouse'
const {x,y} = useMouse()
useMemo()

console.log(Hook)
</script>

<style scoped>
</style>

4. 总结

通过 Composition API,Vue Hooks 让我们可以把那些复杂的业务逻辑、状态管理以及事件监听等都打包进一个个小巧玲珑的函数里。当你需要时,只要说一声“嘿,use这个Hook吧!”——比如 useMouseuseFetch 或者 useLocalStorage——这些 Hooks 就会像忠诚的小助手一样为你效劳,帮你处理好一切琐事,而你只需要关注如何让界面看起来更美、用户体验更好。