📜  如何使用 ReactJS 开发渐进式 Web 应用程序?

📅  最后修改于: 2022-05-13 01:56:28.228000             🧑  作者: Mango

如何使用 ReactJS 开发渐进式 Web 应用程序?

渐进式 React 应用程序对用户操作的响应非常快。它们加载速度快,就像移动应用程序一样吸引人。他们可以访问移动设备功能,利用操作系统并拥有非常高的覆盖面。它支持可安装性、背景同步、缓存和离线支持以及推送通知等其他功能。使用 React,我们可以非常轻松地逐步增强 Web 应用程序,使其看起来和感觉像原生移动应用程序。

现在让我们看一步一步的实现 如何使用 React 开发渐进式 Web 应用程序。

第 1 步:使用 ReactJS,创建一个渐进式 Web 应用程序甚至将现有的 React 项目转换为一个项目变得更加容易。在文本编辑器的终端中,输入以下命令。 CRA 为 Progressive Web App 创建了一个样板,您可以根据需要轻松修改它。

此命令创建一个名为 react-pwa 的新 React 应用程序并导航到您的应用程序的目录。您可以进一步修改 manifest.json 文件和徽标等其他文件,以自定义应用程序并使其成为您自己的应用程序。

第 2 步:让我们实现 PWA 的功能并向我们的应用程序添加更多功能。在文本编辑器的终端中输入以下命令来安装一些第三方和 npm 包。

项目结构:public文件夹中添加worker.jsfeed.js ,在src文件夹中添加components文件夹,使其看起来像这样。

项目结构

第 3 步:注册 Service Worker – Service Worker 是一种在浏览器和网络之间工作的特殊脚本文件。它可以帮助我们执行独特的功能并在页面加载时自行注册。要在您的 React 应用程序中注册一个新的服务工作者,请在您的公共文件夹 ( public/worker.js ) 的 worker.js 文件中添加以下代码。

Javascript
var STATIC_CACHE_NAME = "gfg-pwa";
var DYNAMIC_CACHE_NAME = "dynamic-gfg-pwa";
  
// Add Routes and pages using React Browser Router
var urlsToCache = ["/", "/search", "/aboutus", "/profile"];
  
// Install a service worker
self.addEventListener("install", (event) => {
  
  // Perform install steps
  event.waitUntil(
    caches.open(STATIC_CACHE_NAME).then(function (cache) {
      console.log("Opened cache");
      return cache.addAll(urlsToCache);
    })
  );
});
  
// Cache and return requests
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cacheRes) => {
  
      // If the file is not present in STATIC_CACHE,
      // it will be searched in DYNAMIC_CACHE
      return (
        cacheRes ||
        fetch(event.request).then((fetchRes) => {
          return caches.open(DYNAMIC_CACHE_NAME).then((cache) => {
            cache.put(event.request.url, fetchRes.clone());
            return fetchRes;
          });
        })
      );
    })
  );
});
  
// Update a service worker
self.addEventListener("activate", (event) => {
  var cacheWhitelist = ["gfg-pwa"];
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});


Javascript


Javascript


Javascript
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cacheRes) => {
      return (
        cacheRes ||
        fetch(event.request).then((fetchRes) => {
          return caches.open(DYNAMIC_CACHE_NAME)
          .then((cache) => {
            cache.put(event.request.url, fetchRes.clone());
            return fetchRes;
          });
        })
      );
    })
  );
  if (!navigator.onLine) {
    if (event.request.url === 
        "http://localhost:3000/static/js/main.chunk.js") {
      event.waitUntil(
        self.registration.showNotification("Internet", {
          body: "internet not working",
          icon: "logo.png",
        })
      );
    }
  }
});


Javascript
import React from "react";
import { InputGroup, Button, Container } 
        from "react-bootstrap";
  
const Profile = () => {
  return (
    <>
      
        

                   Latitude           00                  

                   Longitude           00                  

                   Location                           

        

                                            

        
          
Pick an Image instead
                   
        

        

      
       ); };    export default Profile;


Javascript
window.onload = function () {
  var photo = document.getElementById("photoBtn");
  var locationBtn = document.getElementById("locationBtn");
  locationBtn.addEventListener("click", handler);
  var capture = document.getElementById("capture");
  photo.addEventListener("click", initializeMedia);
  capture.addEventListener("click", takepic);
};
  
function initializeLocation() {
  if (!("geolocation" in navigator)) {
    locationBtn.style.display = "none";
  }
}
  
function handler(event) {
  if (!("geolocation" in navigator)) {
    return;
  }
  
  navigator.geolocation.getCurrentPosition(
  function (position) {
    console.log(position);
    var lat = position.coords.latitude;
    var lon = position.coords.longitude;
    console.log(lat);
    console.log(lon);
    latitude.innerHTML = lat;
    longitude.innerHTML = lon;
  });
}
  
function initializeMedia() {
  if (!("mediaDevices" in navigator)) {
    navigator.mediaDevices = {};
  }
  
  if (!("getUserMedia" in navigator.mediaDevices)) {
    navigator.mediaDevices.getUserMedia = 
    function (constraints) {
      var getUserMedia =
        navigator.webkitGetUserMedia 
        || navigator.mozGetUserMedia;
  
      if (!getUserMedia) {
        return Promise.reject(new Error(
            "getUserMedia is not implemented!"));
      }
  
      return new Promise(function (resolve, reject) {
        getUserMedia.call(navigator, 
            constraints, resolve, reject);
      });
    };
  }
  
  navigator.mediaDevices
    .getUserMedia({ video: true })
    .then(function (stream) {
      player.srcObject = stream;
      player.style.display = "block";
    })
    .catch(function (err) {
      console.log(err);
      imagePicker.style.display = "block";
    });
}
  
function takepic(event) {
  canvas.style.display = "block";
  player.style.display = "none";
  capture.style.display = "none";
  
  var context = canvas.getContext("2d");
  context.drawImage(
    player,
    0,
    0,
    canvas.width,
    player.videoHeight / (player.videoWidth / canvas.width)
  );
  player.srcObject.getVideoTracks().forEach(function (track) {
    track.stop();
  });
}


第 4 步:一些旧浏览器可能不支持 Service Worker。但是,大多数现代浏览器(例如 Google Chrome)都内置了对服务人员的支持。在没有支持的情况下,该应用程序将像正常的 Web 应用程序一样运行。为了确保我们不会遇到错误或应用程序不会崩溃,我们需要在客户端的浏览器中检查 Service Worker 的支持状态。为此,请使用以下代码更新公共文件夹 ( public/index.html)中的 index.html 文件。

Javascript


第 5 步:现在,我们有了 Service Worker 基本功能的代码。我们需要注册它。为此,将 src 文件夹 ( src/index.js ) 中 index.js 中的一行从

service-worker.unregister()

serviceWorker.register()

我们的 service worker 即 worker.js 现在将成功注册自己。

运行应用程序的步骤:现在,在文本编辑器的终端中输入以下命令。

npm start

输出:这将在浏览器的 localhost://3000 中打开您的 React 应用程序。并且,在开发工具中,在应用程序选项卡下,您可以看到您的服务工作者已在控制台中注册,并显示“工作者注册成功”消息。

服务工作者注册

解释:我们现在有了基本的服务工作者,它按照我们想要的方式运行。为了实现其他类似原生设备的功能,让我们实现在用户在使用应用程序时离线的情况下发送通知。此外,要查看您的新功能,无需再次运行应用程序,只需单击重新加载按钮即可。

第 6 步:离线时发送推送通知 –推送通知是本机移动功能。并且浏览器会在默认设置中自动请求用户许可。 Web-push是一个第三方包,它将帮助我们使用 VAPID 键来推送通知。现在,我们需要一个VAPID API Key来开始在我们的应用程序中实现推送通知。请注意,每个 VAPID API KEY 对于每个 Service Worker 都是唯一的。

要生成 API 密钥,请在终端中键入以下内容:

./node_modules/.bin/web-push generate-vapid-keys

现在,在您的文本编辑器的终端中,web-push 提供了两个您自己的 vapid 键。我们将使用公共 vapid 密钥来生成推送通知。

修改 index.html 中的脚本。这将对您的 base64字符串VAPID API KEY 进行编码,并将其与 service worker 连接,以便它能够发送通知。

Javascript


第 7 步:让我们使用这个新功能在离线时发送推送通知。在worker.js中修改 fetch 事件并添加以下代码。在显示通知函数中,您可以添加更多属性并根据您的意愿进行修改。

Javascript

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cacheRes) => {
      return (
        cacheRes ||
        fetch(event.request).then((fetchRes) => {
          return caches.open(DYNAMIC_CACHE_NAME)
          .then((cache) => {
            cache.put(event.request.url, fetchRes.clone());
            return fetchRes;
          });
        })
      );
    })
  );
  if (!navigator.onLine) {
    if (event.request.url === 
        "http://localhost:3000/static/js/main.chunk.js") {
      event.waitUntil(
        self.registration.showNotification("Internet", {
          body: "internet not working",
          icon: "logo.png",
        })
      );
    }
  }
});

self.registration.showNotification函数显示所需的通知,甚至在显示之前请求许可。

第 8 步:要检查离线时同步和缓存是否正常工作,您可以在开发工具中将 Service Worker 上方的状态更改为“离线”或在应用程序上方执行相同操作。现在,每当您下线时,您都会看到一条推送通知,指示您下线了。

推送通知:互联网不工作

请注意,尽管某些功能可能会丢失,但您仍然可以看到这些页面。这是因为这些默认页面和 URL 一旦访问过就会存储在缓存中。因此,每次在开发过程中对文件进行更改时,请确保在应用程序选项卡下取消注册并再次注册。

第 9 步:添加相机和地理位置等原生功能——PWA 支持使用原生功能,如访问网络摄像头并在服务人员的帮助下确定位置。让我们首先为此创建 UI,我们可以在其中使用这些功能,在 'src/Profile.js' 中创建一个Profile.js文件,我们可以使用 React Routes 通过 /profile 导航到该文件。

Javascript

import React from "react";
import { InputGroup, Button, Container } 
        from "react-bootstrap";
  
const Profile = () => {
  return (
    <>
      
        

                   Latitude           00                  

                   Longitude           00                  

                   Location                           

        

                                            

        
          
Pick an Image instead
                   
        

        

      
       ); };    export default Profile;

第10步:现在让我们在public/feed.js中添加一个feed.js文件来实现定位和摄像头的功能。

Javascript

window.onload = function () {
  var photo = document.getElementById("photoBtn");
  var locationBtn = document.getElementById("locationBtn");
  locationBtn.addEventListener("click", handler);
  var capture = document.getElementById("capture");
  photo.addEventListener("click", initializeMedia);
  capture.addEventListener("click", takepic);
};
  
function initializeLocation() {
  if (!("geolocation" in navigator)) {
    locationBtn.style.display = "none";
  }
}
  
function handler(event) {
  if (!("geolocation" in navigator)) {
    return;
  }
  
  navigator.geolocation.getCurrentPosition(
  function (position) {
    console.log(position);
    var lat = position.coords.latitude;
    var lon = position.coords.longitude;
    console.log(lat);
    console.log(lon);
    latitude.innerHTML = lat;
    longitude.innerHTML = lon;
  });
}
  
function initializeMedia() {
  if (!("mediaDevices" in navigator)) {
    navigator.mediaDevices = {};
  }
  
  if (!("getUserMedia" in navigator.mediaDevices)) {
    navigator.mediaDevices.getUserMedia = 
    function (constraints) {
      var getUserMedia =
        navigator.webkitGetUserMedia 
        || navigator.mozGetUserMedia;
  
      if (!getUserMedia) {
        return Promise.reject(new Error(
            "getUserMedia is not implemented!"));
      }
  
      return new Promise(function (resolve, reject) {
        getUserMedia.call(navigator, 
            constraints, resolve, reject);
      });
    };
  }
  
  navigator.mediaDevices
    .getUserMedia({ video: true })
    .then(function (stream) {
      player.srcObject = stream;
      player.style.display = "block";
    })
    .catch(function (err) {
      console.log(err);
      imagePicker.style.display = "block";
    });
}
  
function takepic(event) {
  canvas.style.display = "block";
  player.style.display = "none";
  capture.style.display = "none";
  
  var context = canvas.getContext("2d");
  context.drawImage(
    player,
    0,
    0,
    canvas.width,
    player.videoHeight / (player.videoWidth / canvas.width)
  );
  player.srcObject.getVideoTracks().forEach(function (track) {
    track.stop();
  });
}

第 11 步:在 ( /src/public ) 文件夹中创建一个名为 feed.js 的新文件。在 feed.js 中,我们分别使用geolocationmediaDevices来实现位置和摄像头的功能。您还可以使用 Google Geocoder API 将这些纬度和经度转换为地名。

输出:您现在可以导航到 localhost:3000/profile 以拍照并获取位置。

已启用本机功能

说明:单击“获取位置”按钮将触发处理函数内的 navigator.geolocation.getCurrentPosition,从而使用适当的值填充纬度和经度字段。要获取城市的确切名称,请尝试使用上面提到的 Geocoder API。同样,点击 Take a Picture, Now 按钮将触发 initializeMedia函数内的 navigator.mediaDevices.getUserMedia 从而打开前置摄像头并拍照。这两个函数都将首先添加权限,然后自行执行。