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

已发表: 2022-03-20

在本文中,我们将了解如何使用 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 应用。
  • 易于访问:与我们的网络应用程序不同,我们的用户无需在每次访问时都输入网址。 安装后,只需轻轻一按即可打开。
  • 应用程序缓存:在 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 Trusted Web Activity (TWA) 在 Play 商店中将 PWA 部署为 Android 应用程序。 这将有助于您的品牌推广。

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

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

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

什么是服务工作者:

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

Service Worker 独立于主 javascript 运行。 所以它无法访问 DOM API。 它只能访问 IndexedDB API、Fetch API、Cache Storage API。 但它可以通过消息与主线程通信。

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

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

  • 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 文件。 对于演示,我为 Geekflare 网站创建了一个清单文件。

 {
	“名称”:“Geekflare”,
	"short_name": "Geekflare",
	"description": "Geekflare 制作高质量的技术和金融文章,制作工具和 API 来帮助企业和人们成长。",
	"start_url": "/",
	“图标”:[{
		"src": "assets/icon/icon-128x128.png",
		“尺寸”:“128x128”,
		“类型”:“图像/png”
	}, {
		"src": "assets/icon/icon-152x152.png",
		“尺寸”:“152x152”,
		“类型”:“图像/png”
	}, {
		"src": "assets/icon/icon-192x192.png",
		“尺寸”:“192x192”,
		“类型”:“图像/png”
	}, {
		"src": "assets/icon/icon-384x384.png",
		“尺寸”:“384x384”,
		“类型”:“图像/png”
	}, {
		"src": "assets/icon/icon-512x512.png",
		“尺寸”:“512x512”,
		“类型”:“图像/png”
	}],
	"background_color": "#EDF2F4",
	“显示”:“独立”,
	"theme_color": "#B20422",
	“范围”: ”/”,
	“捷径”:[{
			“名称”:“文章”,
			"short_name": "文章",
			"description": "1595 篇关于安全、系统管理员、数字营销、云计算、开发和许多其他主题的文章。",
			“网址”:“/文章”,
			“图标”:[{
				"src": "/assets/icon/icon-152x152.png",
				“尺寸”:“152x152”
			}]
		},
		{
			“名称”:“作者”,
			"short_name": "作者",
			"description": "Geekflare - 作者",
			“网址”:“/作者”,
			“图标”:[{
				"src": "/assets/icon/icon-152x152.png",
				“尺寸”:“152x152”
			}]
		},
		{
			“名称”:“工具”,
			"short_name": "工具",
			"description": "Geekflare - 工具",
			“网址”:“/工具”,
			“图标”:[{
				"src": "/assets/icon/icon-152x152.png",
				“尺寸”:“152x152”
			}]
		},
		{
			“名称”:“交易”,
			"short_name": "交易",
			"description": "Geekflare - 优惠",
			“网址”:“/交易”,
			“图标”:[{
				"src": "/assets/icon/icon-152x152.png",
				“尺寸”:“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) => { // service worker 安装时的事件
    console.log('安装',事件);
    self.skipWaiting();
});

self.addEventListener('activate', (event) => { // service worker 激活时的事件
    console.log('激活',事件);
    返回 self.clients.claim();
});

self.addEventListener('fetch', function(event) { // HTTP 请求拦截器
    event.respondWith(fetch(event.request)); // 发送所有 http 请求,没有任何缓存逻辑
    /*event.respondWith(
        caches.match(event.request).then(function(response) {
            返回响应 || 获取(事件。请求);
        })
    );*/ // 缓存新请求。 如果已经在缓存中,则与缓存一起使用。
});

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

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

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

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

在 android chrome 中安装 PWA

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

chrome中的pWA快捷方式

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

启用推送通知

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

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

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

 if ('Notification' in window && Notification.permission != 'granted') {
    console.log('询问用户权限')
    Notification.requestPermission(状态 => {  
        console.log('状态:'+状态)
        displayNotification('通知已启用');
    });
}


常量 displayNotification = notificationTitle => {
    console.log('显示通知')
    if (Notification.permission == 'granted') {
        navigator.serviceWorker.getRegistration().then(reg => {
            控制台日志(注册)
            常量选项 = {
                    body: '感谢允许推送通知!',
                    图标:'/assets/icons/icon-512x512.png',
                    振动:[100, 50, 100],
                    数据: {
                      dateOfArrival: Date.now(),
                      主键:0
                    }
                  };
    
            reg.showNotification(通知标题,选项);
        });
    }
};

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

pwa 通知 api 权限
pwa-通知-api-显示-通知

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

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

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

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

  1. 使用名称创建一个新项目,然后按继续。 我将使用名称 Geekflare 创建它。
  2. 在下一步中,默认情况下启用 Google Analytics。 您可以切换我们现在不需要它,然后按继续。 如果需要,您可以稍后在 Firebase 控制台中启用它。
  3. 创建项目后,它将如下所示。
Firebase 控制台

然后转到项目设置并单击云消息传递并生成密钥。

firebase 云消息密钥生成

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

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

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

 常量 updateSubscriptionOnYourServer = 订阅 => {
    console.log('在此处编写您的 ajax 代码以将用户订阅保存在您的数据库中', subscription);
    // 使用 fetch、jquery、axios 编写自己的 ajax 请求方法,将订阅保存在服务器中以备后用。
};

const subscribeUser = async () => {
    const swRegistration = 等待 navigator.serviceWorker.getRegistration();
    常量 applicationServerPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY'; // 粘贴你的 webpush 证书公钥
    常量 applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
    swRegistration.pushManager.subscribe({
      userVisibleOnly:真,
      应用服务器密钥
    })
    .then((订阅) => {
        console.log('用户新订阅:', subscription);
        updateSubscriptionOnServer(订阅);
    })
    .catch((错误) => {
        if (Notification.permission === '拒绝') {
          console.warn('通知权限被拒绝')
        } 别的 {
          console.error('订阅用户失败:', err)
        }
    });
};
常量 urlB64ToUint8Array = (base64String) => {
    const padding = '='.repeat((4 - base64String.length % 4) % 4)
    const base64 = (base64String + padding)
        .replace(/\-/g, '+')
        .replace(/_/g, '/')

    常量 rawData = window.atob(base64);
    常量 outputArray = new Uint8Array(rawData.length);

    for (让 i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    返回输出数组;
};

const checkSubscription = async () => {
    const swRegistration = 等待 navigator.serviceWorker.getRegistration();
    swRegistration.pushManager.getSubscription()
    .then(订阅 => {
        如果(!!订阅){
            console.log('用户已订阅');
            updateSubscriptionOnYourServer(订阅);
        } 别的 {
            console.log('用户未订阅。新订阅用户');
            订阅用户();
        }
    });
};

检查订阅();

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

 self.addEventListener('push', (event) => {
  常量 json = JSON.parse(event.data.text())
  console.log('推送数据', event.data.text())
  self.registration.showNotification(json.header, json.options)
});

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

从 node.js 后端推送

您可以使用 web-push npm 模块使其更容易。

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

 常量 webPush = 要求('web-push');
    // pushSubscription 只不过是您从前端发送的订阅以将其保存在数据库中
    const pushSubscription = {"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p2 :"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}};
    //你的网络证书公钥
    常量 vapidPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY';
    //你的网络证书私钥
    const vapidPrivateKey = '网络证书私钥';

    var 有效负载 = JSON.stringify({
      “选项”: {
        "body": "后端 PWA 推送通知测试",
        "徽章": "/assets/icon/icon-152x152.png",
        "icon": "/assets/icon/icon-152x152.png",
        “振动”:[100, 50, 100],
        “数据”: {
          “id”:“458”,
        },
        “行动”:[{
          “动作”:“视图”,
          “标题”:“视图”
        }, {
          “动作”:“关闭”,
          “标题”:“关闭”
        }]
      },
      "header": "来自 Geekflare-PWA 演示的通知"
    });

    变量选项 = {
      虚无细节:{
        主题:'mailto:[电子邮件保护]',
        公钥:vapidPublicKey,
        私钥:vapidPrivateKey
      },
      生存时间:60
    };

    webPush.sendNotification(
      推送订阅,
      有效载荷,
      选项
    ).then(数据 => {
      return res.json({status : true, message : '通知发送'});
    }).catch(错误 => {
      返回 res.json({status : false, message : err });
    });

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

从 PHP 后端推送

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

 <?php if ( !defined('BASEPATH')) exit('不允许直接脚本访问');

需要 __DIR__.'/../vendor/autoload.php';
使用 Minishlink\WebPush\WebPush;
使用 Minishlink\WebPush\Subscription;

// 订阅存储在数据库中
$subsrciptionJson = '{"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2...E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8"p526dh8 ":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}}';
$payloadData = 数组 (
'选项' => 数组 (
                'body' => '后端的 PWA 推送通知测试',
                '徽章' => '/assets/icon/icon-152x152.png',
                '图标' => '/assets/icon/icon-152x152.png',
                '振动' => 
                大批 (
                  0 => 100,
                  1 => 50,
                  2 => 100,
                ),
                '数据' => 
                大批 (
                  'id' => '458',
                ),
                '行动' => 
                大批 (
                  0 => 
                  大批 (
                    '动作' => '视图',
                    '标题' => '视图',
                  ),
                  1 => 
                  大批 (
                    '动作' => '关闭',
                    '标题' => '关闭',
                  ),
                ),
),
'header' => '来自 Geekflare-PWA Demo 的通知',
);

// 认证
$身份验证 = [
    'GCM' => 'your project private-key', // 已弃用且可选,此处仅出于兼容性原因
    'VAPID' => [
        'subject' => 'mailto:[email protected]', // 可以是 mailto: 或您的网站地址
        'publicKey' => 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY', //(推荐)以 Base64-URL 编码的未压缩公钥 P-256
        'privateKey' => 'your web-certificate private-key', //(推荐)实际上是 Base64-URL 中编码的私钥的秘密乘数
    ],
];

$webPush = 新的 WebPush($auth);

$subsrciptionData = json_decode($subsrciptionJson,true);


// 网络推送 6.0
$webPush->sendOneNotification(
  订阅::create($subsrciptionData),
  json_encode($payloadData) // 可选(默认为空)
);

结论

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