【PWA学习与实践】(7)使用Notification API来进行消息提醒

《PWA学习与实践》系列文章已整理至gitbook - PWA学习手册,文字内容已同步至learning-pwa-ebook。转载请注明做者与出处。javascript

本文是《PWA学习与实践》系列的第七篇文章。前端

PWA做为时下最火热的技术概念之一,对提高Web应用的安全、性能和体验有着很大的意义,很是值得咱们去了解与学习。对PWA感兴趣的朋友欢迎关注《PWA学习与实践》系列文章。java

本文中的代码能够在learning-pwa的notification分支上找到(git clone后注意切换到notification分支)。git

1. 引言

在第五篇文章《Web中进行服务端消息推送》中,我介绍了如何使用Push API进行服务端消息推送。提到Push就不得不说与其联系紧密的另外一个API——Notification API。它让咱们能够在“网站外”显示消息提示:github

即便当你切换到其余Tab,也能够经过提醒交互来快速让用户回到你的网站;甚至当用户离开当前网站,仍然能够收到系统的提醒消息,而且能够经过消息提醒快速打开你的网站。chrome

Notification的功能自己与Push并不耦合,你彻底能够只使用Notification API或者Push API来构建Web App的某些功能。所以,本文会先介绍如何使用Notification API。而后,做为Notification的“黄金搭档”,本文还会介绍如何组合使用Push & Notification(消息推送与提醒)。json

2. 使用Notification API

在这第二节里,咱们先来了解如何独立使用Notification功能。相较于第五篇中的Push功能,Notification API更加简洁易懂。promise

2.1. 获取提醒权限

首先,进行调用消息提醒API须要得到用户的受权。浏览器

在调用Notification相关API以前,须要先使用Notification对象上的静态方法Notification.requestPermission()来获取受权。因为Notification.requestPermission()在某些版本浏览器中会接收一个回调函数(Notification.requestPermission(callback))做为参数,而在另外一些浏览器版本中会返回一个promise,所以将该方法进行包装,统一为promise调用:缓存

// index.js
function askPermission() {
    return new Promise(function (resolve, reject) {
        var permissionResult = Notification.requestPermission(function (result) {
            resolve(result);
        });
  
        if (permissionResult) {
            permissionResult.then(resolve, reject);
        }
    }).then(function (permissionResult) {
        if (permissionResult !== 'granted') {
            throw new Error('We weren\'t granted permission.');
        }
    });
}


registerServiceWorker('./sw.js').then(function (registration) {
    return Promise.all([
        registration,
        askPermission()
    ])
 })
复制代码

咱们建立了一个askPermission()方法来统一Notification.requestPermission()的调用形式,并在Service Worker注册完成后调用该方法。调用Notification.requestPermission()获取的permissionResult可能的值为:

  • denied:用户拒绝了通知的显示
  • granted:用户容许了通知的显示
  • default:由于不知道用户的选择,因此浏览器的行为与denied时相同

chrome中,能够在chrome://settings/content/notifications里进行通知的设置与管理。

2.2. 设置你的提醒内容

获取用户受权后,咱们就能够经过registration.showNotification()方法进行消息提醒了。

当咱们注册完Service Worker后,then方法的回调函数会接收一个registration参数,经过调用其上的showNotification()方法便可触发提醒:

// index.js
registerServiceWorker('./sw.js').then(function (registration) {
    return Promise.all([
        registration,
        askPermission()
    ])
}).then(function (result) {
    var registration = result[0];
    /* ===== 添加提醒功能 ====== */
    document.querySelector('#js-notification-btn').addEventListener('click', function () {
        var title = 'PWA即学即用';
        var options = {
            body: '邀请你一块儿学习',
            icon: '/img/icons/book-128.png',
            actions: [{
                action: 'show-book',
                title: '去看看'
            }, {
                action: 'contact-me',
                title: '联系我'
            }],
            tag: 'pwa-starter',
            renotify: true
        };
        registration.showNotification(title, options);
    });
    /* ======================= */
})
复制代码

上面这段代码为页面上的button添加了一个click事件监听:当点击后,调用registration.showNotification()方法来显示消息提醒,该方法接收两个参数:titleoptiontitle用来设置该提醒的主标题,option中则包含了一些其余设置。

  • body:提醒的内容
  • icon:提醒的图标
  • actions:提醒能够包含一些自定义操做
  • tag:至关因而ID,经过该ID标识能够操做特定的notification
  • renotify:是否容许重复提醒,默认为false。当不容许重复提醒时,同一个tag的notification只会显示一次

注意,因为不一样浏览器中,对于option属性的支持状况并不相同。部分属性在一些浏览器中并不支持。

2.3. 捕获用户的点击

在上一部分中,咱们已经为Web App添加了提醒功能。点击页面中的“提醒”按钮,系统就会弹出提醒框,并展现相关提醒消息。

然而更多的时候,咱们并不只仅但愿只展现有限的信息,更但愿能引导用户进行交互。例如推荐一本新书,让用户点击阅读或购买。在上一部分咱们设置的提醒框中,包含了“去看看”和“联系我”两个按钮选项,那么怎么作才能捕获用户的点击操做,而且知道用户点击了哪一个呢?这一小节,就会告诉你如何实现。

还记的上一部分里咱们定义的actions么?

…
actions: [{
    action: 'show-book',
    title: '去看看'
    }, {
    action: 'contact-me',
    title: '联系我'
}]
…
复制代码

为了可以响应用户对于提醒框的点击事件,咱们须要在Service Worker中监听notificationclick事件。在该事件的回调函数中咱们能够获取点击的相关信息:

// sw.js
self.addEventListener('notificationclick', function (e) {
    var action = e.action;
    console.log(`action tag: ${e.notification.tag}`, `action: ${action}`);
    
    switch (action) {
        case 'show-book':
            console.log('show-book');
            break;
        case 'contact-me':
            console.log('contact-me');
            break;
        default:
            console.log(`未处理的action: ${e.action}`);
            action = 'default';
            break;
    }
    e.notification.close();
});
复制代码

e.action获取的值,就是咱们在showNotification()中定义的actions里的action。所以,经过e.action就能够知道用户点击了哪个操做选项。注意,当用户点击提醒自己时,也会触发notificationclick,可是不包含任何action值,因此在代码中将其置于default默认操做中。

如今试一下,咱们就能够捕获用户对于不一样选项的点击了。点击后在Console中会有不一样的输出。

2.4. Service Worker与client通讯

到目前为止,咱们已经能够顺利得给用户展现提醒,而且在用户操做提醒后准确捕获到用户的操做。然而,还缺最重要的一步——针对不一样的操做,触发不一样的交互。例如,

  • 点击提醒自己会弹出书籍简介;
  • 点击“看一看”会给用户展现本书的详情;
  • 点击“联系我”会向应用管理者发邮件等等。

这里有个很重要的地方:咱们在Service Worker中捕获用户操做,可是须要在client(这里的client是指前端页面的脚本环境)中触发相应操做(调用页面方法/进行页面跳转…)。所以,这就须要让Service Worker与client进行通讯。通讯包括下面两个部分:

  1. 在Service Worker中使用Worker的postMessage()方法来通知client:
// sw.js
self.addEventListener('notificationclick', function (e) {
    …… // 略去上一节内容
    
    e.waitUntil(
        // 获取全部clients
        self.clients.matchAll().then(function (clients) {
            if (!clients || clients.length === 0) {
                return;
            }
            clients.forEach(function (client) {
                // 使用postMessage进行通讯
                client.postMessage(action);
            });
        })
    );
});
复制代码
  1. 在client中监听message事件,判断data,进行不一样的操做:
// index.js
navigator.serviceWorker.addEventListener('message', function (e) {
    var action = e.data;
    console.log(`receive post-message from sw, action is '${e.data}'`);
    switch (action) {
        case 'show-book':
            location.href = 'https://book.douban.com/subject/20515024/';
            break;
        case 'contact-me':
            location.href = 'mailto:someone@sample.com';
            break;
        default:
            document.querySelector('.panel').classList.add('show');
            break;
    }
});
复制代码

当用户点击提醒后,咱们在notificationclick监听中,将action经过postMessage()通讯给client;而后在client中监听message事件,基于action(e.data)来进行不一样的操做(跳转到图书详情页/发送邮件/显示简介面板)。

至此,一个比较简单与完整的消息提醒(Notification)功能就完成了。

然而目前的消息提醒还存在必定的局限性。例如,只有在用户访问网站期间才能有机会触发提醒。正如本文一开始所说,Push & Notification的结合将会帮助咱们构筑一个强大推送与提醒功能。下面就来看下它们的简单结合。

3. 消息推送与提醒

在第五篇《Web中进行服务端消息推送》最后,咱们经过监听push事件来处理服务端推送:

// sw.js
self.addEventListener('push', function (e) {
    var data = e.data;
    if (e.data) {
        data = data.json();
        console.log('push的数据为:', data);
        self.registration.showNotification(data.text);        
    } 
    else {
        console.log('push没有任何数据');
    }
});
复制代码

简单修改以上代码,与咱们本文中的提醒功能相结合:

// sw.js
self.addEventListener('push', function (e) {
    var data = e.data;
    if (e.data) {
        data = data.json();
        console.log('push的数据为:', data);
        var title = 'PWA即学即用';
        var options = {
            body: data,
            icon: '/img/icons/book-128.png',
            image: '/img/icons/book-521.png', // no effect
            actions: [{
                action: 'show-book',
                title: '去看看'
            }, {
                action: 'contact-me',
                title: '联系我'
            }],
            tag: 'pwa-starter',
            renotify: true
        };
        self.registration.showNotification(title, options);        
    } 
    else {
        console.log('push没有任何数据');
    }
});
复制代码

使用Push来向用户推送信息,并在Service Worker中直接调用Notification API来展现该信息的提醒框。这样,即便是在用户关闭该Web App时,依然能够收到提醒,相似于Native中的消息推送与提醒。

咱们还能够将这个功能再丰富一些。因为用户在关闭该网站时仍然能够收到提醒,所以加入一些更强大功能:

  • 当用户切换到其余Tab时,点击提醒会马上回到网站的tab;
  • 当用户未打开该网站时,点击提醒能够直接打开网站。
// sw.js
self.addEventListener('notificationclick', function (e) {
    var action = e.action;
    console.log(`action tag: ${e.notification.tag}`, `action: ${action}`);
    
    switch (action) {
        case 'show-book':
            console.log('show-book');
            break;
        case 'contact-me':
            console.log('contact-me');
            break;
        default:
            console.log(`未处理的action: ${e.action}`);
            action = 'default';
            break;
    }
    e.notification.close();

    e.waitUntil(
        // 获取全部clients
        self.clients.matchAll().then(function (clients) {
            if (!clients || clients.length === 0) {
                // 当不存在client时,打开该网站
                self.clients.openWindow && self.clients.openWindow('http://127.0.0.1:8085');
                return;
            }
            // 切换到该站点的tab
            clients[0].focus && clients[0].focus();
            clients.forEach(function (client) {
                // 使用postMessage进行通讯
                client.postMessage(action);
            });
        })
    );
});
复制代码

注意这两行代码,第一行会在网站关闭时打开该网站,第二行会在存在tab时自动切换到网站的tab。

self.clients.openWindow && self.clients.openWindow('http://127.0.0.1:8085');

clients[0].focus && clients[0].focus();
复制代码

4. MacOS Safari中的Web Notification

看一下Web Notification的兼容性

目前移动端浏览器广泛还不支持该特性。可是在Mac OS上的safari里面是支持该特性的,不过其调用方式与上文代码有些不太同样。在safari中使用Web Notification不是调用registration.showNotification()方法,而是须要建立一个Notification对象。

// index.js
……
document.querySelector('#js-notification-btn').addEventListener('click', function () {
    var title = 'PWA即学即用';
    var options = {
        body: '邀请你一块儿学习',
        icon: '/img/icons/book-128.png',
        actions: [{
            action: 'show-book',
            title: '去看看'
        }, {
            action: 'contact-me',
            title: '联系我'
        }],
        tag: 'pwa-starter',
        renotify: true
    };
    // registration.showNotification(title, options);

    // 使用Notification构造函数建立提醒框
    // 而非registration.showNotification()方法
    var notification = new Notification(title, options);
});
……
复制代码

Notification对象继承自EventTarget接口,所以在safari中须要经过添加click事件的监听来触发提醒框的交互操做:

// index.js
notification.addEventListener('click', function (e) {
    document.querySelector('.panel').classList.add('show');
});
复制代码

该功能示例能够在learn-pwa/notify4safari中找到。

5. 写在最后

Web Notification是一个很是强大的API,尤为在和Push结合后,为WebApp带来了相似Native的丰富能力。

本文中全部的代码示例都可以在learn-pwa/notification上找到。

若是你喜欢或想要了解更多的PWA相关知识,欢迎关注我,关注《PWA学习与实践》系列文章。我会总结整理本身学习PWA过程的遇到的疑问与技术点,并经过实际代码和你们一块儿实践。

到目前为止,咱们已经学习了Manifest离线缓存消息推送、消息提醒、Debug等一些基础知识。在下一篇文章里,咱们会继续了解与学习PWA中的一个重要功能——后台同步。

《PWA学习与实践》系列

参考资料

相关文章
相关标签/搜索