利用 Notifications API 展示桌面消息通知

导读

Notifications API 是 HTML5 新增的桌面通知 API,允许 Web 应用在系统级别发送在页面外部显示的通知。即使浏览器应用处于空闲或在后台运行,也可以向用户发送信息。并且在操作系统的通知管理器中会一直保存,用户可以在消息通知显示后再次查看,是非常友好而而强的交互方式。并且 Notifications API 被编写为通用的,足以与大多数操作系统的通知系统兼容的接口。本文将分享如何利用此 API 展示桌面消息通知。

Notifications API 简介

Notifications API 使用 Notification() 构造函数创建 Notification 对象的新实例。方法如下:

js 复制代码
// 兼容处理
let Notification = window.Notification || window.webkitNotifications

// 创建实例 new Notification(title, options)
new Notification('系统通知', {
    // 文字方向,auto / ltr / rtl
    dir: 'ltr',     
    // 赋予通知一个ID,以便在必要的时候对通知进行刷新、替换或移除           
    tag:  guid(),    
    // 一个图片的URL,将被用于显示通知的图标    
    icon: 'path/to/icon.png',  
    // 通知中额外显示的字符串
    body: 'message',
    // 是否一直保持有效
    requireInteraction: true
})

静态属性

Notification.permission,用来表明用户是否允许当前域显示通知:

  • granted: 用户已经明确的授予了显示通知的权限;.
  • denied: 用户已经明确的拒绝了显示通知的权限;
  • default: 用户还未被询问是否授权; 这种情况下权限将视为 denied;

实例属性

除 Notification.permission 静态属性外,每个 Notification 实例都有:title 、dir 、tag 、icon 、body 和 requireInteraction 这 6个常用的只读属性,各个属性的意义在创建 Notification 对象实例的 options 属性中已经用注释说明了,这里就不赘述了。

静态方法

Notification. requestPermission(),用于在当前页面向用户申请显示通知的权限。调用方法如下:

js 复制代码
if (Notification.permission === 'granted') {
    // 弹出消息
} else {
  Notification. requestPermission((permission)=>{
     if(persmission === 'granted'){
        // 弹出消息
     }
  })
}

申请用户授权的时候,浏览器会弹出确认消息提示。如下图:

用户的响应与应用程序一起存储,因此再次调用 Notification.permission 会返回用户的最后选择(granted 或者 denied)。一旦用户授予权限,应用程序就可以显示 Notification 的通知。

注意,这个方法只能被用户行为调用(比如:onclick 事件),并且不能被其他的方式调用。也就是说开发者无法通过JavaScript编程的方式手动设置的方式强制用户允许授权,这是浏览器的一种安全限制措施。 例如:

js 复制代码
// 无法通过 JS 编程的方式强行用户允许弹出消息 
Notification.permission = 'granted'

实例方法

Notification.close(),以编程方式关闭通知实例。

事件处理程序

Notification API 提供了 4 个处理程序:

  • Notification.onclick:click 事件的处理程序。每次用户点击通知时都会触发它。
  • Notification.onclose:close 事件的处理程序。当用户关闭通知时触发。
  • Notification.onerror:error 事件的处理程序。每次通知遇到错误时都会触发它。
  • Notification.onshow:show 事件的处理程序。它在显示通知时触发。

使用 Notifications API 的注意事项

在了解了 Notification API 的属性和方法之后,会发现 Notification API 的接口很简洁,调用起来也很方便。但使用它也有一些需要注意的事项。

安全上下文的限制条件

在 MDN 中关于 Notifications API 文档的一开始,就用警告提示的方式告诉我们:

此功能仅在安全上下文(HTTPS)、某些或所有支持的浏览器中可用。这就意味着要使用 Notifications API 需要调用 API 的站点启用了 HTTPS,否则无法使用 Notifications API(PS:如何在 nginx 服务器启用 HTTPS 将会另外详细介绍)。

处理重复的通知

当应用 Notification API 的系统中会产生大量的通知,而某些情况下对于用户来说,显示大量通知是件令人痛苦的事情。但是系统又必须为用户提示每一条消息通知,以便用户进行下一步的操作。为了避免数以百计的不必要通知铺满用户的桌面,就需要接管一个挂起消息的队列。因此,需要为新建的通知添加一个标记。

js 复制代码
new Notification(title, { 
    // 设置 tid 标记 
    tag: tid 
})

如果有一条通知也具有相同 tid 的标记,并且还没有被显示,那么这条新通知将会替换上一条通知。如果有一条通知具有一个相同的标记,并且已经显示出来了,那么上一条通知将会被关闭,新通知将会被显示出来。这样通过设置 tid 接管挂起消息的队列,从而避免了通知铺满用户的桌面。

Notification API 的兼容处理

并非所有浏览器都将 Notifications API 实现到同一级别,不同的操作系统可能不支持相同的通知功能。因此在使用时候尽量使用所有浏览器都支持的属性和方法。

例如前文中的创建 Notification 实例,获取 Notification 对象就需要处理兼容问题,而创建实例的配置参数也尽量使用各个浏览器都支持的属性(具体的兼容说明参考后文的浏览器情况章节)。

另外,用户可以拒绝显示 Notification 通知的授权,或者用户的浏览器不支持 Notifications API 的时候,需要使用其它的消息显示的替代方案。

浏览器支持情况

Notifications API 的浏览器支持情况(完整的 API 接口使用情况,请参考《MDN Notification 浏览器兼容性》)还是不错的,主流浏览器中除了 IE 不支持,其余的浏览器都支持。

Notifications API 的应用实践

在了解了 Notifications API 相关的基础知识后,接下来就是应用和实践了。

API 封装

在应用 Notifications API 显示通知消息前,我们需要将 Notification API 做一个简单的封装,以便在项目中方便调用:

js 复制代码
/**
 * Notifications API 封装
 * =====================================================================
 * @param {String} title - 通知标题
 * @param {String} msg - 通知正文
 * @param {Object} [options] - 配置参数
 * @returns {Notification}
 */
const notify = (title, msg, options) => {
	const Notification = window.Notification || window.webkitNotifications
	const defaults = {
		// 文字方向,auto / ltr / rtl
		dir: 'ltr',
		// 赋予通知一个ID,以便在必要的时候对通知进行刷新、替换或移除
		tag: new Date().getTime(),
		// 一个图片的URL,将被用于显示通知的图标
		icon: 'path/to/icon.png',
		// 通知中额外显示的字符串
		body: msg,
		// 是否一直保持有效
		requireInteraction: true
	}
	const config = Object.assign(defaults, options)

	// 用户已经授权
	if (Notification.permission === 'granted') {
		return new Notification(title, config)
	} else {
		// 申请用户授权
		Notification.requestPermission((permission) => {
			// 用户同意授权
			if (permission === 'granted') {
				return new Notification(title, config)
			}
		})
	}
}

消息通知的分类

前文介绍处理重复的通知的问题时就提到过需要给消息分类,我们可以按功能模块给消息分类,并且为每个分类设置 msgId。

js 复制代码
import { notify } from '@/notify'

// 此处的 notice 是后台 API 返回的 JSON 格式数据
const success = notice.type === 4
// 成功或者失败的图标路径
const iconPath = success ? '/static/img/notify-success.png' : '/static/img/notify-fail.png'
const url = notice.url

const $notify = notify(notice.title, notice.content, {
  tag: notice.msgId,
  icon: iconPath
})

$notify.onclick = function () {
  $notify.close()

  // 点击通知弹窗,跳转到相应的 url 地址查看 ci 操作运行结果
  if(url){
   location.href = url
  }
}

示例中的 notice 是后端 API 返回的消息信息的数据(是一个对象),msgId 也是后端返回的,同一类的消息 msgId 相同。这样在用户同时收到多个同类消息通知的时候,只会展示一条同一分类的消息,多个消息逐条显示,直到所有消息显示完毕。

如果有其他同学需要使用 Notifications API 展示消息,也可以根据各自的情况,对消息做一个分类,以处理重复的消息展示问题。

渐进增强的消息通知显示交互方式

但凡遇到有兼容性的问题时,都需要在设计时考虑渐进增强的交互设计方案。除了使用 Notifications API 显示消息通知外,同时也需要考虑采用了最常见的模拟消息弹窗的交互方式,以确保用户在无法使用 Notifications API 显示消息通知时也能查看到消息通知。

js 复制代码
// 调用 element-ui 的 Notify 组件显示消息
const $notify = (notice) => {
  const title = notice.title
  const url = notice.url
  // 创建主体内容,带"查看详情"链接
  const $message = h('span', [
    notice.content + ',',
    h(
      'a', {
        attrs: {
          target: notice.target || '_self',
          href: url
        }
      },
      '查看详情'
    )
  ])

  // 没有"查看详情"的URL地址,则只显示消息内容即可
  if (!url) {
    $message = h('span', [msg])
  }

  // Element UI 的消息提示
  if (success) {
    // 弹出成功类型的提示
    this.$notify.success({
      title: title,
      message: $message,
      // 不会自动关闭
      duration: 5000
    })
  } else {
    // 弹出失败类型的提示
    this.$notify.error({
      title: title,
      message: $message,
      duration: 5000
    })
  }
}

采用模拟消息弹窗的交互方式显示消息通知实现起来比较容易,而想做到完美替换 Notifications API 关键在于如何确保用户在打开多个标签页的时候,切换到任何一个操作界面的标签页都可以收到消息。

本文介绍的解决方案是针对使用 VUE 技术栈构建的单页面应用(SPA)的系统。要实现在任何界面都能接收到在其它界面操作的消息通知,就必须在 VUE 应用的主视图框架页面监听接收消息服务推送的消息,这样就可以保证无论路由切换到哪个子模块试图,都可以接收到消息通知。

vue 复制代码
<template>
  <div class="layout">
    <div class="layout__header"></div>
    <div class="layout__body">
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {
  name: "Layout",
  // ... 其它逻辑
  mounted() {
    // 创建与 MQTT 消息服务的连接
    this.connection = MQTT.connect(mqttServerUrl, {
      clientId: perfix + clientId,
      username: perfix + userId,
      password: perfix + pwd,
      clean: true,
      reconnectPeriod: 1000 * 5,
      keepalive: 30,
      protocol: "wss"
    });
    
    // 忽略了订阅消息的逻辑
    // 监听 MTQQ 消息,受到消息后,检测是否是订阅的通知类型的消息
    this.connection.on("message", (topic, notice) => {
      // 是通知类型的消息  
      if (isNoticeTopic(topic)) {
        // 将 MQTT 消息服务返回的二进制信息,转化成 JSON 格式数据  
        const message = JSON.parse(notice.toString());
        
        // this.notify() 方法中就同时包含封装 Notifications API 
        // 的 notify() 和 $notify(),会同时调用显示两种消息通知
        this.notify(message);
      }
    });
  },
};
</script>

最终效果

可以看到,使用 Notification API 显示消息,用户可以在通知弹窗关闭后,在操作系统的通知管理窗口查看之前接受到的消息,不用担心错过什么重要的消息。无论浏览器是否在激活状态,用户也都可以接收到来之系统推送的消息通知。

并且,Notification API 也兼容了所有主流的操作系统(Windows\Mac\Linux)。这也是为什么说使用 Notification API 显示消息是非常友好而强大的交互方式。

总结与思考

随着 Notifications API 的不断更新完善,现在一部分的主流浏览器已经开始支持设置 actions 配置属性,允许用户设置更多的自定义操作,来丰富系统消息通知窗口的交互方式,一进步优化用户体验。

作为前端开发工程师的我��就需要持续关注新的技术更新,在适当的时候应用这些新的特性以更好的提升用户体验。

相关推荐
雪碧聊技术6 分钟前
01-Ajax入门与axios使用、URL知识
前端·javascript·ajax·url·axios库
adminIvan10 分钟前
Element plus使用menu时候如何在折叠时候隐藏掉组件自带的小箭头
前端·javascript·vue.js
会发光的猪。29 分钟前
【 ElementUI 组件Steps 步骤条使用新手详细教程】
前端·javascript·vue.js·elementui·前端框架
我家媳妇儿萌哒哒30 分钟前
el-table合并单元格之后,再进行隔行换色的且覆盖表格行鼠标移入的背景色的实现
前端·javascript·elementui
baiduguoyun1 小时前
react的import 导入语句中的特殊符号
前端·react.js
前端青山1 小时前
webpack指南
开发语言·前端·javascript·webpack·前端框架
NiNg_1_2341 小时前
ECharts实现数据可视化入门详解
前端·信息可视化·echarts
励志前端小黑哥2 小时前
有了Miniconda,再也不用担心nodejs、python、go的版本问题了
前端·python
喵叔哟2 小时前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特2 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts