Vue3+JS 组合式 API 实战:从项目痛点到通用 Hook 封装

Vue3 组合式 API 的实战技巧 —— 组合式 API 帮我解决了不少 Options API 难以应对的问题,尤其是在代码复用和复杂组件维护上。

一、为什么放弃 Options API?聊聊三年项目里的真实痛点​

刚接触 Vue3 时,我曾因 “惯性” 继续用 Options API 写业务,但随着项目复杂度提升,两个痛点越来越明显:​

  1. 代码碎片化:一个数据请求相关的逻辑(加载态、数据处理、错误提示),要分散在data、methods、mounted里,后期维护时需在多个选项间跳转,尤其复杂表单组件,逻辑溯源成本极高;​
  2. 复用性差:比如多个组件都需要 “分页加载列表” 功能,Options API 只能通过mixins实现,但mixins存在命名冲突、逻辑来源不清晰的问题(比如同事接手项目时,不知道某个变量是来自组件本身还是mixins)。​

直到用组合式 API 重构了第一个列表页,才真正体会到 “逻辑聚合” 的爽 —— 所有和 “列表请求” 相关的代码都放在一起,复用只需复制一个函数,这也是我今天想重点分享的核心:用组合式 API 封装通用 Hook,解决重复造轮子问题。​

二、实战:封装 2 个 Vue3+JS 通用 Hook(附完整代码)​

下面结合我项目中高频使用的场景,分享两个通用 Hook 的封装思路,所有代码均基于 Vue3+JS 编写,可直接复制到项目中使用。​

1. useRequest:统一处理请求状态(加载 / 成功 / 失败)​

几乎所有业务组件都需要请求接口,而 “加载态显示”“错误提示”“请求取消” 是通用需求。之前每个组件都要写loading: false、error: '',重复代码占比 30% 以上,用useRequest可完全统一。​

代码实现:

import { ref, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus'; // 假设项目用Element Plus,可替换为其他UI库/*** 通用请求Hook* @param {Function} requestFn - 接口请求函数(需返回Promise)* @param {Object} options - 配置项(可选)* @param {boolean} options.autoRun - 是否默认执行请求(默认true)* @param {Function} options.onSuccess - 请求成功回调* @param {Function} options.onError - 请求失败回调*/
export function useRequest(requestFn, options = {}) {const { autoRun = true, onSuccess, onError } = options;const loading = ref(false); // 加载态const data = ref(null); // 请求结果const error = ref(''); // 错误信息const controller = new AbortController(); // 用于取消请求// 核心请求函数const fetchData = async (...args) => {loading.value = true;error.value = '';try {// 传递signal,支持取消请求const result = await requestFn(...args, { signal: controller.signal });data.value = result;onSuccess && onSuccess(result); // 自定义成功回调} catch (err) {// 排除手动取消请求的错误if (err.name !== 'AbortError') {error.value = err.message || '请求失败,请重试';ElMessage.error(error.value); // 全局错误提示onError && onError(err); // 自定义失败回调}} finally {loading.value = false;}};// 自动执行请求(默认开启)if (autoRun) {fetchData();}// 组件卸载时取消请求,避免内存泄漏onUnmounted(() => {controller.abort();});return {loading,data,error,fetchData, // 手动触发请求(比如刷新按钮)cancelRequest: () => controller.abort() // 手动取消请求};
}

使用示例(用户列表组件):

import { useRequest } from '@/hooks/useRequest';
import { getUserList } from '@/api/user'; // 接口函数export default {setup() {// 1. 初始化请求Hook,传入接口函数和配置const { loading, data: userList, fetchData } = useRequest(getUserList, {onSuccess: (res) => {console.log('用户列表请求成功', res);}});// 2. 手动刷新(比如搜索按钮点击)const handleRefresh = (searchParams) => {fetchData(searchParams); // 传递参数给接口函数};return {loading,userList,handleRefresh};}
};
2. usePagination:快速实现分页功能​

管理后台中 “分页列表” 是高频场景,页码切换、每页条数变更、总数计算这些逻辑完全可以复用。usePagination可结合上面的useRequest,快速搭建分页功能。​

代码实现:

import { ref, computed } from 'vue';/*** 通用分页Hook* @param {Function} fetchFn - 列表请求函数(需接收page、pageSize参数)* @param {Object} defaultParams - 默认分页参数(可选)*/
export function usePagination(fetchFn, defaultParams = {}) {// 分页基础参数const page = ref(defaultParams.page || 1); // 当前页码const pageSize = ref(defaultParams.pageSize || 10); // 每页条数const total = ref(0); // 总条数// 计算总页数const totalPage = computed(() => Math.ceil(total.value / pageSize.value) || 1);// 页码切换事件const handlePageChange = (newPage) => {page.value = newPage;fetchFn({ page: newPage, pageSize: pageSize.value }); // 触发请求};// 每页条数变更事件const handlePageSizeChange = (newSize) => {pageSize.value = newSize;page.value = 1; // 重置为第一页fetchFn({ page: 1, pageSize: newSize }); // 触发请求};// 重置分页参数(比如搜索时)const resetPagination = () => {page.value = 1;pageSize.value = defaultParams.pageSize || 10;};return {page,pageSize,total,totalPage,handlePageChange,handlePageSizeChange,resetPagination,// 分页参数对象(方便传递给请求函数)paginationParams: computed(() => ({page: page.value,pageSize: pageSize.value}))};
}

结合 useRequest 使用示例:

import { useRequest } from '@/hooks/useRequest';
import { usePagination } from '@/hooks/usePagination';
import { getUserList } from '@/api/user';export default {setup() {// 1. 初始化分页Hook,定义请求函数(接收分页参数)const { paginationParams, total, handlePageChange, handlePageSizeChange } = usePagination(async (params) => {// 调用useRequest的fetchData,传递分页参数await fetchData({ ...params, ...searchParams.value });});// 2. 初始化请求Hook,请求时带上分页参数const searchParams = ref({}); // 搜索参数(可选)const { loading, data: userList, fetchData } = useRequest(async (params = {}) => {const res = await getUserList({ ...paginationParams.value, ...params });total.value = res.total; // 更新总条数return res.list;});// 3. 搜索功能(重置分页)const handleSearch = (params) => {searchParams.value = params;handlePageChange(1); // 搜索时跳转到第一页};return {loading,userList,page: paginationParams.page,pageSize: paginationParams.pageSize,total,handlePageChange,handlePageSizeChange,handleSearch};}
};

三、Vue3+JS 开发的 3 个避坑技巧(三年经验总结)​

  1. 避免在 setup 中直接修改 props:Vue3 中 props 仍是单向数据流,若需修改 props,可通过emit通知父组件,或用toRef创建 props 的响应式引用(如const name = toRef(props, 'name'));​
  2. watch 监听对象时要加 deep:若监听的是对象 / 数组,需开启deep: true才能监听到内部属性变化(如watch(user, () => {}, { deep: true })),但尽量避免监听整个对象,可直接监听具体属性(如watch(() => user.name, () => {})),性能更好;​
  3. Hook 命名规范统一:自定义 Hook 建议以use开头(如useRequest、usePagination),方便团队识别和维护,避免出现getPagination、paginationUtil这类不统一的命名。​

四、总结与交流​

以上就是我基于 Vue3+JS 栈的实战分享 —— 从项目痛点出发,用组合式 API 封装通用 Hook,既能减少重复代码,又能提升项目可维护性。这也是我三年前端开发中,从 “写功能” 到 “写优雅的功能” 的一个重要转变。​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98758.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98758.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

把 AI 塞进「电梯按钮」——基于 64 kB 零样本声纹的离线故障预测按钮

标签:零样本声纹、电梯按钮、离线 AI、TinyML、RISC-V、低功耗、GD32V303、故障预警 ---- 1. 背景:为什么按钮要「听声音」? 全国 700 万台电梯,按钮故障率 0.3 %/年,却常出现: • 机械卡滞、触点氧化&…

清华大学联合项目 论文解读 | MoTo赋能双臂机器人:实现零样本移动操作

研究背景 移动操作是机器人领域的核心挑战,它使机器人能够在各种任务和动态日常环境中为人类提供帮助。传统的移动操作方法由于缺乏大规模训练,往往难以在不同任务和环境中实现泛化。而现有操作基础模型虽在固定基座任务中表现出强泛化性,却无…

go webrtc - 2 webrtc重要概念

webrtc是一套音视频传输技术生态,不是一个协议或一个什么东西。3种模式本文基于 SFU 形式阐述!重要概念:sfu 服务负责:信令 服务负责:peerConnection:track:房间:虚拟分组概念用户&a…

“下游任务”概念详解:从定义到应用场景

“下游任务”概念详解:从定义到应用场景 一、什么是“下游任务”? 在机器学习(尤其是深度学习)中,“下游任务”(Downstream Task)是相对“上游过程”而言的目标任务——可以理解为:我…

视频怎么做成 GIF?用 oCam 一键录制 GIF 动画超简单

GIF 动图因其生动直观、无需点击播放的特点,越来越受欢迎。你是否也曾看到一段有趣的视频,想把它做成 GIF 发给朋友或用在PPT里?其实,将视频片段转换为 GIF 并不需要复杂的视频剪辑技术,使用一款支持直接录制为 GIF 的…

Vue.config.js中的Webpack配置、优化及多页面应用开发

Vue.config.js中的Webpack配置、优化及多页面应用开发 在Vue CLI 3项目中,vue.config.js文件是工程化配置的核心入口,它通过集成Webpack配置、优化策略和多页面开发支持,为项目构建提供高度可定制化的解决方案。本文将从基础配置、性能优化、…

行业学习【电商】:直播电商的去头部化、矩阵号?

声明:以下部分内容含AI生成这两个词是当前直播电商和MCN领域的核心战略,理解了它们就理解了行业正在发生的深刻变化。一、如何理解“去头部化”?“去头部化” 指的是平台或MCN机构有意识地减少对超头部主播(如曾经的李佳琦、薇娅&…

【MFC视图和窗口基础:文档/视图的“双胞胎”魔法 + 单文档程序】

大家好,我是你的MFC编程小伙伴!学MFC就像探险古墓:到处是神秘的“房间”(窗口)和“宝藏”(数据)。今天咱们聊聊核心概念 – 视图、窗口和文档。这些是MFC的“骨架”,懂了它们&#x…

深度学习(六):代价函数的意义

在深度学习的浩瀚世界中,代价函数(Cost Function),又称损失函数(Loss Function)或目标函数(Objective Function),扮演着至关重要的角色,它就像一个导航员&…

Kable使用指南:Android BLE开发的现代化解决方案

概述 Kable(com.juul.kable:core)是一个专为Android蓝牙低功耗(BLE)开发设计的Kotlin协程友好库。它通过提供简洁的API和响应式编程模式,极大地简化了BLE设备交互的复杂性。本文将详细介绍Kable的使用方法,…

Android图案解锁绘制

使用到的库是Pattern Locker,根据示例进行了修改,把默认样式和自定义样式进行了合并调整。 设置密码 布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xm…

Kotlin 协程之 Flow 的理解使用及源码解析

前言 在前面的文章中&#xff0c;我们已经讨论了 Channel 的概念和基本使用以及 Channel 的高阶应用。这篇我们来看日常开发中更常用的Flow。 “冷流” 和 “热流” 的本质 先来梳理一下所谓的 “冷流” 和 “热流”。 核心概念 我们已经知道 Channel 是 “热流”&#xff…

简述ajax、node.js、webpack、git

本系列可作为前端学习系列的笔记&#xff0c;HTML、CSS和JavaScript系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 点赞关注不迷路&#xff01;您的点赞、关注和收藏是对小编最大的支持和鼓励&#xff01; 系列文章目录 简述ajax、…

经营帮会员经营:全方位助力企业高效发展,解锁商业新可能

在商业竞争愈发激烈的当下&#xff0c;企业若想脱颖而出&#xff0c;高效的经营管理体系至关重要。经营帮的会员经营板块&#xff0c;凭借丰富且实用的功能&#xff0c;为企业打造了一站式的经营助力平台&#xff0c;从多维度赋能企业&#xff0c;让发展之路更顺畅。会员经营与…

Vue 封装Input组件 双向通信

子组件<template><div class"box"><div class"box-left"><input blur"handleBlur" v-model"localInput" class"box-left-input"> </div><div class"box-right"><p style…

伽马(gamma)变换记录

此只记录伽马变换原理及其应用结果&#xff08;文章所有内容基于数字图像处理-冈萨雷斯&#xff09;&#xff0c;和直接用MATLAB代码生成伽马变换代码。一、原理伽马变换的公式很简答 就是一个有规律的幂运算 公式如下&#xff1a;一般在图像中进行应用是 C1 y为不同值时r的输…

电路学习(六)三极管

三极管是一种电流驱动元器件&#xff08;MOS管为电压驱动&#xff09;&#xff0c;在电路中可以充当开关&#xff0c;放大电流等作用。本文章参考了尚硅谷的视频资料。1. 什么是三极管&#xff1f;三极管又被称为晶体三极管&#xff08;Bipolar Junction Transistor&#xff0c…

配置docker常见问题

输入sudo yum install -y yum-utils device-mapper-persistent-data lvm2出现Cannot find a valid baseurl for repo: base/7/x86_64一、检查网络输入ping www.baidu.com出现PING www.a.shifen.com (220.181.111.1) 56(84) bytes of data. 64 bytes from 220.181.111.1 (220.18…

Python 实战:票据图像自动矫正技术拆解与落地教程

在日常办公自动化&#xff08;OA&#xff09;或财务数字化场景中&#xff0c;拍摄的票据常因角度问题出现倾斜、变形&#xff0c;不仅影响视觉呈现&#xff0c;更会导致 OCR 文字识别准确率大幅下降。本文将从技术原理到代码实现&#xff0c;手把手教你用 Python 打造票据图像自…

vue3+TS项目配置unocss

配置unocss &#xff08;1&#xff09;安装依赖 npm i unocss unocss/preset-uno unocss/preset-attributify -D npm install unocss/transformer-directives&#xff08;2&#xff09;根目录新建uno.config.ts文件 import { defineConfig } from "unocss"; impor…