如何将WebApp转换为具有推送通知功能的PWA

在这篇文章中,我们将看到如何使用Firebase Cloud Messaging将Web应用程序或网站转换为PWA,并使用推送通知。

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

例如,像Twitter和Amazon这样的大公司已经将他们的Web应用程序转换为PWA以获得更多的用户参与度。

PWA是什么?

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

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

PWA与Web应用程序的不同之处

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

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

为什么企业应该考虑PWA

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

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

大多数客户的要求可以通过PWA功能本身来满足。对于他们,我们建议仅使用PWA,并向他们提供将他们的PWA转换为使用TWA部署在Playstore中的Android应用程序的想法。

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

例如:Titan Eyeplus

起初,他们开发了一个PWA应用并使用TWA(可信Web活动)将其部署在Play商店中。一旦他们完成了Android应用程序的开发,他们就在Play商店中部署了他们的真正的Android应用程序。他们通过使用PWA实现了时间上市和开发成本的双重目标。

PWA功能

PWA提供了与原生应用程序类似的功能。

主要功能包括:

  • 可安装:Web应用程序可以像原生应用程序一样安装。
  • 缓存:可以进行应用程序缓存,从而提供离线支持。
  • 推送通知:可以从服务器向用户发送推送通知,以吸引他们访问我们的网站。
  • 地理围栏:每当设备位置发生变化时,应用程序可以通过事件进行通知。
  • 付款请求:在您的应用程序中启用付款,提供与原生应用程序类似的出色用户体验。

还有更多功能即将推出。

其他功能包括:

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

PWA相对于原生应用的优势

原生应用的性能比PWA好,功能也比PWA多。但是,PWA仍然具有一些优势。

  • PWA可以在Android、IOS和桌面等跨平台上运行。
  • 它可以降低您的开发成本。
  • 与原生应用相比,功能部署更加简单。
  • 易于发现,因为PWA(网站)对搜索引擎友好。
  • 安全,因为它只能在链接_2上运行。

PWA相对于原生应用的劣势

  • 与原生应用相比,功能有限。
  • PWA的功能不能保证支持所有设备。
  • PWA的品牌力较低,因为它不能在应用商店或Play商店中获得。

您可以使用Android的链接_3将PWA部署为Play商店的Android应用程序。这将有助于您的品牌推广。

将Web应用程序转换为PWA所需的内容

将任何Web应用程序或网站转换为PWA所需的内容。

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

什么是Service Worker:

Service Worker(客户端脚本)是您的Web应用程序和外部环境之间的代理,用于向我们的Web应用程序提供推送通知和支持缓存。

Service Worker独立于主JavaScript运行。因此,它无法访问DOM API,只能访问link_4、link_5、link_6。但是它可以通过消息与主线程通信。

Service Worker提供的服务:

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

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

什么是清单文件

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

  • name:应用程序的名称
  • short_name:应用程序的简称(如果提供了name和short_name属性,则浏览器将使用short_name)
  • description:描述我们的应用程序的描述
  • start_url:指定我们的PWA启动时的主页
  • icons:用于PWA的一组图像,用于主屏幕等
  • background_color:设置PWA应用程序启动画面的背景颜色
  • display:自定义我们的浏览器UI,以在我们的PWA应用程序中显示
  • theme_color:PWA应用程序的主题颜色
  • scope:我们的应用程序的URL范围,用于PWA的考虑。默认为清单文件所在位置。
  • shortcuts:我们的PWA应用程序的快捷链接

将Web应用程序转换为PWA

为了演示目的,我创建了一个Geekflare网站的文件夹结构,其中包含静态文件。

  • index.html – 主页
  • articles/
    • 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网站创建了一个清单文件。

          {
              "name": "Geekflare",
              "short_name": "Geekflare",
              "description": "Geekflare提供高质量的技术和金融文章,制作工具和API,帮助企业和个人成长。",
              "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": "文章",
                      "short_name": "文章",
                      "description": "安全、系统管理员、数字营销、云计算、开发等1595篇文章。",
                      "url": "/articles",
                      "icons": [{
                          "src": "/assets/icon/icon-152x152.png",
                          "sizes": "152x152"
                      }]
                  },
                  {
                      "name": "作者",
                      "short_name": "作者",
                      "description": "Geekflare - 作者",
                      "url": "/authors",
                      "icons": [{
                          "src": "/assets/icon/icon-152x152.png",
                          "sizes": "152x152"
                      }]
                  },
                  {
                      "name": "工具",
                      "short_name": "工具",
                      "description": "Geekflare - 工具",
                      "url": "/tools",
                      "icons": [{
                          "src": "/assets/icon/icon-152x152.png",
                          "sizes": "152x152"
                      }]
                  },
                  {
                      "name": "优惠",
                      "short_name": "优惠",
                      "description": "Geekflare - 优惠",
                      "url": "/deals",
                      "icons": [{
                          "src": "/assets/icon/icon-152x152.png",
                          "sizes": "152x152"
                      }]
                  }
              ]
          }

          注册Service Worker

          在根目录下创建一个名为register-service-worker.js的脚本文件和service-worker.js的服务工作器脚本。

          第一个文件register-service-worker.js是在主线程上运行的JavaScript文件,可以访问DOM API。而service-worker.js是一个独立于主线程运行的服务工作器脚本,其生命周期也很短。它会在事件调用服务工作器时运行,直到完成处理。

          通过检查主线程的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(‘install', event);
          self.skipWaiting();
          });

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

          self.addEventListener(‘fetch', function(event) { // HTTP请求拦截器
          event.respondWith(fetch(event.request)); // 发送所有HTTP请求,不进行任何缓存逻辑
          /*event.respondWith(
          caches.match(event.request).then(function(response) {
          return response || fetch(event. request);
          })
          );*/ // 缓存新请求。如果已存在于缓存中,则从缓存中提供。
          });

          我们没有关注如何启用离线支持的缓存。我们只讨论了如何将Web应用转换为PWA。

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

          添加后刷新页面。现在您可以在移动Chrome上像下面这样安装您的应用程序。

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

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

          启用推送通知

          Web推送通知发送到浏览器,使我们的用户更频繁地与我们的应用程序进行互动。我们可以通过使用以下内容来启用它:

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

          在我们的应用程序中启用推送通知的第一步是检查Notification 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-512×512.png',
          vibrate: [100, 50, 100],
          data: {
          dateOfArrival: Date.now(),
          primaryKey: 0
          }
          };

          reg.showNotification(notificationTitle, options);
          });
          }
          };

          如果一切正常,您将收到应用程序发送的通知。

          ‘Notification' in window将告诉我们该浏览器是否支持Notification API。Notification.permission将告知用户是否已被允许显示通知。如果用户允许我们的应用程序,则值将为“granted”。如果用户拒绝,则值将为“blocked”。

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

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

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

          1. 创建一个带有名称的新项目,然后按“继续”。我将用名称Geekflare创建它。
          2. 在下一步中,默认情况下启用了Google Analytics。您可以切换到不需要该功能,然后按“继续”。如果需要,您以后可以在Firebase控制台中启用它。
          3. 项目创建完成后,它将如下所示。

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

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

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

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

          const updateSubscriptionOnYourServer = subscription => {
              console.log('在此处编写您的ajax代码,将用户订阅保存到您的数据库中', subscription);
              // 使用fetch、jquery、axios等编写自己的ajax请求方法,将订阅保存到服务器以供以后使用。
          };
          
          const subscribeUser = async () => {
              const swRegistration = await navigator.serviceWorker.getRegistration();
              const applicationServerPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY'; // 粘贴你的Web推送证书公钥
              const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
              swRegistration.pushManager.subscribe({
                userVisibleOnly: true,
                applicationServerKey
              })
              .then((subscription) => {
                  console.log('用户新订阅成功:', subscription);
                  updateSubscriptionOnServer(subscription);
              })
              .catch((err) => {
                  if (Notification.permission === 'denied') {
                    console.warn('通知权限已被拒绝')
                  } else {
                    console.error('订阅用户失败:', 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  {
              const swRegistration = await navigator.serviceWorker.getRegistration();
              swRegistration.pushManager.getSubscription()
              .then(subscription => {
                  if (!!subscription) {
                      console.log('用户已经订阅。');
                      updateSubscriptionOnYourServer(subscription);
                  } else {
                      console.log('用户尚未订阅。重新订阅用户');
                      subscribeUser();
                  }
              });
          };
          
          checkSubscription();

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

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

          现在前端部分已经设置完成。通过使用订阅,您可以在需要时随时向用户发送推送通知,直到他们拒绝推送服务为止。

          从node.js后端推送

          您可以使用npm模块web-push使其更加简便。

          从nodeJS服务器发送推送通知的示例代码。

          const webPush = require('web-push');
              // pushSubscription是从前端发送到服务器保存在数据库中的订阅对象
              const pushSubscription = {"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}};
              //你的网络证书公钥
              const vapidPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY';
              //你的网络证书私钥
              const vapidPrivateKey = 'web-certificate private key';
          
              var payload = JSON.stringify({
                "options": {
                  "body": "PWA后端推送测试",
                  "badge": "/assets/icon/icon-152x152.png",
                  "icon": "/assets/icon/icon-152x152.png",
                  "vibrate": [100, 50, 100],
                  "data": {
                    "id": "458",
                  },
                  "actions": [{
                    "action": "view",
                    "title": "查看"
                  }, {
                    "action": "close",
                    "title": "关闭"
                  }]
                },
                "header": "来自Geekflare-PWA演示的通知"
              });
          
              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 : '通知已发送'});
              }).catch(err => {
                return res.json({status : false, message : err });
              });

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

          从PHP后端推送

          对于PHP后端,您可以使用web-push-php composer包。请查看以下示例代码以发送推送通知。

            array (
                          'body' => 'PWA后端推送通知测试',
                          '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' => '查看',
                            ),
                            1 => 
                            array (
                              'action' => 'close',
                              'title' => '关闭',
                            ),
                          ),
          ),
          'header' => '来自Geekflare-PWA演示的通知',
          );
          
          // 认证
          $auth = [
              'GCM' => '你的项目私钥', // 废弃且可选,仅为兼容性原因而在此
              'VAPID' => [
                  'subject' => 'mailto:[email protected]', // 可以是mailto: 或你的网站地址
                  'publicKey' => 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY', // (推荐) Base64-URL编码的P-256未压缩公钥
                  'privateKey' => '你的网站证书私钥', // (推荐) 实际上是Base64-URL编码的私钥的秘密乘法器
              ],
          ];
          
          $webPush = new WebPush($auth);
          
          $subsrciptionData = json_decode($subsrciptionJson,true);
          
          
          // webpush 6.0
          $webPush->sendOneNotification(
            Subscription::create($subsrciptionData),
            json_encode($payloadData) // 可选(默认为null)
          );

          结论

          希望这能让你了解如何将Web应用程序转换为PWA。你可以检查本文的源代码 here 并进行演示 here。我还使用示例代码从后端发送了推送通知进行了测试。

类似文章