但是Hooks在我们前端开拓领域中还是授予了一些特定的意义,它是一种更灵巧和模块化的办法来编写组件逻辑,可以更好地复用和组织代码。它们使得组件的逻辑更加简洁、模块化和易于掩护。
来一个Option Api 和 Composition Api 的比拟讲Vue3 Hooks前,我们先来看下Option Api 和 Composition Api 比拟。
特性

Option API
Composition API
定义组件
通过 data, methods, computed, watch 分别定义组件的状态、方法、打算属性和监听器
通过 setup 函数,利用 Vue 的组合函数(如 ref, reactive, computed, watch)定义组件的所有逻辑
代码组织
按功能(如 data、methods 等)分组,构造化明了
按逻辑单元分组,干系逻辑聚合在一起,便于掩护和复用
可读性和可掩护性
对付小型组件较好,但繁芜组件的逻辑分散在各个部分,难以掩护
对付繁芜组件更优,逻辑单元清晰,便于理解和掩护
类型支持
依赖外部工具(如 TypeScript)进行类型检讨和推断
原生支持类型,TypeScript 推断更好,类型安全更高
逻辑复用
利用 mixins 复用逻辑,但可能导致命名冲突和难以调试
利用 hooks 或 composable 函数复用逻辑,无命名冲突,代码更清晰
this
在 data, methods, computed, watch 等中利用 this 引用组件实例
在 setup 中没有 this,利用组合函数返回的工具直接引用
掩护
按功能分组,虽然构造化明了,但逻辑会分散在不同部分,不利于掩护
可以轻松将同一个逻辑关注点干系的代码放在一起,直不雅观明了,而且可以很轻松地将这一组代码移动到一个外部文件
性能优化
须要手动处理,如深度监听相应式数据等
内置优化,如自动依赖网络,更高效
打包体积
打包体积相对较大,包含较多的 runtime 代码
打包体积更小,树摇优化(Tree Shaking)更有效,减少未利用代码
这里我们借鉴一下官网的比拟图
通过上面内容可以看到,如果是比较繁芜的功能,我们利用Composition Api 将功能和逻辑点分组分块写在一起,如果有些功能逻辑能够复用,然后合营后文我们讲到的自定义Hooks就可以轻松驾驭,写出高性能,便于掩护的代码。
Hooks命名规范下面的这些规范,都只是建议性规范,并非逼迫性。
为了保持同等性和可读性,所有的自定义hooks该当以use开头。这可以让开发者一眼识别出这是一个hook。命名该当只管即便描述hook的功能或用场,以便其他开拓者能够快速理解这个hook的浸染。常日我们会用名词或名词短语来命名,而不是动词。例如,useUserData比useGetUserData更得当。每个hook该当只管即便只做一件事,这样可以让hook更加专注和可重用。如果一个hook承担了多个职责,考虑将其拆分成多个更小的hooks。命名时要避免利用与Vue已有功能冲突的名称,例如useData,useMethods等,这些名称可能会引起稠浊。在Vue3 中的Hooks官方对自定义hooks定义:在 Vue 运用的观点中,“组合式函数” (Composables) 是一个利用 Vue 组合式 API 来封装和复用有状态逻辑的函数。
Vue3 中为什么大力推崇利用 Hooks,这得益于Vue3 Composition API 供应了通过函数组织组件逻辑的办法。它通过 setup 函数作为组件的入口点,许可你在函数中定义组件的状态、打算属性和副浸染。setup 函数在组件实例创建之前调用,并返回一个工具,组件模板中的属性和方法都会从这个工具中得到。
这里我们来一个非常大略的Hooks利用例子。
useCounter.ts
代码解读
复制代码
import { ref } from 'vue'; / useCounter 函数用于创建和管理一个计数器 / export function useCounter() { const count = ref(0); const increment = () => { count.value++; }; // 返回一个包含 count 和 increment 的工具,供外部利用 return { count, increment }; }
在组件中利用
vue
代码解读
复制代码
<template> <div > <p>{{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script lang="ts" setup> import { useCounter } from './useCounter.ts'; const { count, increment } = useCounter(); </script>
useCounter.ts 供应了一个大略易用的计数器逻辑片段,可以轻松地集成到Vue组件 中。
为什么在Vue3中就基本看不到Mixin了?这里我们上一个奇怪的例子来直接解释在之前Vue2 Mixin开拓随意马虎碰着的问,把稳下面代码是有省略简写的。
javascript
代码解读
复制代码
// 第一个 Mixin const mixinA = { data() { return { message: '来自 Mixin A' }; }, methods: { showMessage() { console.log(this.message); } } }; // 第二个 Mixin const mixinB = { data() { return { messageB: '来自 Mixin B' }; }, methods: { showMessage() { this.messageA= '来自messageB' console.log(this.message); } } }; // 第三个 Mixin const mixinC = { mixins: [mixinA], data() { return { messageC: '来自 Mixin C' }; }, methods: { showMessage() { this.message= '来自messageC' console.log(this.message); } } }; // 利用这两个 Mixin 的组件 export default { mixins: [mixinB, mixinC], created() { console.log(this.message) // 这里的message 来自哪里? this.showMessage(); // 这里的 showMessage 方法到底调用的谁的 console.log(this.message) // 这里的message 结果是啥? } };
通过上面例子我们不难创造 Mixin 有很严重的缺陷:
如果同时混入有链式或者多个Mixin,你会创造属性和方法难以追溯,韶光久了须要一个一个去点开看。如果有相同的属性名和方法名,会被覆盖,产生意想不到bug。无法向Mixin通报参数来改变逻辑。再来看看用 Vue3 Composition API Hooks 是如何完美办理这个问题的,把稳下面代码是有省略简写的
xml
代码解读
复制代码
<template> <div> <p>{{ messageA }}</p> <p>{{ messageB }}</p> <button @click="handleMessages">Show Messages</button> </div> </template> <script setup> import { ref } from 'vue'; // 第一个hooks 组件 useMixinA function useMixinA() { const message = ref('来自 Hook A'); function showMessage() { console.log(message.value); } return { message, showMessage }; } // 第二个hooks 组件 useMixinB function useMixinB() { const message = ref('来自 Hook B'); function showMessage() { console.log(message.value); } return { message, showMessage }; } // 第三个hooks 组件 useMixinC function useMixinC(num) { const message = ref('来自 Hook C'); function showMessage() { console.log(num); } return { message, showMessage }; } const { message: messageA, showMessage: showMessageA } = useMixinA(); const { message: messageB, showMessage: showMessageB } = useMixinB(); const { message: messageC, showMessage: showMessageC } = useMixinC(1); function handleMessages() { showMessageA(); // 输出 '来自 Hook A' showMessageB(); // 输出 '来自 Hook B' showMessageC(); // 输出 1 console.log(messageA.value); // 输出 '来自 Hook A' console.log(messageB.value); // 输出 '来自 Hook B' console.log(messageC.value); // 输出 '来自 Hook C' } </script> <style scoped> button { margin-top: 10px; } </style>
通过上面例子我们可以很清晰的看到Composition API 自定义Hooks的优点
通过解构赋值可以给每个 hook 返回的属性和方法起别名,这样避免了命名冲突。可以很轻松的追溯到属性和方法的来源。可以灵巧通报通过任何参数来改变它的逻辑,这大大提高了Vue3在抽象逻辑方面的灵巧性。Vue3 Hooks vs React HooksVue3 Hooks 紧张强调 模块化与复用性: 通过组合函数,开拓者可以将逻辑提取到独立的函数中,提升代码的可读性和复用性。灵巧性:它供应了更灵巧的办法来组织和管理组件逻辑,特殊是对付繁芜的组件。React Hooks紧张强调函数式编程(函数组件): 供应了在函数组件中利用状态和其他 React 特性的能力,肃清了类组件的繁芜性。React Hooks 供应了在函数组件中利用状态和其他 React 特性的能力,肃清了类组件的繁芜性。简洁与优雅:它简化了组件的状态管理和副浸染处理,使代码更简洁、更易读。来看一个 React Hooks 的实际例子
javascript
代码解读
复制代码
import React, { useState, useEffect } from 'react'; function Timer() { const [count, setCount] = useState(0); useEffect(() => { const timer = setInterval(() => { setCount(prevCount => prevCount + 1); }, 1000); return () => clearInterval(timer); // 打消副浸染 }, [count]); //只有 数组里面的count 变革了useEffect才会调用 // 仿照一个耗时的打算 const computeFactorial = (n) => { console.log('Computing factorial...'); if (n <= 1) return 1; return n computeFactorial(n - 1); }; // 利用 useMemo 来缓存打算结果 const factorial = useMemo(() => computeFactorial(count), [count]); return <div>{message} {factorial}</div>; } export default Timer;
代码阐明:
打消副浸染:在 Timer 组件中,我们利用 useEffect 来设置一个计时器,并在返回的打消函数中利用 clearInterval 来打消这个计时器,从而避免内存泄露和其他潜在问题。利用useEffect第二个参数 优化性能: 只有 数组里面的count 变革了useEffect才会调用。利用 useMemo 优化性能:在 ExpensiveComputation 组件中,我们定义了一个耗时的打算函数 computeFactorial。通过 useMemo 钩子,我们可以将这个打算结果缓存起来,只有当依赖项 count 发生变革时才重新打算,从而避免了不必要的重新打算。实战运用-自定义Hooks来一个比较常用的关于列表要求的Hooks 封装,为了方便大家理解以是功能只封装了比较范例的利用场景,实际操作中还可以加入要求参数拦截修正等等功能。
下面代码紧张用到了 ElementPlus Vue3,首先新建一个hooks 文件 useTable.ts
useTable.ts
代码解读
复制代码
import { ref, reactive } from 'vue'; import { debounce } from "lodash-es" interface TableType { manual?: boolean; //是否手动触发 requestFn: Function,//要求接口方法 defaultParams?: Record<string, any> //默认参数 } export function useTable({ requestFn, manual, defaultParams }: TableType) { const tableData = ref([]) const pagination = reactive({ pageSize: 10, currentPage: 1, total: 0 }) const queryTableData = async (param?: Record<string, any>) => { let params: any = { pageSize: pagination.pageSize, currentPage: pagination.currentPage, ...defaultParams, ...param }; console.log('params', params) const result = await requestFn(params) if (result) { tableData.value = result.list pagination.total = result.total } } // 添加防抖 const debouncedSearch = debounce((params?: Record<string, any>) => queryTableData(params), 500); // 页数切换 function handleCurrentChange(current: number) { pagination.currentPage = current debouncedSearch(); } // size切换 function handleSizeChange(size: number) { pagination.pageSize = size; debouncedSearch(); } // 条件 查询 function querySearch(params?: Record<string, any>) { debouncedSearch(params); } if (!manual) { //自动实行 debouncedSearch() } return { tableData, pagination, querySearch, handleSizeChange, handleCurrentChange, queryTableData, }; }
再来创建一个Vue 文件引入我们的 hooks
ini
代码解读
复制代码
<template> <div> <el-form :inline="true" :model="formInline" class="demo-form-inline"> <el-form-item label="姓名"> <el-input v-model="formInline.user" placeholder="请输入姓名" clearable /> </el-form-item> <el-form-item label="城市"> <el-select v-model="formInline.region" placeholder="请选择城市" style="width: 200px;" clearable > <el-option label="Zone one" value="上海" /> <el-option label="Zone two" value="北京" /> </el-select> </el-form-item> <el-form-item label="韶光"> <el-date-picker v-model="formInline.date" type="date" placeholder="请选择韶光" clearable /> </el-form-item> <el-form-item> <el-button type="primary" @click="querySearch(formInline)">查询</el-button> </el-form-item> </el-form> <el-table :data="tableData" border style="width: 100%"> <el-table-column prop="user" label="姓名" /> <el-table-column prop="region" label="城市" /> <el-table-column prop="date" label="韶光" /> </el-table> <div style="display: flex;justify-content: flex-end;padding: 10px;"> <el-pagination v-model:current-page="pagination.currentPage" :page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </div> </template> <script lang="ts" setup> import {reactive} from "vue"; import {useTable} from './useTable.ts' const formInline = reactive({ user: "", region: "", date: "", }); // 仿照接口要求 const requestFn = ()=>{ return new Promise((resolve)=>{ const total = Math.floor(Math.random() 101) //天生随机数 0 -100 const list = [] for (let index = 0; index < 10; index++) { list.push({ user:"测试"+index, region:2, date:"2024-07-31" }) } const resultData={ total, size:10, list:list } resolve(resultData) }) } const {tableData,pagination,handleSizeChange,handleCurrentChange,querySearch} = useTable({requestFn,defaultParams:{age:1}}) </script>
运行效果如下
代码功能讲解:
把页面列表查询等比较常用的功能封装到Hooks 里面,方便其他页面引用。把页码切换,size 切换功能等也封装过去。利用 lodash debounce函数给我们要求接口添加防抖功能,防止频繁要求。总结通过上面内容 我们可以理解到Vue3 hooks 供应了一种灵巧高效的办法来管理组件的状态和逻辑。比较 Vue 2 的 Options API,Composition API 和 hooks 供应了更强的功能,避免了命名冲突和逻辑混乱。通过 自定义 hooks,开拓者可以更方便地复用逻辑和管理繁芜组件。
作为前端开拓我认为节制 hooks 的用法,和思想是必不可少的,当然现在也有很多比较成熟的Hooks库,例如 ahooks和 vueuse等,推举学习节制。
原文链接:https://juejin.cn/post/7397424623958425638