vue3 hooks文件夹中文件类型、命名规范、引用方式

文章目录

      • [一、hooks 文件夹适合存放的文件类型](#一、hooks 文件夹适合存放的文件类型)
      • [二、hooks 文件的命名规范(行业通用)](#二、hooks 文件的命名规范(行业通用))
      • [三、hooks 文件的目录结构示例](#三、hooks 文件的目录结构示例)
      • [四、hooks 文件的编写 & 引用示例](#四、hooks 文件的编写 & 引用示例)
        • [示例1:通用型 hook(本地存储封装 - useStorage.js)](#示例1:通用型 hook(本地存储封装 - useStorage.js))
        • [示例2:业务型 hook(用户登录状态 - useUser.js)](#示例2:业务型 hook(用户登录状态 - useUser.js))
        • [示例3:通用请求 hook(useRequest.js)](#示例3:通用请求 hook(useRequest.js))
      • 五、进阶技巧
      • 六、知识总结

在 Vue3 中, hooks 文件夹是 组合式函数(Composables) 的核心存放目录,用于抽离复用的业务逻辑/通用功能,遵循「高内聚、低耦合」的设计原则。下面我会从 文件类型、命名规范、引用方式 三个维度详细讲解,并给出可直接复用的示例。

一、hooks 文件夹适合存放的文件类型

hooks 文件夹专门存放use 开头的组合式函数,核心是抽离组件中可复用的逻辑,常见类型包括:

  1. 通用功能型:如防抖、节流、时间格式化、本地存储操作等;
  2. 业务逻辑型:如用户登录状态、商品列表请求、表单验证等;
  3. 交互型:如滚动监听、窗口尺寸监听、鼠标事件处理等;
  4. API 请求型:封装通用的接口请求逻辑(如列表分页请求、详情请求)。

❌ 不适合放的文件

  • 组件(.vue 文件,应放 components 文件夹);
  • 工具函数(纯函数如 utils/format.js,应放 utils 文件夹);
  • 全局配置/常量(应放 config/constants 文件夹)。

二、hooks 文件的命名规范(行业通用)

命名直接决定可读性,遵循以下规则:

规则 示例 说明
前缀必须是 use useUser.js 符合 Vue 官方命名习惯
驼峰式命名 useScrollPosition.js 多单词用驼峰,清晰易懂
功能语义化 useProductList.js 一眼能看出功能(商品列表)
拆分粒度适中 一个文件一个核心功能 避免大而全的「万能 hook」

补充

  • 通用型 hook 可加分类前缀:useStorage/local.js(本地存储)、useRequest/list.js(列表请求);
  • 避免缩写:用 useWindowSize 而非 useWinSize

三、hooks 文件的目录结构示例

复制代码
src/
├── hooks/                # hooks 根目录
│   ├── useUser.js        # 用户相关逻辑(登录、信息获取)
│   ├── useStorage.js     # 本地存储封装(localStorage/sessionStorage)
│   ├── useRequest.js     # 通用请求 hook(基于 axios)
│   ├── useScroll.js      # 滚动监听 hook
│   └── useFormValidate.js # 表单验证 hook
├── components/           # 组件目录
├── views/                # 页面目录
└── main.js

四、hooks 文件的编写 & 引用示例

示例1:通用型 hook(本地存储封装 - useStorage.js)
javascript 复制代码
// src/hooks/useStorage.js
import { ref, watch } from 'vue'

/**
 * 本地存储 hook(封装 localStorage 操作)
 * @param {string} key 存储的键名
 * @param {any} defaultValue 默认值
 * @returns {[value, setValue]} 存储值 + 修改方法
 */
export function useLocalStorage(key, defaultValue) {
  // 从本地存储读取值,无则用默认值
  const value = ref(JSON.parse(localStorage.getItem(key)) || defaultValue)

  // 监听值变化,同步到本地存储
  watch(
    value,
    (newVal) => {
      localStorage.setItem(key, JSON.stringify(newVal))
    },
    { deep: true } // 深度监听对象/数组
  )

  // 修改值的方法
  const setValue = (newVal) => {
    value.value = newVal
  }

  return [value, setValue]
}

// 封装 sessionStorage(同理)
export function useSessionStorage(key, defaultValue) {
  const value = ref(JSON.parse(sessionStorage.getItem(key)) || defaultValue)

  watch(
    value,
    (newVal) => {
      sessionStorage.setItem(key, JSON.stringify(newVal))
    },
    { deep: true }
  )

  const setValue = (newVal) => {
    value.value = newVal
  }

  return [value, setValue]
}

组件中引用

vue 复制代码
<!-- src/views/Profile.vue -->
<template>
  <div>
    <p>用户名:{{ username }}</p>
    <button @click="setUsername('新用户名')">修改用户名</button>
  </div>
</template>

<script setup>
// 按需导入 hook
import { useLocalStorage } from '@/hooks/useStorage'

// 使用 hook:key 为 'user_name',默认值为 '游客'
const [username, setUsername] = useLocalStorage('user_name', '游客')
</script>
示例2:业务型 hook(用户登录状态 - useUser.js)
javascript 复制代码
// src/hooks/useUser.js
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' // 假设用了 Element Plus
import { getUserInfo, login } from '@/api/user' // 假设封装了用户相关接口

export function useUser() {
  // 响应式数据:用户信息、加载状态
  const userInfo = ref(null)
  const loading = ref(false)

  // 登录方法
  const loginHandler = async (account, password) => {
    try {
      loading.value = true
      const res = await login({ account, password })
      if (res.code === 200) {
        ElMessage.success('登录成功')
        // 登录成功后获取用户信息
        await fetchUserInfo()
        return true
      } else {
        ElMessage.error(res.msg || '登录失败')
        return false
      }
    } catch (err) {
      ElMessage.error('网络错误,请重试')
      return false
    } finally {
      loading.value = false
    }
  }

  // 获取用户信息
  const fetchUserInfo = async () => {
    try {
      const res = await getUserInfo()
      userInfo.value = res.data
    } catch (err) {
      ElMessage.error('获取用户信息失败')
    }
  }

  // 退出登录
  const logout = () => {
    userInfo.value = null
    localStorage.removeItem('token')
    ElMessage.success('退出登录成功')
  }

  // 组件挂载时自动获取用户信息(如有 token)
  onMounted(() => {
    const token = localStorage.getItem('token')
    if (token) {
      fetchUserInfo()
    }
  })

  // 返回供组件使用的状态和方法
  return {
    userInfo,
    loading,
    loginHandler,
    logout
  }
}

组件中引用

vue 复制代码
<!-- src/views/Login.vue -->
<template>
  <div class="login">
    <el-input v-model="account" placeholder="账号"></el-input>
    <el-input v-model="password" type="password" placeholder="密码"></el-input>
    <el-button @click="handleLogin" :loading="loading">登录</el-button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useUser } from '@/hooks/useUser'
import { useRouter } from 'vue-router'

const router = useRouter()
const account = ref('')
const password = ref('')

// 解构获取 hook 中的状态和方法
const { loading, loginHandler } = useUser()

// 登录按钮点击事件
const handleLogin = async () => {
  const success = await loginHandler(account.value, password.value)
  if (success) {
    router.push('/home') // 登录成功跳转到首页
  }
}
</script>
示例3:通用请求 hook(useRequest.js)
javascript 复制代码
// src/hooks/useRequest.js
import { ref } from 'vue'

/**
 * 通用请求 hook
 * @param {Function} apiFunc 接口请求函数(返回 Promise)
 * @returns {Object} 请求相关状态和方法
 */
export function useRequest(apiFunc) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)

  // 执行请求的方法
  const fetchData = async (...args) => {
    try {
      loading.value = true
      error.value = null
      const res = await apiFunc(...args) // 传递请求参数
      data.value = res.data
      return res
    } catch (err) {
      error.value = err
      console.error('请求失败:', err)
      throw err // 抛出错误,方便组件捕获
    } finally {
      loading.value = false
    }
  }

  // 重置数据
  const reset = () => {
    data.value = null
    error.value = null
  }

  return {
    data,
    loading,
    error,
    fetchData,
    reset
  }
}

组件中引用

vue 复制代码
<!-- src/views/GoodsList.vue -->
<template>
  <div>
    <el-table v-loading="loading" :data="data">
      <!-- 表格列配置 -->
    </el-table>
    <el-button @click="fetchGoodsList(1)">刷新列表</el-button>
  </div>
</template>

<script setup>
import { useRequest } from '@/hooks/useRequest'
import { getGoodsList } from '@/api/goods' // 商品列表接口

// 初始化请求 hook
const { data, loading, fetchData: fetchGoodsList } = useRequest(getGoodsList)

// 组件挂载时请求第一页数据
fetchGoodsList(1)
</script>

五、进阶技巧

  1. hook 组合复用 :一个 hook 可以引用另一个 hook,例如 useUser 中引用 useStorage 存储 token;

  2. 参数校验 :在 hook 中增加参数校验(如 if (!key) throw new Error('key 不能为空')),提升健壮性;

  3. 类型提示 :结合 TypeScript 给 hook 加类型,示例:

    typescript 复制代码
    // src/hooks/useStorage.ts
    export function useLocalStorage<T>(key: string, defaultValue: T): [Ref<T>, (val: T) => void] {
      // 实现逻辑
    }

六、知识总结

  1. 文件类型hooks 文件夹只放以 use 开头的组合式函数,聚焦可复用的逻辑(通用功能/业务逻辑/交互/API 请求);
  2. 命名规范 :必须以 use 开头 + 驼峰式 + 语义化,一个文件一个核心功能;
  3. 引用方式 :通过 import { 方法名 } from '@/hooks/文件名' 按需导入,在组件的 <script setup> 中直接使用,返回的响应式数据可直接绑定到模板。

这种方式能大幅提升代码复用率,让组件逻辑更清晰,是 Vue3 组合式 API 最核心的最佳实践之一。

相关推荐
还是大剑师兰特2 天前
地图的分类
大剑师·地图分类
还是大剑师兰特13 天前
ruoyi‑vue3 添加【车辆管理】菜单组完整方案
大剑师·若依系统
还是大剑师兰特17 天前
若依(ruoyi)前后台系统从零到一部署配置(坑点带解析)
大剑师
还是大剑师兰特17 天前
ideaIC‑2024.1.7(社区版zip绿色解压版)完整使用教程
大剑师·idealc
还是大剑师兰特19 天前
MySQL8.0 Windows完整安装教程
windows·mysql·大剑师
还是大剑师兰特2 个月前
VSCode 插件冲突导致崩溃、卡死、假死 全套根治方案
vscode·大剑师
还是大剑师兰特2 个月前
ThinkPad T490 Windows 自动修复失败完整解决步骤
windows·大剑师·t490
还是大剑师兰特2 个月前
二年级奥数(12) : 巧填符号+竖式数字谜
大剑师·二年级奥数
还是大剑师兰特2 个月前
二年级奥数(9):鸡兔同笼+ 归一问题
大剑师·二年级奥数
还是大剑师兰特2 个月前
二年级奥数(7):移多补少8题 + 和差入门8题 + 年龄问题8题
大剑师·二年级奥数