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 最核心的最佳实践之一。

相关推荐
还是大剑师兰特1 天前
.pnpm-store作用是什么,可以删除吗?
大剑师·pnpm-store
还是大剑师兰特2 个月前
拥抱AI,还是大剑师兰特2025年博客创作详细总结
人工智能·大剑师·2025博客之星
还是大剑师兰特2 个月前
SVG图像文件结构
大剑师·svg图像
还是大剑师兰特3 个月前
JEPG图像文件结构
大剑师·jepg结构
还是大剑师兰特3 个月前
GIF图像文件结构
大剑师·gif图像结构
还是大剑师兰特3 个月前
PNG图像文件结构
服务器·大剑师·png结构
还是大剑师兰特3 个月前
单兵作战需要哪些计算能力
大剑师·作战工具
还是大剑师兰特3 个月前
MVC和MVVM模式详解+对比
mvc·mvvm·大剑师
还是大剑师兰特3 个月前
前端设计模式:详解、应用场景与核心对比
前端·设计模式·大剑师