- 人工智能
- AI Agent
- MCP 服务
- 工具调用
- GUI 自动化
- 语音
- 数字人
- 交互助手
【免费下载链接】airi
💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。
本文讲解 VueUse 的useTitlecomposable —— 一个把document.title变为响应式状态的轻量工具。在 AIRI 的多个 Vue 3 应用(stage-web、stage-pocket、stage-tamagotchi 等)中,各应用index.html目前只有静态<title>(如AIRI),尚未接入动态标题管理;通过useTitle可以基于路由、消息数等响应式状态实时改写浏览器标签页标题,同时借助titleTemplate统一品牌后缀、借助restoreOnUnmount防止组件卸载后标题污染。读完本文,你将掌握useTitle的全部 API 形态、选项语义与类型约束,并能直接在自己的 Vue 3 组件中落地可复用的动态标题方案。
背景:为什么需要响应式标题
浏览器标签页标题(document.title)是 SPA 中最直观的"当前状态"指示器:新消息提醒、正在处理的页面、未保存的变更,都应该反映在标题上。在 AIRI 仓库中,多个前端应用都依赖@vueuse/core(见 apps/stage-pocket/package.json、apps/stage-tamagotchi/package.json、apps/component-calling/package.json 等),且各应用入口 HTML 仅声明了静态标题,例如 apps/stage-pocket/index.html 的<title>AIRI</title>。这意味着标题一旦设置便不会随页面交互变化。useTitle提供了一条更简洁、可维护的替代路径:让标题成为响应式数据的一部分,随ref/computed自动同步。
需要特别注意的是,useTitle属于 VueUse 的 Browser 分类(见 SKILL.md 的函数索引),它直接操作document.title,因此与 SSR 不兼容:在服务端渲染期间你的值只会被客户端应用。如果项目引入 SSR,需要改用其他 head 管理方案。
基本用法:读取与设置标题
useTitle的默认调用不需要任何参数,返回一个可直接读写的响应式ref:
import { useTitle } from '@vueuse/core' const title = useTitle() console.log(title.value) // 打印当前 document.title title.value = 'Hello' // 立即改写页面标题这段代码背后是标准的ref语义:读取title.value得到当前标题,写入title.value触发document.title同步更新。由于返回对象是Ref<string | null | undefined>,你也可以在<script setup>中直接解构后在模板或逻辑里复用,实现标题的"单一数据源"管理。
设置初始标题
如果希望在调用时就立即把标题设为指定值,直接把字符串作为第一个参数传入即可:
import { useTitle } from '@vueuse/core' const title = useTitle('New Title')这等价于先读取当前标题再写回,但更简洁。传入的初始值同时也会成为返回ref的初始值,后续读写都围绕它进行。
响应式绑定:让标题随 ref / computed 联动
useTitle的第一个参数接受MaybeRef<string | null | undefined>,即普通字符串、ref或computed都可以。当传入一个响应式来源时,标题会在该来源变化时自动更新。这是最常见的实战形态,例如把标题绑定到未读消息数:
import { useTitle } from '@vueuse/core' import { computed, shallowRef } from 'vue' const messages = shallowRef(0) const title = computed(() => { return !messages.value ? 'No message' : `${messages.value} new messages` }) useTitle(title) // document.title 会与 ref "title" 保持一致这里computed负责把数值派生为标题字符串,useTitle只做同步,职责单一。消息数变化 →computed重算 → 标签页标题自动刷新,全程无需手写watch或事件监听。该模式同样适用于路由驱动:例如在 apps/stage-pocket/src/main.ts 的router.afterEach钩子旁,可以引入一个基于当前路由 meta 的computed,配合useTitle让每个页面切换后标题自动反映当前页面名称。
统一品牌后缀:titleTemplate
多个页面共用一个应用时,通常希望标题统一带上站点名称,例如对话页面 | AIRI。useTitle支持 Vue Meta 风格的标题模板(titleTemplate),模板中的%s占位符会被替换为实际标题:
import { useTitle } from '@vueuse/core' const title = useTitle('New Title', { titleTemplate: '%s | My Awesome Website', })执行后标签页标题会显示为New Title | My Awesome Website。对于 AIRI 这类多应用仓库,可以借此把所有页面的标题统一为页面名 | AIRI,与现有静态<title>AIRI</title>(见 apps/stage-web/index.html)的品牌一致性无缝衔接。
titleTemplate的类型为MaybeRef<string> | ((title: string) => string),这意味着它既可以是字符串模板,也可以是接收当前标题并返回完整标题的函数,适合需要条件拼接或国际化(i18n)的场景。
监听外部标题变化:observe
默认情况下useTitle只响应由它自己驱动的值变化。若希望反过来观察document.title被其他代码(第三方脚本、浏览器扩展、其他模块)修改的情况,可以开启observe选项:
const title = useTitle(undefined, { observe: true, })observe基于MutationObserver监听document.title(实际上监听的是包含标题文本的<title>元素所在节点),任何外部改写都会反向同步回返回的ref,形成双向同步。默认值为false。
两个互斥选项:observe 与 titleTemplate
observe与titleTemplate在类型层面被设计为二选一的互斥关系(详见下文的联合类型声明),不可同时启用:
::: warningobserve与titleTemplate不兼容。 :::
原因从实现语义上可以理解:titleTemplate会改写写出的标题(写入的是模板展开后的结果),此时再观察原始document.title会造成环路或语义混乱;而observe模式下标题以 DOM 中的实际值(而非模板)为唯一事实来源。
组件卸载时还原标题:restoreOnUnmount
restoreOnUnmount用于解决一个常见的 SPA 隐患:组件设置了动态标题,但当组件卸载(如用户离开页面)后标题却停留在最后的状态,导致后续页面顶着错误的标题。启用后,useTitle会在组件卸载(onScopeDispose)时把标题还原为调用前的原始值:
const title = useTitle('Hello', { restoreOnUnmount: true, })它的类型不仅仅是布尔值,还支持传入自定义还原函数,让你决定还原成什么:
restoreOnUnmount?: false | ((originalTitle: string, currentTitle: string) => string | null | undefined)还原函数接收两个参数:originalTitle(组件挂载前的原始标题)与currentTitle(当前标题),返回值为还原后的新标题;返回null/undefined时使用默认还原逻辑。这允许做精细化控制,例如"仅当当前标题仍是由本组件设置时才还原,否则保持现状"。
选项速查表
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
observe | boolean | false | 使用MutationObserver观察document.title的外部变化并反向同步;与titleTemplate互斥 |
titleTemplate | MaybeRef<string> \| ((title: string) => string) | '%s' | 标题模板字符串(%s为占位符)或生成完整标题的函数;与observe互斥 |
restoreOnUnmount | false \| ((originalTitle, currentTitle) => string \| null \| undefined) | — | 组件卸载时是否/如何还原标题 |
document | Document(继承自ConfigurableDocument) | 当前document | 指定要操作的目标文档对象,便于在 iframe、测试环境等场景中替换 |
其中document选项来自ConfigurableDocument类型的继承(UseTitleOptions = ConfigurableDocument & UseTitleOptionsBase),VueUse 中大量 Browser composable 都遵循这一约定,便于单元测试中注入 mock 文档。
返回类型解读
useTitle有两个重载,返回值随调用形态不同而变化:
export type UseTitleReturn = | ComputedRef<string | null | undefined> | Ref<string | null | undefined> // 传入 ReadonlyRefOrGetter 时 useTitle( newTitle: ReadonlyRefOrGetter<string | null | undefined>, options?: UseTitleOptions, ): ComputedRef<string | null | undefined> // 不传或传入 MaybeRef 时 useTitle( newTitle?: MaybeRef<string | null | undefined>, options?: UseTitleOptions, ): Ref<string | null | undefined>可以推断:当第一个参数是ref或 getter 时,返回ComputedRef(只读的派生标题视图,最终值与传入来源一致);当传入普通字符串或省略参数时,返回可写的Ref,写入操作直接作用于document.title。
完整类型声明
以下是本技能文档(.agents/skills/vueuse-functions/references/useTitle.md)中给出的官方类型声明,可直接作为集成时的类型契约:
export type UseTitleOptionsBase = { /** * Restore the original title when unmounted * @param originTitle original title * @returns restored title */ restoreOnUnmount?: | false | (( originalTitle: string, currentTitle: string, ) => string | null | undefined) } & ( | { /** * Observe `document.title` changes using MutationObserve * Cannot be used together with `titleTemplate` option. * * @default false */ observe?: boolean } | { /** * The template string to parse the title (e.g., '%s | My Website') * Cannot be used together with `observe` option. * * @default '%s' */ titleTemplate?: MaybeRef<string> | ((title: string) => string) } ) export type UseTitleOptions = ConfigurableDocument & UseTitleOptionsBase export type UseTitleReturn = | ComputedRef<string | null | undefined> | Ref<string | null | undefined>值得注意的设计点是:UseTitleOptionsBase中observe与titleTemplate被建模为互斥的联合类型分支,这是把"两个选项不能同时使用"的约束直接编码进类型系统,让错误配置在编译期就被拦截,而不是留到运行时才暴露。
落地建议:在 AIRI 多应用仓库中的实践模式
结合仓库现状(多个应用已安装@vueuse/core、入口 HTML 仅有静态标题、apps/stage-pocket/src/main.ts 已有路由钩子基础设施),推荐以下实践路径:
- 品牌统一:在
main.ts或全局布局组件中调用一次useTitle('', { titleTemplate: '%s | AIRI' }),为全站标题注入统一后缀。 - 路由联动:利用
router.afterEach获取to.meta.title,配合useTitle传入computed,实现页面级动态标题;由于useTitle不兼容 SSR,且这些应用均为纯客户端渲染(main.ts中无 SSR 逻辑),可以安全使用。 - 会话级提醒:在聊天等实时场景中,仿照"未读消息数"示例,用
computed派生"新消息"提示并写入标题,提升用户注意力引导。 - 局部组件:若某个悬浮窗、开发工具面板需要临时改写标题,启用
restoreOnUnmount: true,保证组件卸载后标题自动还原,避免状态残留。
useTitle本身是 SKILL.md 中标记为AUTO(适用即用)的 Browser 类函数,只要项目已安装@vueuse/core,即可按上文任一形态直接使用,无需额外安装依赖。
- 人工智能
- AI Agent
- MCP 服务
- 工具调用
- GUI 自动化
- 语音
- 数字人
- 交互助手
【免费下载链接】airi
💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。
相关推荐
VueUse 的 useTitle:打造响应式页面标题的完整实战指南
VueUse 的 useTitle:打造响应式页面标题的完整实战指南 导读 useTitle 是 VueUse(Vue 3 Composition API 工具
前端如何用 Video2X 3 步跑通视频超分辨率完整实战指南
如何用 Video2X 3 步跑通视频超分辨率完整实战指南 Video2X 是一个基于机器学习的视频超分辨率与帧插值框架:用 Real ESRGAN、Anime
音视频视频处理图像处理人工智能QuickRecorder macOS 录屏工具完整上手:一条命令装好,5 分钟录出第一条片
QuickRecorder macOS 录屏工具完整上手:一条命令装好,5 分钟录出第一条片 QuickRecorder 是一款给 macOS 用户用的轻量录屏
屏幕录制音视频桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考