如何使用推送通知将 WebApp 转换为 PWA

在本文中,我们将了解如何使用 Firebase 云消息传递将 Web 应用程序或网站转换为带有推送通知的 PWA。

在现代世界中,大多数 Web 应用程序正在转换为 PWA(渐进式 Web 应用程序),因为它提供了离线支持、推送通知、后台同步等功能。 PWA 功能使我们的 Web 应用程序更像原生应用程序,并提供丰富的用户体验。

例如,像 Twitter 和亚马逊这样的大公司已经将他们的 Web 应用程序转换为 PWA 以提高用户参与度。

什么是 PWA?

PWA = (Web App) + (一些原生应用功能)

PWA 是您的同一个 Web 应用程序(HTML+CSS+JS)。 它在所有浏览器上的工作方式与您的 Web 应用程序相同,就像以前一样。 但是当您的网站在现代浏览器上加载时,它可以具有本机功能。 它使您的 Web 应用程序比以前更强大,也使其更具可扩展性,因为我们可以在前端预取和缓存资产,它减少了对后端服务器的请求。

PWA 与 Web App 有何不同

  • 可安装:您的 Web 应用可以像原生应用一样安装
  • 渐进式:与您的 Web 应用程序相同,但具有一些本机功能
  • 原生应用体验:安装后,用户可以像原生应用一样使用和导航 Web 应用。
  • 易于访问:与我们的网络应用程序不同,我们的用户无需在每次访问时都输入网址。 安装后,只需轻轻一按即可打开。
  • 应用程序缓存:在 PWA 之前,我们的 Web 应用程序实现的唯一缓存机制是使用仅对浏览器可用的 HTTP 缓存。 但是使用 PWA,我们可以使用 Web 应用程序中不可用的客户端代码本身来缓存内容。
  • (App/Play) 商店发布:PWA 可以在 Google Play Store 和 IOS App Store 发布。

将您的应用程序转换为 PWA 只会使其更强大。

为什么企业应该考虑 PWA

虽然我们的大多数客户联系我们并要求首先开发 Web App 解决方案,然后他们要求 Android 和 iOS 应用程序。 我们要做的就是由一个单独的团队在 web 应用程序中构建与 Android/IOS 应用程序相同的功能,这需要更多的开发成本和更多的上市时间。

但是有些客户的预算有限,或者有些客户可能认为上市时间对他们的产品更重要。

PWA 功能本身可以满足大多数客户端需求。 对于他们,我们只建议 PWA,如果他们想在 Playstore 中部署,我们建议他们使用 TWA 将他们的 PWA 转换为 Android 应用程序。

如果您的需求确实需要 PWA 无法满足的原生应用程序功能。 客户可以按照自己的意愿去开发这两个应用程序。 但即使在那种情况下。 他们可以在 Play 商店中部署 PWA,直到 Android 开发完成。

示例:泰坦 Eyeplus

最初,他们开发了一个 PWA 应用程序,并使用 TWA(Trusted Web Activity)将其部署在 Play 商店中。 一旦他们完成了他们的 Android 应用程序开发。 他们在 Play 商店中部署了他们真正的 Android 应用程序。 他们使用 PWA 实现了上市时间和开发成本。

PWA 功能

PWA 为我们的 Web 应用程序提供了类似原生应用程序的功能。

主要特点是:

  • 可安装:像本机应用程序一样安装的 Web 应用程序。
  • 缓存:应用程序缓存是可能的,这为我们的应用程序提供了离线支持。
  • 推送通知:可以从我们的服务器发送推送通知,以吸引我们的用户访问我们的网站。
  • 地理围栏:只要设备位置发生变化,就可以通过事件通知应用程序。
  • 支付请求:在您的应用程序中启用支付功能,并像原生应用程序一样提供出色的用户体验。

未来还有更多功能。

其他特点是:

  • 快捷方式:清单文件中添加的可快速访问的 URL。
  • Web Share API:让您的应用程序接收来自其他应用程序的共享数据。
  • 徽章 API:在已安装的 PWA 中显示通知计数。
  • 定期后台同步 API:保存用户的数据,直到它连接到网络。
  • 联系人选择器:用于从用户的手机中选择联系人。
  • 文件选择器:用于访问本地系统/移动设备上的文件

PWA 相对于原生应用程序的优势

本机应用程序比 PWA 性能更好,并且比 PWA 具有更多功能。 但是,它仍然比本机应用程序具有一些优势。

  • PWA 在 Android、IOS、桌面等跨平台上运行。
  • 它降低了您的开发成本。
  • 与本机应用程序相比,易于功能部署。
  • 易于发现,因为 PWA(网站)对 SEO 友好
  • 安全,因为它仅适用于 HTTPS

PWA 相对于原生应用程序的缺点

  • 与本机应用程序相比,可用的功能有限。
  • PWA 功能不保证支持所有设备。
  • PWA 的品牌知名度很低,因为它在应用商店或 Play 商店中不可用。

您可以使用 android 在 Play 商店中将 PWA 部署为 Android 应用程序 可信网络活动 (TWA). 这将有助于您的品牌推广。

将 Web App 转换为 PWA 所需的东西

用于将任何 Web 应用程序或网站转换为 PWA。

  • Service-Worker:任何用于缓存、推送通知的 PWA 应用程序的核心,是我们请求的代理。
  • 清单文件:它包含有关您的 Web 应用程序的详细信息。 它曾经像在主屏幕上下载我们的应用程序一样下载我们的应用程序。
  • 应用程序徽标:应用程序图标的高质量图像 512 x 512 像素。 PWA 在主屏幕、启动屏幕等上需要应用徽标。因此我们必须使用任何工具为我们的 APP 创建一组 1:1 比例的图像。
  • 响应式设计:Web 应用程序应该能够响应不同屏幕尺寸的工作。

什么是服务工作者:

服务工作者(客户端脚本)是您的 Web 应用程序和外部之间的代理,为我们的 Web 应用程序提供推送通知并支持缓存。

Service Worker 独立于主 javascript 运行。 所以它无法访问 DOM API。 它只能访问 索引数据库 API, 获取 API, 缓存存储 API. 但它可以通过消息与主线程通信。

  9 个最佳 Azure 性能监控和故障排除工具

service worker 提供的服务:

  • 拦截来自您的源域的 HTTP 请求。
  • 从您的服务器接收推送通知。
  • 我们的应用程序的离线可用性

Service Worker 控制您的应用程序并可以操纵您的请求,但它独立运行。 因此,出于这个原因,必须使用 HTTPS 启用源域以避免中间人攻击。

什么是清单文件

清单文件 (manifest.json) 包含有关我们的 PWA 应用程序的详细信息,以告知浏览器。

  • 名称:应用程序的名称
  • short_name:我们的应用程序的简称。 如果提供
  • 同时具有属性名称和短名称,浏览器将采用短名称。
  • 描述:描述我们的应用程序的描述。
  • start_url:指定我们的 PWA 启动时应用程序的主页。
  • 图标:用于主屏幕等的 PWA 图像集。
  • background_color:在我们的 PWA 应用程序中设置启动画面的背景颜色。
  • display:自定义我们的浏览器 UI 以显示在我们的 PWA 应用程序中。
  • theme_color:PWA 应用的主题颜色。
  • 范围:我们为 PWA 考虑的应用程序的 URL 范围。 默认为清单文件所在的位置。
  • 快捷方式:我们的 PWA 应用程序的快速链接。

将 Web 应用程序转换为 PWA

出于演示目的,我创建了一个带有静态文件的 techblik.com 网站文件夹结构。

  • index.html – 主页
  • 文章/
    • index.html – 文章页面
  • 作者/
    • index.html – 作者页面
  • 工具/
    • index.html – 工具页面
  • 交易/
    • index.html – 交易页面

如果您已经有任何网站或 Web 应用程序,请尝试按照以下步骤将其转换为 PWA。

为 PWA 创建所需的图像

首先,获取您的应用程序徽标,并以 1:1 的比例将其裁剪为 5 种不同的尺寸。 我用过 https://tools.crawlink.com/tools/pwa-icon-generator/ 快速获得不同的图像尺寸。 所以你也可以使用它。

创建清单文件

其次,使用您的应用程序详细信息为您的 Web 应用程序创建一个 manifest.json 文件。 对于演示,我为 techblik.com 网站创建了一个清单文件。

{
	"name": "techblik.com",
	"short_name": "techblik.com",
	"description": "techblik.com produces high-quality technology & finance articles, makes tools, and APIs to help businesses and people grow.",
	"start_url": "/",
	"icons": [{
		"src": "assets/icon/icon-128x128.png",
		"sizes": "128x128",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-152x152.png",
		"sizes": "152x152",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-192x192.png",
		"sizes": "192x192",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-384x384.png",
		"sizes": "384x384",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-512x512.png",
		"sizes": "512x512",
		"type": "image/png"
	}],
	"background_color": "#EDF2F4",
	"display": "standalone",
	"theme_color": "#B20422",
	"scope": "/",
	"shortcuts": [{
			"name": "Articles",
			"short_name": "Articles",
			"description": "1595 articles on Security, Sysadmin, Digital Marketing, Cloud Computing, Development, and many other topics.",
			"url": "https://geekflare.com/articles",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Authors",
			"short_name": "Authors",
			"description": "techblik.com - Authors",
			"url": "/authors",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Tools",
			"short_name": "Tools",
			"description": "techblik.com - Tools",
			"url": "https://techblik.com.com/tools",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Deals",
			"short_name": "Deals",
			"description": "techblik.com - Deals",
			"url": "/deals",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		}
	]
}

注册服务工作者

在根文件夹中创建脚本文件 register-service-worker.js 和 service-worker.js。

第一个,register-service-worker.js 是 javascript 文件,将在可以访问 DOM API 的主线程上运行。 但是 service-worker.js 是一个独立于主线程运行的 service worker 脚本,它的生命周期也很短。 每当事件调用服务工作者时它就会运行并运行直到它完成该过程。

通过检查主线程 javascript 文件,您可以检查服务工作者是否在其中注册。 如果没有,您可以注册服务工作者脚本(service-worker.js)。

将以下代码段粘贴到 register-service-worker.js 中:

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
        navigator.serviceWorker.register('/service-worker.js');
    });
}

将以下代码段粘贴到 service-worker.js

self.addEventListener('install', (event) => { // event when service worker install
    console.log( 'install', event);
    self.skipWaiting();
});

self.addEventListener('activate', (event) => { // event when service worker activated
    console.log('activate', event);
    return self.clients.claim();
});

self.addEventListener('fetch', function(event) { // HTTP request interceptor
    event.respondWith(fetch(event.request)); // send all http request without any cache logic
    /*event.respondWith(
        caches.match(event.request).then(function(response) {
            return response || fetch(event. request);
        })
    );*/ // cache new request. if already in cache serves with the cache.
});

我们没有专注于如何启用缓存以支持离线支持。 我们只讨论如何将 Web 应用程序转换为 PWA。

在 HTML 页面的 all head 标签中添加清单文件和脚本。

<link rel="manifest" href="https://techblik.com.com/manifest.json">
<script src="/register-service-worker.js"></script>

添加后刷新页面。 现在您可以在移动 chrome 上安装您的应用程序,如下所示。

  9 适用于中小型企业的最佳云 PBX 解决方案

在主屏幕上,应用程序被添加。

如果您使用的是 WordPress。 尝试使用现有的 PWA 转换器插件。 对于 vueJS 或 reactJS,您可以按照上述方法或使用现有的 PWA npm 模块来加快您的开发速度。 因为 PWA npm 模块已经启用了离线支持缓存等。

启用推送通知

Web 推送通知被发送到浏览器,以使我们的用户更频繁地与我们的应用程序互动/互动。 我们可以通过使用启用它

  • 通知 API:它用于配置我们的推送通知应该如何显示给用户。
  • 推送 API: 用于接收从我们的服务器发送到浏览器的通知消息。

在我们的应用程序中启用推送通知的第一步是检查通知 API 并从用户那里获得显示通知的权限。 对于该复制并将下面的代码段粘贴到您的 register-service-worker.js 中。

if ('Notification' in window && Notification.permission != 'granted') {
    console.log('Ask user permission')
    Notification.requestPermission(status => {  
        console.log('Status:'+status)
        displayNotification('Notification Enabled');
    });
}


const displayNotification = notificationTitle => {
    console.log('display notification')
    if (Notification.permission == 'granted') {
        navigator.serviceWorker.getRegistration().then(reg => {
            console.log(reg)
            const options = {
                    body: 'Thanks for allowing push notification !',
                    icon:  '/assets/icons/icon-512x512.png',
                    vibrate: [100, 50, 100],
                    data: {
                      dateOfArrival: Date.now(),
                      primaryKey: 0
                    }
                  };
    
            reg.showNotification(notificationTitle, options);
        });
    }
};

如果一切顺利。 您将收到来自应用程序的通知。

窗口中的“通知”将告诉我们该浏览器支持通知 API。 Notification.permission 将告诉用户已被允许显示通知。 如果用户允许我们的应用程序,则该值将被“授予”。 如果用户拒绝了该值,则该值将被“阻止”。

启用 Firebase 云消息传递并创建订阅

现在真正的部分开始了。 为了从您的服务器向用户推送通知,我们需要为每个用户提供一个唯一的端点/订阅。 为此,我们将使用 firebase 云消息传递。

第一步,通过访问此链接创建一个 Firebase 帐户 https://firebase.google.com/ 然后按开始。

  • 使用名称创建一个新项目,然后按继续。 我将使用名称 techblik.com 创建它。
  • 在下一步中,默认情况下启用 Google Analytics。 您可以切换我们现在不需要它,然后按继续。 如果需要,您可以稍后在 Firebase 控制台中启用它。
  • 创建项目后,它将如下所示。
  • 然后转到项目设置并单击云消息传递并生成密钥。

    通过上述步骤,您获得了 3 个密钥。

    • 项目服务器密钥
    • Web 推送证书私钥
    • 网络推送证书公钥

    现在将以下代码段粘贴到 register-service-worker.js 中:

    const updateSubscriptionOnYourServer = subscription => {
        console.log('Write your ajax code here to save the user subscription in your DB', subscription);
        // write your own ajax request method using fetch, jquery, axios to save the subscription in your server for later use.
    };
    
    const subscribeUser = async () => {
        const swRegistration = await navigator.serviceWorker.getRegistration();
        const applicationServerPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY'; // paste your webpush certificate public key
        const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
        swRegistration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey
        })
        .then((subscription) => {
            console.log('User is subscribed newly:', subscription);
            updateSubscriptionOnServer(subscription);
        })
        .catch((err) => {
            if (Notification.permission === 'denied') {
              console.warn('Permission for notifications was denied')
            } else {
              console.error('Failed to subscribe the user: ', err)
            }
        });
    };
    const urlB64ToUint8Array = (base64String) => {
        const padding = '='.repeat((4 - base64String.length % 4) % 4)
        const base64 = (base64String + padding)
            .replace(/-/g, '+')
            .replace(/_/g, '/')
    
        const rawData = window.atob(base64);
        const outputArray = new Uint8Array(rawData.length);
    
        for (let i = 0; i < rawData.length; ++i) {
            outputArray[i] = rawData.charCodeAt(i);
        }
        return outputArray;
    };
    
    const checkSubscription = async () => {
        const swRegistration = await navigator.serviceWorker.getRegistration();
        swRegistration.pushManager.getSubscription()
        .then(subscription => {
            if (!!subscription) {
                console.log('User IS Already subscribed.');
                updateSubscriptionOnYourServer(subscription);
            } else {
                console.log('User is NOT subscribed. Subscribe user newly');
                subscribeUser();
            }
        });
    };
    
    checkSubscription();

    将以下代码段粘贴到 service-worker.js 中。

    self.addEventListener('push', (event) => {
      const json = JSON.parse(event.data.text())
      console.log('Push Data', event.data.text())
      self.registration.showNotification(json.header, json.options)
    });

    现在全部设置在前端。 通过使用订阅,您可以随时向您的用户发送推送通知,直到他们没有被拒绝推送服务。

      20 个在线 JSON 编辑器、解析器和格式化工具

    从 node.js 后端推送

    您可以使用 网络推送 npm 模块使其更容易。

    从 nodeJS 服务器发送推送通知的示例片段。

    const webPush = require('web-push');
        // pushSubscription is nothing but subscription that you sent from your front-end to save it in DB
        const pushSubscription = {"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}};
        //your web certificates public-key
        const vapidPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY';
        //your web certificates private-key
        const vapidPrivateKey = 'web-certificate private key';
    
        var payload = JSON.stringify({
          "options": {
            "body": "PWA push notification testing fom backend",
            "badge": "/assets/icon/icon-152x152.png",
            "icon": "/assets/icon/icon-152x152.png",
            "vibrate": [100, 50, 100],
            "data": {
              "id": "458",
            },
            "actions": [{
              "action": "view",
              "title": "View"
            }, {
              "action": "close",
              "title": "Close"
            }]
          },
          "header": "Notification from techblik.com-PWA Demo"
        });
    
        var options = {
          vapidDetails: {
            subject: 'mailto:[email protected]',
            publicKey: vapidPublicKey,
            privateKey: vapidPrivateKey
          },
          TTL: 60
        };
    
        webPush.sendNotification(
          pushSubscription,
          payload,
          options
        ).then(data => {
          return res.json({status : true, message : 'Notification sent'});
        }).catch(err => {
          return res.json({status : false, message : err });
        });

    上面的代码将向订阅发送推送通知。 service-worker 中的推送事件将被触发。

    从 PHP 后端推送

    对于 PHP 后端,您可以使用 网络推送-php 作曲家包。 检查示例代码以发送下面的推送通知。

    <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
    
    require __DIR__.'/../vendor/autoload.php';
    use MinishlinkWebPushWebPush;
    use MinishlinkWebPushSubscription;
    
    // subscription stored in DB
    $subsrciptionJson = '{"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}}';
    $payloadData = array (
    'options' =>  array (
                    'body' => 'PWA push notification testing fom backend',
                    'badge' => '/assets/icon/icon-152x152.png',
                    'icon' => '/assets/icon/icon-152x152.png',
                    'vibrate' => 
                    array (
                      0 => 100,
                      1 => 50,
                      2 => 100,
                    ),
                    'data' => 
                    array (
                      'id' => '458',
                    ),
                    'actions' => 
                    array (
                      0 => 
                      array (
                        'action' => 'view',
                        'title' => 'View',
                      ),
                      1 => 
                      array (
                        'action' => 'close',
                        'title' => 'Close',
                      ),
                    ),
    ),
    'header' => 'Notification from techblik.com-PWA Demo',
    );
    
    // auth
    $auth = [
        'GCM' => 'your project private-key', // deprecated and optional, it's here only for compatibility reasons
        'VAPID' => [
            'subject' => 'mailto:[email protected]', // can be a mailto: or your website address
            'publicKey' => 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY', // (recommended) uncompressed public key P-256 encoded in Base64-URL
            'privateKey' => 'your web-certificate private-key', // (recommended) in fact the secret multiplier of the private key encoded in Base64-URL
        ],
    ];
    
    $webPush = new WebPush($auth);
    
    $subsrciptionData = json_decode($subsrciptionJson,true);
    
    
    // webpush 6.0
    $webPush->sendOneNotification(
      Subscription::create($subsrciptionData),
      json_encode($payloadData) // optional (defaults null)
    );

    结论

    我希望这能让您对将 Web 应用程序转换为 PWA 有所了解。 你可以查看这篇文章的源代码 这里 并演示它 这里. 我也在示例代码的帮助下通过从后端发送推送通知来测试推送通知。