Vue 3 与 Notification API 高级应用
1. 概述
Notification API 允许网页向用户显示系统通知,即使应用在后台运行也能推送消息。Vue 3 与 Notification API 结合,可以构建出具有实时通知功能的应用,如聊天应用、新闻推送、任务提醒等。本集将深入探讨 Notification API 的高级特性,并学习如何在 Vue 3 中优雅地封装和使用这些功能。
1.1 什么是 Notification API?
Notification API 是浏览器提供的 Web API,用于向用户显示系统级别的通知。这些通知会显示在操作系统的通知中心,无论浏览器窗口是否处于活动状态。它支持自定义标题、内容、图标、声音等,并能处理用户交互事件。
1.2 应用场景
- 聊天应用的新消息通知
- 新闻和内容推送
- 任务提醒和日历事件
- 实时数据更新(如股票、天气)
- 系统状态和错误提示
- 电子商务订单状态更新
- 社交媒体互动通知
1.3 Vue 3 中的优势
- Composition API 允许将通知逻辑封装为可复用的 composables
- 响应式系统可以实时更新通知状态
- 生命周期钩子可以妥善管理通知资源
- TypeScript 支持提供了更好的类型安全性
- 与 PWA 结合可以实现离线通知
2. 核心知识
2.1 Notification API 基础
Notification API 主要通过 Notification 构造函数和 Notification 静态属性提供,包含以下核心功能:
// 请求通知权限
const permission = await Notification.requestPermission();
// 检查当前权限状态
const permission = Notification.permission;
// 创建并显示通知
const notification = new Notification(title, options);
// 关闭通知
notification.close();2.2 通知权限
Notification API 需要用户授权才能显示通知,权限状态包括:
default:用户尚未做出选择granted:用户允许显示通知denied:用户拒绝显示通知
2.3 通知选项
创建通知时可以设置多种选项:
interface NotificationOptions {
body?: string; // 通知内容
icon?: string; // 通知图标 URL
badge?: string; // 通知徽章 URL(移动端)
tag?: string; // 通知标签,相同标签的通知会替换
data?: any; // 附加数据
lang?: string; // 通知语言
vibrate?: number[]; // 振动模式 [持续时间, 间隔, 持续时间...]
sound?: string; // 通知声音 URL
renotify?: boolean; // 是否替换现有通知时重新通知
requireInteraction?: boolean; // 是否需要用户手动关闭
silent?: boolean; // 是否静音通知
timestamp?: number; // 通知时间戳
actions?: NotificationAction[]; // 通知操作按钮
}
interface NotificationAction {
action: string; // 操作标识符
title: string; // 操作按钮文本
icon?: string; // 操作按钮图标
}2.4 创建 Notification Composable
我们可以创建一个 useNotification composable 来封装 Notification API:
// composables/useNotification.ts
import { ref, onMounted, onUnmounted } from 'vue';
export interface NotificationOptions extends globalThis.NotificationOptions {
/** 是否自动请求权限 */
autoRequestPermission?: boolean;
/** 通知点击回调 */
onClick?: (event: Event, notification: Notification) => void;
/** 通知关闭回调 */
onClose?: (event: Event, notification: Notification) => void;
/** 通知错误回调 */
onError?: (event: Event, notification: Notification) => void;
/** 通知显示回调 */
onShow?: (event: Event, notification: Notification) => void;
}
export interface UseNotificationReturn {
/** 通知权限状态 */
permission: ref<NotificationPermission>;
/** 是否已授权 */
isGranted: ref<boolean>;
/** 显示通知 */
show: (title: string, options?: NotificationOptions) => Notification | null;
/** 请求权限 */
requestPermission: () => Promise<NotificationPermission>;
/** 关闭所有通知 */
closeAll: () => void;
}
let notificationInstances: Notification[] = [];
export function useNotification(options: NotificationOptions = {}): UseNotificationReturn {
const permission = ref<NotificationPermission>(Notification.permission);
const isGranted = ref(permission.value === 'granted');
// 清理通知实例
const cleanupNotification = (notification: Notification) => {
const index = notificationInstances.indexOf(notification);
if (index > -1) {
notificationInstances.splice(index, 1);
}
};
// 请求权限
const requestPermission = async () => {
if (!('Notification' in window)) {
console.error('This browser does not support desktop notification');
return 'denied';
}
const result = await Notification.requestPermission();
permission.value = result;
isGranted.value = result === 'granted';
return result;
};
// 显示通知
const show = (title: string, showOptions: NotificationOptions = {}) => {
if (permission.value !== 'granted') {
console.error('Notification permission not granted');
return null;
}
const mergedOptions = { ...options, ...showOptions };
const { onClick, onClose, onError, onShow, ...notificationOptions } = mergedOptions;
try {
const notification = new Notification(title, notificationOptions);
notificationInstances.push(notification);
// 绑定事件
if (onClick) {
notification.addEventListener('click', (event) => onClick(event, notification));
}
if (onClose) {
notification.addEventListener('close', (event) => {
onClose(event, notification);
cleanupNotification(notification);
});
}
if (onError) {
notification.addEventListener('error', (event) => {
onError(event, notification);
cleanupNotification(notification);
});
}
if (onShow) {
notification.addEventListener('show', (event) => onShow(event, notification));
}
// 自动关闭(如果设置了 duration)
if (showOptions.duration) {
setTimeout(() => {
notification.close();
}, showOptions.duration);
}
return notification;
} catch (error) {
console.error('Error showing notification:', error);
return null;
}
};
// 关闭所有通知
const closeAll = () => {
notificationInstances.forEach(notification => {
try {
notification.close();
} catch (error) {
console.error('Error closing notification:', error);
}
});
notificationInstances = [];
};
onMounted(() => {
if (options.autoRequestPermission && permission.value === 'default') {
requestPermission();
}
});
onUnmounted(() => {
closeAll();
});
return {
permission,
isGranted,
show,
requestPermission,
closeAll
};
}2.5 处理通知事件
通知支持多种事件,我们可以在 composable 中统一处理:
// 在通知创建后绑定事件
notification.addEventListener('click', (event) => {
// 点击通知时触发
window.focus();
notification.close();
});
notification.addEventListener('close', (event) => {
// 通知关闭时触发
});
notification.addEventListener('error', (event) => {
// 通知出错时触发
});
notification.addEventListener('show', (event) => {
// 通知显示时触发
});3. 最佳实践
3.1 用户体验优化
- 提供清晰的通知内容和操作选项
- 避免过度发送通知,设置合理的频率限制
- 允许用户自定义通知偏好(如声音、振动、频率)
- 实现通知分组,避免通知泛滥
- 提供通知历史记录,允许用户查看错过的通知
- 确保通知内容与应用状态同步
3.2 权限管理
- 仅在必要时请求通知权限
- 提供清晰的解释,说明为什么需要通知权限
- 允许用户随时更改通知设置
- 处理权限被拒绝的情况,提供优雅降级
- 不要反复请求权限,这会导致用户体验差
3.3 性能优化
- 限制同时显示的通知数量
- 及时清理不再需要的通知实例
- 优化通知图标和声音资源,减少加载时间
- 考虑使用 Service Worker 处理后台通知
- 避免在通知中包含过多复杂内容
3.4 跨浏览器兼容性
- 检查
Notification是否存在 - 考虑浏览器对不同通知选项的支持差异
- 为不支持的浏览器提供降级方案
- 测试移动端和桌面端的表现
- 考虑使用第三方库(如 OneSignal、Firebase Cloud Messaging)处理复杂推送需求
3.5 安全性考虑
- 确保通知内容来自可信来源
- 避免在通知中包含敏感信息
- 验证通知数据的完整性
- 考虑使用加密保护通知内容
- 实现通知撤销机制,防止错误信息传播
4. 常见问题与解决方案
4.1 权限被拒绝
问题:用户拒绝了通知权限,导致无法显示通知。
解决方案:
- 提供清晰的解释,说明为什么需要通知权限
- 允许用户在设置中重新启用通知
- 实现优雅降级,如在应用内显示通知
<template>
<div class="notification-settings">
<h3>通知设置</h3>
<div v-if="!isGranted">
<p>启用通知以接收实时更新</p>
<button @click="requestPermission">允许通知</button>
</div>
<div v-else>
<p>通知已启用</p>
<button @click="openBrowserSettings">更改设置</button>
</div>
</div>
</template>
<script setup lang="ts">
import { useNotification } from './composables/useNotification';
const { isGranted, requestPermission } = useNotification();
const openBrowserSettings = () => {
// 提示用户在浏览器设置中更改通知权限
alert('请在浏览器设置中管理通知权限');
};
</script>4.2 通知不显示
问题:权限已授予,但通知仍然不显示。
解决方案:
- 检查浏览器是否支持 Notification API
- 确保通知选项设置正确
- 检查浏览器和操作系统的通知设置
- 考虑浏览器的 "请勿打扰" 模式
- 实现调试机制,记录通知相关事件
4.3 点击通知无反应
问题:点击通知后没有预期的行为。
解决方案:
- 确保正确绑定了
click事件监听器 - 在点击事件中调用
window.focus()激活应用 - 处理通知的
data属性,根据数据执行相应操作 - 测试不同浏览器的点击行为差异
4.4 移动端兼容性问题
问题:通知在桌面端正常,但在移动端不显示或表现异常。
解决方案:
- 检查移动端浏览器对 Notification API 的支持
- 优化通知图标和徽章,适配移动端显示
- 考虑使用 PWA 和 Service Worker 实现可靠的移动端通知
- 测试不同移动设备和操作系统的表现
5. 高级学习资源
5.1 官方文档
5.2 第三方库
- OneSignal - 跨平台推送通知服务
- Firebase Cloud Messaging - Google 提供的推送服务
- vue-notification - Vue 通知组件
- Novu - 开源通知基础设施
5.3 相关标准
- Push API - 用于接收服务器推送的消息
- Service Worker API - 用于后台处理通知
- Web App Manifest - 用于配置 PWA 通知设置
6. 实践练习
6.1 练习 1:创建通知组件
目标:创建一个可复用的 Vue 3 通知组件。
要求:
- 使用
useNotificationcomposable 实现通知功能 - 支持自定义标题、内容、图标和操作按钮
- 实现通知权限管理
- 支持通知队列和自动关闭
- 实现通知历史记录
代码框架:
<template>
<div class="notification-component">
<button @click="showNotification" :disabled="!isGranted">
发送通知
</button>
<div v-if="!isGranted">
<button @click="requestPermission">允许通知</button>
</div>
<!-- 通知历史记录 -->
<div class="notification-history">
<h3>通知历史</h3>
<div v-for="(notification, index) in notificationHistory" :key="index">
<div class="history-item">
<h4>{{ notification.title }}</h4>
<p>{{ notification.body }}</p>
<span>{{ new Date(notification.timestamp).toLocaleString() }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useNotification } from './composables/useNotification';
// 实现通知组件
</script>6.2 练习 2:实现推送通知
目标:结合 Push API 和 Service Worker 实现服务器推送通知。
要求:
- 注册 Service Worker 处理推送事件
- 实现推送订阅和取消订阅功能
- 处理服务器推送的通知
- 支持自定义推送数据和操作
提示:
- 需要一个后端服务来发送推送消息
- 使用 VAPID 密钥进行推送认证
- 在 Service Worker 中处理
push事件
6.3 练习 3:创建通知中心
目标:创建一个完整的通知中心应用。
要求:
- 支持多种类型的通知(消息、提醒、系统通知)
- 实现通知分类和筛选
- 支持通知标记为已读/未读
- 支持通知删除和清空
- 实现通知设置自定义
- 支持通知音效和振动设置
提示:
- 使用 localStorage 或 IndexedDB 存储通知历史
- 实现响应式设计,适配不同设备
- 考虑性能优化,尤其是在通知数量较多时
7. 总结
本集深入探讨了 Vue 3 与 Notification API 的高级应用,包括:
- Notification API 的核心概念和使用方法
- 创建可复用的
useNotificationcomposable - 最佳实践,如用户体验优化、权限管理和性能优化
- 常见问题的解决方案
- 高级学习资源和实践练习
通过本集的学习,您应该能够熟练地在 Vue 3 应用中集成 Notification API,构建出功能丰富的实时通知系统。在实际开发中,还需要考虑浏览器兼容性、移动端表现和与后端服务的集成等因素,以确保通知功能的可靠性和良好的用户体验。