一、前言
上篇懂了"为什么",本篇开始"动手"。选两个最简单的下手:useTitle (响应式改网页标题)和 useToggle(布尔开关)。麻雀虽小五脏俱全------返回值怎么设计、初始值怎么兜底、怎么命名,组合式函数的全部基础规范都在这两个十行函数里。
二、useTitle:让网页标题变成响应式数据
目标 API :const title = useTitle(),改 title.value 页面标签实时变。
先想清楚实现三步:读当前标题作初始值 → 数据变时写回 document.title → 返回数据。
js
// src/composables/useTitle.js
import { ref, watch } from 'vue'
export function useTitle(newTitle) {
// 初始值:传了就用传的,没传就读当前标题
const title = ref(newTitle ?? document.title)
// 数据变 → 写回浏览器标签
watch(title, (val) => {
document.title = val
}, { immediate: true }) // immediate:进来先同步一次
return title
}
vue
<!-- 使用 -->
<template>
<p>页面标签实时变化:</p>
<button @click="title = `(${count} 条未读)我的应用`">模拟新消息</button>
<button @click="title = '我的应用'">清除未读</button>
<p>{{ count }} 条未读</p>
</template>
<script setup>
import { ref } from 'vue'
import { useTitle } from '@/composables/useTitle'
const count = ref(3)
const title = useTitle('我的应用')
</script>
设计复盘(每个细节都有讲究):
newTitle ?? document.title:参数可选的兜底写法,两种用法都支持------useTitle()纯托管、useTitle('xxx')带初始值immediate: true:保证"进来那一刻"标题就被同步(比如接管了一个已经乱七八糟的标题)- 返回 ref 而不是字符串 :返回 ref 调用方才能改、页面才响应------组合式函数返回的一律是"活的数据"
【截图位置:点击按钮后浏览器标签页标题变化的效果】
三、useToggle:布尔开关 + 任意两值切换
目标 API :const [state, toggle] = useToggle()------数组解构的返回形式(和 012 篇 toRefs 的对象形式并列,两种都要会)。
js
// src/composables/useToggle.js
import { ref } from 'vue'
export function useToggle(init = false) {
const state = ref(init)
/**
* 切换状态
* @param {boolean} [value] 可选:直接指定目标值
*/
function toggle(value) {
// 传了值就用传的,没传就取反
state.value = value ?? !state.value
}
return [state, toggle]
}
vue
<template>
<!-- 场景 1:开关弹窗 -->
<button @click="toggle()">弹窗开关:{{ open ? '开' : '关' }}</button>
<!-- 场景 2:明文/密文切换,直接指定值 -->
<input :type="showPwd ? 'text' : 'password'" v-model="pwd" />
<button @click="toggle(true)">显示明文</button>
<button @click="toggle(false)">隐藏</button>
</template>
<script setup>
import { useToggle } from '@/composables/useToggle'
const [open, toggle] = useToggle() // 弹窗开关
const [showPwd, setPwdVisible] = useToggle(false) // 密码可见性
const pwd = ref('')
</script>
设计复盘:
- 返回数组
[state, toggle]:调用方解构时可以随意重命名(toggle/setPwdVisible),数组按位置对号,特别适合"一数据一方法"的小函数 value ?? !state.value:toggle() 无参取反、toggle(true) 定点设置,一个函数两种用法- JSDoc 注释:写上参数说明,同事用你的函数时 IDE 有提示------协作从注释开始
对象形式返回(项目里统一风格时用):
js
// 同一逻辑的另一种返回形态
return { state, toggle }
// 使用:const { state, toggle } = useToggle()
数组形式 适合"元素少且位置固定";对象形式适合成员多、要按名取用的场景。VueUse 两种混用(简单函数用数组,复杂的用对象),你的项目定死一种即可。
四、进阶:把两个函数组合起来
组合式函数的精髓是可以互相组合。写一个 useDisclosure(弹窗控制器),内部用 useToggle:
js
// src/composables/useDisclosure.js
import { useToggle } from './useToggle'
/**
* 弹窗/抽屉控制器
* @returns {{ isOpen: import('vue').Ref<boolean>, open: Function, close: Function, toggle: Function }}
*/
export function useDisclosure(init = false) {
const [isOpen, rawToggle] = useToggle(init)
return {
isOpen,
open: () => rawToggle(true),
close: () => rawToggle(false),
toggle: () => rawToggle()
}
}
vue
<template>
<button @click="open">打开确认弹窗</button>
<div v-if="isOpen" class="mask" @click.self="close">
<div class="box">
确定要删除吗?
<button @click="close">取消</button>
<button @click="onConfirm">确定</button>
</div>
</div>
</template>
<script setup>
import { useDisclosure } from '@/composables/useDisclosure'
const { isOpen, open, close } = useDisclosure()
function onConfirm() {
console.log('执行删除')
close()
}
</script>
<style scoped>
.mask { position: fixed; inset: 0; background: rgba(0,0,0,.5);
display: flex; align-items: center; justify-content: center; }
.box { background: #fff; padding: 20px; border-radius: 8px; }
</style>
open / close / toggle 语义分明的弹窗控制器,一个项目里每个弹窗都用它------这就是 031 篇 Teleport 弹窗的官方搭档。函数调用函数,能力不断叠加,这就是"组合式"三个字的含义。
【截图位置:弹窗打开、点遮罩关闭的运行效果】
五、踩坑记录
- useTitle 在 SSR/无 window 环境报错 :
document不存在------需要兼容 SSR 时加typeof document !== 'undefined'判断(本系列走 SPA,先知道有这回事) - 返回了死值 :
return title.value返回的是当时的字符串,调用方怎么改页面都不动------永远返回 ref - toggle 里的坑 :
value ?? !state.value若写成value || !state.value,传false会被 || 当 falsy 吞掉变成取反------nullish 合并符(??)才区分 false 和空 - 忘记可选括号 :
useToggle定义成function useToggle(init)但调用写useToggle()------init 是 undefined,ref(undefined)的布尔判断是 false,恰好能用但类型不干净,养成init = false给默认值的习惯 - 一个文件塞十个函数:useTitle 和 useToggle 混在一个文件里 export------一个函数一个文件,index.js 统一导出,按需 import
六、今日小结
- useTitle:
ref + watch(immediate)三件套,返回活 ref 是铁律 - useToggle:
[state, toggle]数组返回便于重命名;value ?? !value兼容定点设置 - 返回形态二选一:数组(小而固定)/ 对象(成员多),项目内统一
- useDisclosure 示范函数组合函数------"组合式"的真正含义
下篇预告
继续造下一组:useMouse / useWindowSize------第一次在组合式函数里操作 window 事件监听,生命周期清理的模板写法会固化成肌肉记忆。下一篇 037。