uni-app 通知系统
核心知识点讲解
1. 通知系统概述
通知系统是移动应用中非常重要的功能,它可以:
- 及时向用户传递重要信息
- 提高用户活跃度和留存率
- 实现个性化的用户提醒
- 支持应用内和应用外的消息触达
2. 通知类型
在 uni-app 中,通知主要分为以下几种类型:
2.1 本地通知
- 定义:由应用本地触发的通知,不需要网络连接
- 特点:实时性高,无需后端支持,适合定时提醒等场景
- API:使用
uni.createLocalNotification()创建
2.2 推送通知
- 定义:由服务器通过推送服务发送的通知
- 特点:需要后端支持,可以实现远程消息触达
- 服务:支持极光推送、个推、Firebase Cloud Messaging 等
2.3 应用内通知
- 定义:仅在应用内部显示的通知
- 特点:不需要系统权限,实现灵活
- 场景:应用内消息、更新提醒、活动通知等
3. 通知触发条件
通知的触发条件主要包括:
- 时间触发:定时提醒、闹钟、日程安排
- 事件触发:用户操作、系统事件、网络状态变化
- 状态触发:订单状态更新、消息到达、任务完成
- 位置触发:基于地理位置的通知(地理围栏)
4. 通知展示方式
通知的展示方式有多种形式:
- 系统通知栏:显示在设备的通知中心
- 应用内弹窗:在应用内部弹出的通知
- 顶部通知条:显示在应用顶部的通知条
- 徽章通知:应用图标上的数字徽章
- 声音和振动:通过声音和振动提醒用户
5. 通知权限管理
在实现通知功能时,需要注意权限管理:
- iOS 权限:需要在
info.plist中配置通知权限 - Android 权限:需要在
AndroidManifest.xml中配置通知权限 - 权限申请:使用
uni.requestSubscribeMessage()申请订阅消息权限
实用案例分析
案例:实现完整的通知系统
功能需求
我们需要实现一个包含以下功能的通知系统:
- 本地定时通知
- 应用内通知
- 服务器推送通知
- 通知管理中心
实现方案
1. 本地通知实现
// 1. 检查通知权限
uni.getSetting({
success: (res) => {
if (!res.authSetting['notification']) {
// 申请通知权限
uni.requestSubscribeMessage({
tmplIds: [], // 模板ID数组
success: (res) => {
console.log('订阅消息成功', res);
},
fail: (err) => {
console.log('订阅消息失败', err);
}
});
}
}
});
// 2. 创建本地通知
function createLocalNotification(title, content, time) {
uni.createLocalNotification({
title: title,
content: content,
payload: {"key": "value"},
// 定时通知,单位毫秒
// 如果不设置,则立即发送
// time: Date.now() + time
});
}
// 3. 监听通知点击事件
uni.onLocalNotificationClick((res) => {
console.log('点击了本地通知', res);
// 可以根据通知的 payload 进行相应的操作
uni.navigateTo({
url: '/pages/detail/detail?id=' + res.payload.id
});
});2. 应用内通知组件
<template>
<view class="notification-container">
<!-- 顶部通知条 -->
<view v-if="showTopNotification" class="top-notification">
<text>{{ topNotification.content }}</text>
<view class="close-btn" @click="closeTopNotification">×</view>
</view>
<!-- 通知中心 -->
<view class="notification-center">
<view class="notification-header">
<text class="title">通知中心</text>
<text class="clear-btn" @click="clearAllNotifications">清空</text>
</view>
<view class="notification-list">
<view
v-for="(notification, index) in notifications"
:key="index"
class="notification-item"
:class="{ 'unread': !notification.read }"
@click="markAsRead(index)"
>
<view class="notification-icon">
<text>{{ getNotificationIcon(notification.type) }}</text>
</view>
<view class="notification-content">
<text class="notification-title">{{ notification.title }}</text>
<text class="notification-desc">{{ notification.content }}</text>
<text class="notification-time">{{ formatTime(notification.time) }}</text>
</view>
<view v-if="!notification.read" class="unread-dot"></view>
</view>
<view v-if="notifications.length === 0" class="empty-notification">
<text>暂无通知</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
showTopNotification: false,
topNotification: {
content: ''
},
notifications: [
{
id: 1,
title: '系统通知',
content: '您的账号已成功登录',
type: 'system',
time: Date.now(),
read: false
},
{
id: 2,
title: '活动通知',
content: '限时优惠活动开始了',
type: 'activity',
time: Date.now() - 3600000,
read: true
}
]
};
},
methods: {
// 显示顶部通知
showTopNotification(content) {
this.topNotification.content = content;
this.showTopNotification = true;
// 3秒后自动关闭
setTimeout(() => {
this.closeTopNotification();
}, 3000);
},
// 关闭顶部通知
closeTopNotification() {
this.showTopNotification = false;
},
// 标记为已读
markAsRead(index) {
this.notifications[index].read = true;
},
// 清空所有通知
clearAllNotifications() {
this.notifications = [];
},
// 获取通知图标
getNotificationIcon(type) {
const icons = {
system: '📢',
activity: '🎉',
message: '💬',
order: '📦'
};
return icons[type] || '📢';
},
// 格式化时间
formatTime(time) {
const date = new Date(time);
return date.toLocaleString();
},
// 添加通知
addNotification(notification) {
this.notifications.unshift({
id: Date.now(),
read: false,
time: Date.now(),
...notification
});
// 显示顶部通知
this.showTopNotification(notification.content);
}
}
};
</script>
<style scoped>
.notification-container {
padding: 20rpx;
}
/* 顶部通知条样式 */
.top-notification {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #007AFF;
color: white;
padding: 15rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 9999;
animation: slideDown 0.3s ease;
}
.close-btn {
font-size: 32rpx;
font-weight: bold;
}
/* 通知中心样式 */
.notification-center {
margin-top: 20rpx;
}
.notification-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.title {
font-size: 28rpx;
font-weight: bold;
}
.clear-btn {
font-size: 24rpx;
color: #007AFF;
}
/* 通知列表样式 */
.notification-list {
background-color: #F5F5F5;
border-radius: 10rpx;
overflow: hidden;
}
.notification-item {
display: flex;
align-items: center;
padding: 20rpx;
background-color: white;
margin-bottom: 1rpx;
position: relative;
}
.notification-item.unread {
background-color: #F8F8F8;
}
.notification-icon {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background-color: #F0F0F0;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
font-size: 32rpx;
}
.notification-content {
flex: 1;
}
.notification-title {
font-size: 26rpx;
font-weight: 500;
margin-bottom: 8rpx;
display: block;
}
.notification-desc {
font-size: 22rpx;
color: #666;
margin-bottom: 8rpx;
display: block;
}
.notification-time {
font-size: 20rpx;
color: #999;
}
.unread-dot {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 10rpx;
height: 10rpx;
border-radius: 50%;
background-color: #FF3B30;
}
.empty-notification {
padding: 60rpx 0;
text-align: center;
color: #999;
}
/* 动画效果 */
@keyframes slideDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
</style>3. 推送通知集成
以极光推送为例,实现服务器推送通知:
// 1. 安装极光推送插件
// 在 HBuilderX 中搜索并安装 "极光推送" 插件
// 2. 初始化极光推送
const jpush = uni.requireNativePlugin('JG-JPush');
jpush.init();
// 3. 注册设备
jpush.getRegistrationID({
success: (res) => {
console.log('RegistrationID:', res.registerID);
// 将 RegistrationID 发送到服务器
// 用于服务器向特定设备发送推送
}
});
// 4. 监听推送消息
jpush.addNotificationListener({
success: (res) => {
console.log('收到推送消息:', res);
// 处理推送消息
if (res.notification) {
// 显示通知
uni.showToast({
title: res.notification.alert,
duration: 2000
});
}
}
});
// 5. 监听点击通知事件
jpush.addReceiveOpenNotificationListener({
success: (res) => {
console.log('点击了推送通知:', res);
// 处理点击通知后的跳转逻辑
if (res.extras && res.extras.url) {
uni.navigateTo({
url: res.extras.url
});
}
}
});4. 通知管理中心
<template>
<view class="notification-settings">
<view class="setting-section">
<text class="section-title">通知设置</text>
<!-- 通知类型开关 -->
<view class="setting-item">
<text class="setting-label">系统通知</text>
<switch
:checked="notificationSettings.system"
@change="toggleSetting('system', $event)"
/>
</view>
<view class="setting-item">
<text class="setting-label">活动通知</text>
<switch
:checked="notificationSettings.activity"
@change="toggleSetting('activity', $event)"
/>
</view>
<view class="setting-item">
<text class="setting-label">消息通知</text>
<switch
:checked="notificationSettings.message"
@change="toggleSetting('message', $event)"
/>
</view>
<view class="setting-item">
<text class="setting-label">订单通知</text>
<switch
:checked="notificationSettings.order"
@change="toggleSetting('order', $event)"
/>
</view>
</view>
<view class="setting-section">
<text class="section-title">通知样式</text>
<!-- 通知样式设置 -->
<view class="setting-item">
<text class="setting-label">声音</text>
<switch
:checked="notificationSettings.sound"
@change="toggleSetting('sound', $event)"
/>
</view>
<view class="setting-item">
<text class="setting-label">振动</text>
<switch
:checked="notificationSettings.vibration"
@change="toggleSetting('vibration', $event)"
/>
</view>
<view class="setting-item">
<text class="setting-label">徽章</text>
<switch
:checked="notificationSettings.badge"
@change="toggleSetting('badge', $event)"
/>
</view>
</view>
<view class="setting-section">
<text class="section-title">通知权限</text>
<button type="primary" @click="checkNotificationPermission">检查通知权限</button>
<button type="default" @click="requestNotificationPermission">申请通知权限</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
notificationSettings: {
system: true,
activity: true,
message: true,
order: true,
sound: true,
vibration: true,
badge: true
}
};
},
mounted() {
// 从本地存储加载设置
this.loadSettings();
},
methods: {
// 切换设置
toggleSetting(key, event) {
this.notificationSettings[key] = event.detail.value;
// 保存设置到本地存储
this.saveSettings();
},
// 保存设置
saveSettings() {
uni.setStorageSync('notificationSettings', this.notificationSettings);
},
// 加载设置
loadSettings() {
const settings = uni.getStorageSync('notificationSettings');
if (settings) {
this.notificationSettings = settings;
}
},
// 检查通知权限
checkNotificationPermission() {
uni.getSetting({
success: (res) => {
const hasPermission = res.authSetting['notification'] || res.authSetting['notify'];
uni.showToast({
title: hasPermission ? '已获得通知权限' : '未获得通知权限',
duration: 2000
});
}
});
},
// 申请通知权限
requestNotificationPermission() {
uni.requestSubscribeMessage({
tmplIds: [], // 模板ID数组
success: (res) => {
console.log('申请通知权限成功', res);
uni.showToast({
title: '申请通知权限成功',
duration: 2000
});
},
fail: (err) => {
console.log('申请通知权限失败', err);
uni.showToast({
title: '申请通知权限失败',
duration: 2000,
icon: 'none'
});
}
});
}
}
};
</script>
<style scoped>
.notification-settings {
padding: 20rpx;
}
.setting-section {
background-color: white;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 26rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: block;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #F0F0F0;
}
.setting-item:last-child {
border-bottom: none;
}
.setting-label {
font-size: 24rpx;
color: #333;
}
button {
margin-top: 20rpx;
}
</style>代码优化建议
1. 通知去重和合并
当有多个相似通知时,可以考虑去重和合并,避免通知轰炸:
// 通知去重函数
function deduplicateNotifications(notifications) {
const uniqueNotifications = [];
const seen = new Set();
for (const notification of notifications) {
const key = `${notification.type}-${notification.content}`;
if (!seen.has(key)) {
seen.add(key);
uniqueNotifications.push(notification);
}
}
return uniqueNotifications;
}
// 通知合并函数
function mergeSimilarNotifications(notifications) {
const merged = [];
const groups = {};
for (const notification of notifications) {
if (!groups[notification.type]) {
groups[notification.type] = [];
}
groups[notification.type].push(notification);
}
// 合并同一类型的通知
for (const type in groups) {
const group = groups[type];
if (group.length > 1) {
// 创建合并通知
merged.push({
id: Date.now(),
title: `${type}通知`,
content: `您有 ${group.length} 条新${type}通知`,
type: type,
time: Date.now(),
read: false,
count: group.length
});
} else if (group.length === 1) {
merged.push(group[0]);
}
}
return merged;
}2. 通知频率限制
为了避免过多的通知打扰用户,可以实现通知频率限制:
// 通知频率限制
const notificationLimits = {
minInterval: 60000, // 最小通知间隔(毫秒)
maxPerHour: 10, // 每小时最大通知数
maxPerDay: 50 // 每天最大通知数
};
// 通知记录
const notificationHistory = [];
// 检查通知是否可以发送
function canSendNotification() {
const now = Date.now();
// 过滤出最近一小时的通知
const recentNotifications = notificationHistory.filter(n => now - n.time < 3600000);
// 过滤出今天的通知
const today = new Date().setHours(0, 0, 0, 0);
const todayNotifications = notificationHistory.filter(n => n.time >= today);
// 检查最近一次通知的时间间隔
const lastNotification = notificationHistory[notificationHistory.length - 1];
const timeSinceLast = lastNotification ? now - lastNotification.time : Infinity;
// 检查限制条件
if (timeSinceLast < notificationLimits.minInterval) {
return false;
}
if (recentNotifications.length >= notificationLimits.maxPerHour) {
return false;
}
if (todayNotifications.length >= notificationLimits.maxPerDay) {
return false;
}
return true;
}
// 发送通知前检查
function sendNotification(notification) {
if (canSendNotification()) {
// 发送通知
console.log('发送通知:', notification);
// 记录通知
notificationHistory.push({
time: Date.now(),
type: notification.type
});
// 限制历史记录长度
if (notificationHistory.length > 100) {
notificationHistory.shift();
}
return true;
} else {
console.log('通知频率过高,跳过发送');
return false;
}
}3. 通知优先级管理
实现通知优先级管理,确保重要通知能够及时送达:
// 通知优先级
const NOTIFICATION_PRIORITY = {
HIGH: 3,
MEDIUM: 2,
LOW: 1
};
// 通知队列
const notificationQueue = [];
// 添加通知到队列
function addToNotificationQueue(notification, priority = NOTIFICATION_PRIORITY.MEDIUM) {
notificationQueue.push({
...notification,
priority,
addedTime: Date.now()
});
// 按优先级排序
notificationQueue.sort((a, b) => b.priority - a.priority);
// 处理队列
processNotificationQueue();
}
// 处理通知队列
function processNotificationQueue() {
if (notificationQueue.length > 0 && canSendNotification()) {
const notification = notificationQueue.shift();
sendNotification(notification);
// 继续处理队列
setTimeout(processNotificationQueue, 1000);
}
}总结
本教程详细介绍了 uni-app 通知系统的实现方法,包括:
- 通知类型:本地通知、推送通知、应用内通知
- 触发条件:时间触发、事件触发、状态触发、位置触发
- 展示方式:系统通知栏、应用内弹窗、顶部通知条、徽章通知
- 权限管理:iOS 和 Android 的通知权限配置和申请
- 完整实现:本地通知、应用内通知、服务器推送通知、通知管理中心
通过本教程的学习,您应该能够:
- 理解通知系统的核心概念和实现原理
- 掌握本地通知和推送通知的实现方法
- 开发功能完整的通知管理中心
- 优化通知系统的用户体验
通知系统是移动应用中非常重要的功能,合理的通知设计可以提高用户活跃度和留存率,为用户提供更好的使用体验。在实现通知功能时,需要注意平衡通知的及时性和用户体验,避免过度打扰用户。