浏览器通知是怎么做到的?前端实现桌面消息提醒

浏览器通知是怎么做到的?前端实现桌面消息提醒

最近看到一些网站在收到新消息时,会在电脑右下角弹出类似系统通知的提醒。比如论坛有新回复、私信、帖子更新时,即使当前页面不在前台,也能看到 Chrome 推出的通知。

很多前端同学第一反应可能是:这是网页里写了一个 fixed 弹窗吗?

其实不是。

这种通知通常是 浏览器原生通知能力。网站拿到用户授权后,通过浏览器的 Notification API 或 Push API,让 Chrome 把消息交给操作系统通知中心展示。

它不是普通弹窗

普通网页弹窗一般是 DOM 元素,比如:

<div class="toast">你有一条新消息</div>

这种弹窗只能显示在网页内部,页面关闭或切到别的软件后,用户就看不到了。

而浏览器通知是系统级展示:

new Notification('你有一条新消息', {
  body: '有人回复了你的帖子',
  icon: '/logo.png'
})

它的样式、位置、关闭按钮都由浏览器和操作系统控制,前端不能像 CSS 那样自由修改。

前端实现分两种情况

第一种是页面还开着,只是用户切到了后台。

这种情况比较简单,前端拿到新消息后直接调用 new Notification() 就可以。

第二种是页面已经关闭了,用户仍然能收到通知。

这种就需要 Service Worker + Push API + 后端推送。前端负责订阅推送并注册 Service Worker,后端负责真正把消息推给浏览器。

页面打开时的实现

先判断浏览器是否支持通知:

function supportNotification() {
  return 'Notification' in window
}

然后通过用户点击申请通知权限:

async function requestNotifyPermission() {
  if (!supportNotification()) return false

  const permission = await Notification.requestPermission()
  return permission === 'granted'
}

注意,权限申请最好放在“开启通知”按钮点击后触发,不建议页面一加载就申请。很多浏览器会拦截这种没有用户行为触发的权限请求。

收到新消息后,可以这样弹通知:

function showNotify(message) {
  if (!supportNotification()) return
  if (Notification.permission !== 'granted') return

  const notice = new Notification(message.title, {
    body: message.body,
    icon: message.icon || '/logo.png',
    tag: message.tag || message.id
  })

  notice.onclick = () => {
    window.focus()

    if (message.url) {
      window.location.href = message.url
    }

    notice.close()
  }
}

如果项目里有 WebSocket,可以在收到消息时调用:

socket.onmessage = event => {
  const data = JSON.parse(event.data)

  if (document.hidden) {
    showNotify({
      id: data.id,
      title: data.title,
      body: data.content,
      icon: data.avatar,
      url: `/message/${data.id}`,
      tag: `message-${data.id}`
    })
  }
}

这里用 document.hidden 判断页面是否在后台。这样可以避免用户正在看页面时,系统通知还一直弹出来,体验会更自然。

页面关闭后也能收到通知

如果希望用户关掉页面后仍然收到消息,就不能只靠 new Notification() 了。

这时候要注册 Service Worker:

const registration = await navigator.serviceWorker.register('/sw.js')

然后订阅 Push:

const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: vapidPublicKey
})

再把订阅信息传给后端保存:

await fetch('/api/push-subscription', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(subscription)
})

Service Worker 里监听推送消息:

self.addEventListener('push', event => {
  const data = event.data.json()

  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: data.icon || '/logo.png',
      tag: data.tag,
      data: {
        url: data.url
      }
    })
  )
})

点击通知后打开对应页面:

self.addEventListener('notificationclick', event => {
  event.notification.close()

  const url = event.notification.data?.url || '/'

  event.waitUntil(
    clients.openWindow(url)
  )
})

前端需要注意的点

浏览器通知必须在 HTTPS 环境下使用,本地 localhost 例外。

通知权限只能由用户授权,前端不能静默开启。

通知样式不能完全自定义,前端主要能控制标题、内容、图标、跳转地址、去重标识等字段。

如果只是页面打开时提醒,用 Notification API + WebSocket 就够了。

如果页面关闭后也要提醒,就必须接入 Service Worker + Push API + 后端推送服务

总结

网页右下角这种系统通知,本质上是浏览器提供的原生能力。

前端要做的事情主要有三步:

  1. 引导用户开启通知权限
  2. 在收到新消息时判断是否需要弹通知
  3. 根据业务场景选择普通 Notification 或 Service Worker Push

对于论坛、聊天、工单、邮箱这类系统来说,浏览器通知是一个很实用的功能。它不复杂,但要注意权限、兼容性和用户体验。尤其是不要滥用通知,否则用户很容易直接把网站通知权限关掉。

posted @ 2026-07-02 14:17  jialiangzai  阅读(3)  评论(0)    收藏  举报