You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PWA通知功能异常排查:为何无法获取通知授权提示?

问题:PWA通知授权请求无法弹出的排查

我开发了一款用于获取服务器最新10条新闻的PWA应用,需求是当服务器推送新新闻时,浏览器或PWA应用能发出通知。但目前无法弹出通知授权请求,请问问题出在哪里?

// Проверка поддержки и регистрация Service Worker
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
      .then(registration => {
        console.log('Service Worker зарегистрирован:', registration);
      })
      .catch(err => {
        console.error('Ошибка регистрации Service Worker:', err);
      });
}

let latestArticleId = 0;

// Загрузка новостей при загрузке страницы и каждые 10 секунд
document.addEventListener('DOMContentLoaded', () => {
    loadData();
    setInterval(loadData, 10000);

    // Обработчик нажатия на кнопку для запроса разрешения на уведомления
    const enableNotificationsButton = document.getElementById('enable-notifications');
    enableNotificationsButton.addEventListener('click', () => {
        Notification.requestPermission().then(permission => {
            if (permission === "granted") {
                console.log("Разрешение на уведомления получено");
                enableNotificationsButton.style.display = 'none'; // Скрыть кнопку после получения разрешения
            }
        });
    });
});


// Запрос новостей с сервера или из кеша
function loadData() {
    fetch('/api/news')
      .then(response => {
        if (!response.ok) {
          throw new Error('Ошибка сетевого запроса');
        }
        return response.json();
      })
      .then(data => {
        if (data.items.length > 0) {
          updateNewsList(data.items);
          checkForNewArticle(data.items);
        }
      })
      .catch(error => {
        console.error('Ошибка при получении новостей:', error);
        loadFromCache();
      });
}

// Обновление списка новостей на странице
function updateNewsList(articles) {
    const newsContainer = document.getElementById('news-container');
    newsContainer.innerHTML = '';
    articles.slice(0,10).forEach(article => {
      const articleElem = document.createElement('div');
      articleElem.className = 'news-article';
      articleElem.innerHTML = `<h3>${article.title}</h3><p>Опубликовано ${article.author} в ${article.publicationDate}</p>`;
      newsContainer.appendChild(articleElem);
    });
}

// Проверка наличия новых статей
function checkForNewArticle(articles) {
    if (articles[0].id > latestArticleId) {
      latestArticleId = articles[0].id;
      if (document.hidden) {
        showNotification(articles[0].title);
      }
    }
}

// Отображение уведомлений
function showNotification(title) {
    if (Notification.permission === "granted") {
      new Notification("Новая статья!", {
        body: title,
        icon: '/icons/icon-192x192.png'
      });
    } else if (Notification.permission !== "denied") {
      Notification.requestPermission().then(permission => {
        if (permission === "granted") {
          new Notification("Новая статья!", {
            body: title,
            icon: '/icons/icon-192x192.png'
          });
        }
      });
    }
}

// Загрузка новостей из кеша
function loadFromCache() {
    if (!navigator.serviceWorker) {
      console.log('Service Worker не поддерживается этим браузером.');
      return;
    }

    navigator.serviceWorker.controller.postMessage({ type: 'get-cached-news' });

    navigator.serviceWorker.onmessage = event => {
      if (event.data.type === 'cached-news' && event.data.articles) {
        updateNewsList(event.data.articles);
      }
    };
}

核心问题及修复方案

1. 通知权限请求触发时机违反浏览器安全规则

浏览器强制要求Notification.requestPermission()必须由用户主动交互(点击、触摸等)触发,你的代码在showNotification中,当页面隐藏时自动发起权限请求,属于被动触发,浏览器会直接拦截,不会弹出授权框。

修复:
仅保留按钮点击时的权限请求逻辑,showNotification中遇到未授权状态时,改为引导用户点击按钮开启权限:

function showNotification(title) {
  if (Notification.permission === "granted") {
    new Notification("Новая статья!", {
      body: title,
      icon: '/icons/icon-192x192.png'
    });
  } else if (Notification.permission !== "denied") {
    // 显示按钮并引导用户主动触发权限请求
    const enableBtn = document.getElementById('enable-notifications');
    if (enableBtn) {
      enableBtn.style.display = 'block';
      enableBtn.textContent = "点击开启通知,接收新新闻提醒";
    }
  }
}

2. 主线程通知在后台场景下的局限性

当PWA处于后台时,页面主线程可能被浏览器挂起,此时通过主线程创建通知会失效,应该改用Service Worker发送通知。

修复:

  • 主线程向Service Worker发送通知指令
function showNotification(title) {
  if (Notification.permission === "granted") {
    if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
      navigator.serviceWorker.controller.postMessage({
        type: 'show-notification',
        title: "Новая статья!",
        body: title,
        icon: '/icons/icon-192x192.png'
      });
    } else {
      // 降级到主线程通知
      new Notification("Новая статья!", {
        body: title,
        icon: '/icons/icon-192x192.png'
      });
    }
  } else if (Notification.permission !== "denied") {
    const enableBtn = document.getElementById('enable-notifications');
    if (enableBtn) {
      enableBtn.style.display = 'block';
      enableBtn.textContent = "点击开启通知,接收新新闻提醒";
    }
  }
}
  • 在service-worker.js中添加消息监听,由Service Worker发起通知:
self.addEventListener('message', (event) => {
  if (event.data.type === 'show-notification') {
    self.registration.showNotification(event.data.title, {
      body: event.data.body,
      icon: event.data.icon
    });
  }
});

3. 初始ID初始化逻辑缺陷

页面加载时latestArticleId设为0,如果服务器返回的第一条新闻ID也是0,会直接跳过新通知检测。

修复:
首次加载新闻时初始化latestArticleId:

.then(data => {
  if (data.items.length > 0) {
    updateNewsList(data.items);
    // 首次加载时初始化ID,避免误判
    if (latestArticleId === 0) {
      latestArticleId = data.items[0].id;
    } else {
      checkForNewArticle(data.items);
    }
  }
})

4. 按钮元素存在性未做校验

如果页面中不存在enable-notifications按钮,代码会抛出错误,导致后续逻辑中断。

修复:
添加按钮存在性判断:

const enableNotificationsButton = document.getElementById('enable-notifications');
if (enableNotificationsButton) {
  enableNotificationsButton.addEventListener('click', () => {
    Notification.requestPermission().then(permission => {
      if (permission === "granted") {
        console.log("Разрешение на уведомления получено");
        enableNotificationsButton.style.display = 'none';
      }
    });
  });
}

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:53:28