vue3中封装hooks
有这样的一个封装
// import { ref } from 'vue';// /**
// * boolean组合式函数
// * @param initValue 初始值
// */
// export default function useBoolean(initValue = false) {
// const bool = ref(initValue);// function setBool(value: boolean) {
// bool.value = value;
// }
// function setTrue() {
// setBool(true);
// }
// function setFalse() {
// setBool(false);
// }
// function toggle() {
// setBool(!bool.value);
// }// return {
// bool,
// setBool,
// setTrue,
// setFalse,
// toggle
// };
// }import useBoolean from './use-boolean';export default function useLoading(initValue = false) {const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue);return {loading,startLoading,endLoading};
}// const { loading, startLoading, endLoading } = useLoading();
这是一个使用 Vue 3 组合式 API(Composition API) 编写的自定义 Hook(组合式函数)结构,采用了 函数封装与复用 的设计模式。整体结构清晰、可复用性强,体现了现代前端开发中“逻辑抽象”的思想。
我们来逐步解析这个结构:
🌐 整体结构概览
import useBoolean from './use-boolean';export default function useLoading(initValue = false) {const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue);return {loading,startLoading,endLoading};
}
🔍 分析:useLoading
的作用
useLoading
是一个自定义组合式函数(Custom Composition Function),它的目的是:
封装“加载状态”相关的逻辑,提供简洁易用的 API 来控制
loading
状态。
它本身不直接操作 DOM 或响应数据,而是复用另一个更通用的 useBoolean
函数来管理布尔值状态。
🔗 依赖:useBoolean
函数(被注释掉的部分)
虽然被注释了,但它是核心基础。我们先还原它的功能:
import { ref } from 'vue';export default function useBoolean(initValue = false) {const bool = ref(initValue);function setBool(value: boolean) {bool.value = value;}function setTrue() {setBool(true);}function setFalse() {setBool(false);}function toggle() {setBool(!bool.value);}return {bool,setBool,setTrue,setFalse,toggle};
}
这个函数是一个通用的布尔状态管理器,返回:
bool
: 响应式布尔值(ref<boolean>
)setBool(value)
: 设置为任意布尔值setTrue()
: 设为true
setFalse()
: 设为false
toggle()
: 切换状态
✨ useLoading
如何利用 useBoolean
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue);
这里使用了 ES6 解构赋值 + 别名(alias) 技术:
原名(来自 useBoolean ) | 别名(在 useLoading 中使用) | 含义 |
---|---|---|
bool | loading | 表示当前是否处于加载状态 |
setTrue | startLoading | 开始加载(设为 true) |
setFalse | endLoading | 结束加载(设为 false) |
💡 这是一种“语义化封装”:将通用的布尔操作包装成具有业务含义的名称。
1.为什么不用普通变量?
我们来看一个反例:
// ❌ 错误示例:不用 ref
let bool = false;function setTrue() {bool = true; // 普通赋值
}
问题来了:
- 这个
bool
是一个普通的 JavaScript 变量。 - 当你在 Vue 组件中使用它时,Vue 根本不知道这个变量发生了变化。
- 所以即使你调用了
setTrue()
,页面上的视图不会更新!
👉 Vue 的响应式系统只追踪通过 ref
、reactive
等方式定义的响应式数据。
2. ref
如何实现响应式?
当你用 ref(initValue)
创建变量时,Vue 做了这些事:
- 把这个值包装成一个带有
.value
的对象。 - 在背后监听
.value
的读取和写入操作(通过get
/set
代理)。 - 一旦
.value
被修改,Vue 就知道“这个数据变了”,然后重新渲染依赖它的模板。
const bool = ref(false);// 在模板中使用:
// <div v-if="bool">正在加载...</div>// 调用 setTrue()
setTrue(); // → bool.value = true → Vue 检测到变化 → 更新视图
3. 在 useBoolean
中的作用
export default function useBoolean(initValue = false) {const bool = ref(initValue); // ✅ 响应式布尔值function setBool(value: boolean) {bool.value = value; // 修改响应式数据}return {bool, // 返回响应式引用,供外部使用setBool,setTrue,setFalse,toggle};
}
这样,任何使用这个 Hook 的组件都可以:
- 读取
bool.value
(在 JS 中)或bool
(在模板中自动解包) - 调用
setTrue()
等方法改变状态 - 页面自动更新
4. 模板中使用 ref
时的“自动解包”
有趣的是,在模板中使用 ref
时,Vue 会自动帮你解开 .value
:
<script setup>
import useBoolean from './use-boolean';
const { bool, toggle } = useBoolean();
</script><template><!-- 不需要写 bool.value,Vue 自动解包 --><p>{{ bool ? '开' : '关' }}</p><button @click="toggle">切换</button>
</template>
但在 JS/TS 中操作时,必须手动加 .value
:
bool.value = true; // 必须 .value
📦 最终返回的 API
return {loading,startLoading,endLoading
};
调用者可以这样使用:
const { loading, startLoading, endLoading } = useLoading(false);// 开始请求
async function fetchData() {startLoading(); // 显示加载中...try {const data = await api.getData();} finally {endLoading(); // 隐藏加载状态}
}
🏗️ 架构设计思想总结
特性 | 说明 |
---|---|
✅ 逻辑复用 | useLoading 复用了 useBoolean ,避免重复写 ref 和 setXXX |
✅ 语义清晰 | 将 setTrue 改名为 startLoading ,让调用者更容易理解用途 |
✅ 职责分离 | useBoolean 负责基础逻辑,useLoading 负责业务语义 |
✅ 类型友好(TypeScript) | 返回的对象结构明确,便于类型推导 |
✅ 可扩展性 | 可以继续扩展如 toggleLoading 或添加延迟、防抖等特性 |
🔄 类比理解(类比 React)
如果你熟悉 React,这类似于:
// 自定义 Hook
function useBoolean(init) { ... }
function useLoading(init) { return useBoolean(init); }
只不过 Vue 的 ref
+ reactive
提供了更细粒度的响应式系统。
✅ 使用建议
你可以进一步增强 useLoading
:
export default function useLoading(initValue = false) {const { bool: loading, setTrue: startLoading, setFalse: endLoading, toggle } = useBoolean(initValue);// 可选:带自动结束的 loadingfunction withLoading(promise) {startLoading();return Promise.resolve(promise).finally(endLoading);}return {loading,startLoading,endLoading,toggle, // 也可以暴露 togglewithLoading // 高阶用法:自动管理异步加载};
}
然后:
withLoading(fetchData()); // 自动开始和结束 loading