最简实现是直接赋值document.title,如document.title = count > 0 ? (${count}) 我的应用 : '我的应用';需缓存上一次count防节流,避免setinterval裸轮询,并统一管理权限以防被路由等覆盖。

title 里加未读数的最简实现方式
直接改 document.title 就行,不需要框架、不依赖 DOM 加载完成时机,只要脚本能执行就能写。浏览器对 title 的更新是同步生效的,但要注意:中文系统下部分浏览器(如旧版 Edge)可能在任务栏缩略图里不刷新数字,桌面通知栏倒是基本都支持。
典型写法:
function updateTitleBadge(count) {
const base = '我的应用';
document.title = count > 0 ? `(${count}) ${base}` : base;
}
调用 updateTitleBadge(3) 就会把标题变成 (3) 我的应用。
为什么 setInterval 刷 title 会卡顿或失效
不是 title 更新慢,而是频繁修改触发了浏览器的防抖机制——Chrome 和 Firefox 都会对短时间内连续的 document.title 修改做节流,大约每秒最多更新 1–2 次。如果用轮询接口再立刻改 title,很容易被吞掉中间状态。
- 改法:只在真正有变化时更新,用变量缓存上一次的 count 值
- 别用
setInterval(() => updateTitleBadge(getUnreadCount()), 1000)这种裸轮询 - 推荐配合事件驱动,比如 WebSocket 收到新消息、或者 fetch 完成后才调用更新
微信浏览器和 iOS Safari 的特殊表现
这两个环境对 document.title 的渲染更保守:页面在后台时,iOS Safari 可能完全不更新 title;微信内置浏览器则经常缓存 title 直到用户切回页面,甚至要手动触发一次 history.replaceState 才能“唤醒”刷新。
一款AI工具,主要用于管理 OpenClaw 所使用的来自 OpenRouter 的免费 AI 模型。自动按质量对模型进行排序,配置回退机制以应对速率限制,并更新 opencla...,适合需要提升相关任务效率的用户。
兼容性补救写法(仅限必要场景):
function forceTitleUpdate(newTitle) {
document.title = newTitle;
// iOS & 微信 hack
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = '/favicon.ico';
document.head.appendChild(iframe);
setTimeout(() => document.head.removeChild(iframe), 100);
}
原理是靠 iframe 加载触发页面重绘,但别滥用——它会引发额外请求,且现代 iOS 已逐步修复该问题。
服务端推送未读数时,前端怎么避免 title 被覆盖
很多单页应用会在路由切换或组件卸载时重置 title(比如 React Router 的 useEffect 清理函数里又写了 document.title = '首页'),结果刚设好的 badge 瞬间被抹掉。
- 关键原则:title 控制权必须收口,不能多处分散修改
- 建议封装一个全局 title 管理器,内部维护 baseTitle + badgeCount 两个字段,所有修改都走统一 setter
- 路由层只负责设置 baseTitle,消息模块只负责调用
setTitleBadge(n) - 避免在 useEffect / componentWillUnmount 里硬编码 title 字符串
真正麻烦的从来不是怎么写那行 document.title = ...,而是谁有权限改、什么时候该清空 badge、用户点开消息列表后要不要自动归零——这些逻辑散落在各处时,title 就成了最不可靠的状态显示器。










