如何开发一个渐进式Web应用程序PWA


Posted in Javascript onMay 10, 2021

概述

自苹果推出了iPhone应用商店以来,App成为了我们生活中不可或缺的一部分,而对于实体业务也是如此,现在各行业都在推出自己的App,但有没有人想过这样一种场景,如果自己的潜在客户还没有安装你的App亦或是即便安装但因为客户的手机存储空间紧张而卸载掉了你的App?那有没有使App更轻量,更易安装的技术实现呢?答案是“有的”。

渐进式Web应用程序就是为此而生的,它同时具备了Web应用功能和以前只有在原生应用才有的功能的特点,渐进式Web应用程序通过从主屏幕上的图标启动,也可以根据推送通知启动,加载时间几乎可以忽略不计,而且除了可以在线使用外,也可以打包成可离线使用。

最重要的是,渐进式Web应用程序在手机上创建方式也很简单,因为它们只是对你网站的增强,当有人在第一次访问你的网站时,PWA的功能在经过你授权后就会自动为你创建在手机上。

下面, 我们会一起来看看如何来创建一个属于自己的PWA应用。

要求

要开始学习本教程,您必须安装以下软件:

  • node 8.9 版本及以上 (https: // nodejs.org/en/download/). Yarn(https://yarnpkg.com) Git.

作为本教程初始的工程,你可以clone以下Github库:

git clone https://github.com/petereijgermans11/progressive-web-app

然后,到以下目录中  

cd pwa-article / pwa-app-manifest-init

通过以下命令安装依赖并启动工程

npm i && npm start

通过以下地址打开应用:http:// localhost:8080

如何开发一个渐进式Web应用程序PWA

应用的网址

有许多方法可以访问我的本地主机:为了从远程访问发布在你机器上的8080端口的地址。为此,您可以使用ngrok。请参阅:https://ngrok.com/

使用以下命令安装ngrok:

npm install -g ngrok

在终端中运行以下命令。该命令为您生成一个可供外部访问的URL

ngrok http 8080

然后在Chrome中的移动设备上浏览至生成的网址。

PWA需要的技术组件是什么?

PWA有三个重要的技术组件协调工作,包括:

Manifest清单文件,Service Worker和在https下运行。

如何开发一个渐进式Web应用程序PWA

Manifest清单文件

清单文件是一个JSON配置文件,其中包含了PWA的基础信息,例如应用的icon,Web应用程序名称及背景颜色。如果浏览器检测到网站存在PWA清单文件,Chrome会自动出现“添加到主屏幕”按钮。如果用户点击同意,该图标将被添加到主屏幕,并且将安装PWA。

如何开发一个渐进式Web应用程序PWA

创建一个Manifest.json

PWA的Manifest.json文件如下所示:

JSON格式

{
  "name": "Progressive Selfies",
  "short_name": "PWA Selfies",
  "icons": [
    {
      "src": "/src/images/icons/app-icon-192x192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "/src/images/icons/app-icon-512x512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": "/index.html",
  "scope": ".",
  "display": "standalone",
  "background_color": "#fff",
  "theme_color": "#3f51b5"
}

告诉浏览器你应用的清单

在与index.html文件相同的级别的目录中创建Manifest.json文件。清单文件创建后,将清单文件引用链接添加到index.html中。

<link rel=”manifest” href=”/manifest.json”>

Manifest 属性介绍

Manifest有很多配置属性,接下来我们会对其中的属性做一个简单的介绍

  • name、short_name:指定Web应用的名称,short_name是该应用的简称,当没有足够空间展示应用的name属性时,系统就会使用short_name 。 
  • display:display属性指定Web应用的显示模式,它有四个值可供配置:fullscreen、standalone、minimal-ui和browser,但一般常用的属性就是fullscreen和standalone。 fullscreen:全屏显示 standalone:这种模式下打开的应用不会出现浏览器的地址栏,所以因此看起来更像是一个原生应用 minimal-ui、browser:和使用浏览器访问区别不大。 
  • orientation:控制Web应用的显示的方向及禁止手机转屏。 
  • icons、background_color:icon用于指定应用图标,background_color是应用加载完成前的背景色,通过设置这两个属性,可组合成应用的Splash Screen。 
  • theme_color:定义应用程序的默认主题颜色。 
  • description:设置应用的一段描述内容。

以上是pwa 清单文件属性的一些说明,我们通过将设置完成的清单文件并将其放置在与index.html文件同级的目录中即可完成清单文件的添加。

打开Chrome开发者工具 ? Application - Manifest,查看添加的清单文件是否加载完成,如果没有下图的信息,我们可以通过重新启动服务器npm start来重新加载。

如何开发一个渐进式Web应用程序PWA

什么是Service Worker?

Service Worker(SW) 是一段JavaScript,它作为浏览器和网络服务器间的代理。Service Worker可以在基于浏览器的 web 应用中实现如离线缓存、消息推送、静默更新等 native 应用常见的功能,以给 web 应用提供更好更丰富的使用体验。

另外,这个API还允许利用缓存来支持离线体验,从而使开发人员可以完全控制用户的使用体验

如何开发一个渐进式Web应用程序PWA

Service Worker生命周期

对于Service Worker,基本设置的步骤如下:

  • 首先应注册SW,如果SW已注册,浏览器会根据于安装事件自动开始安装。
  • 安装SW后,它将收到激活事件。此激活事件可用于清理SW早期版本的中使用的资源。

如何开发一个渐进式Web应用程序PWA

实际操作应该首先创建一个和index.html同级,名为sw.js的空文件。然后再index.html文件中,添加一个base标签,如下:

<base href="/" rel="external nofollow" >

最后,在src/js/app.js中添加以下代码注册SW。此代码将在页面 “加载”过程中被激活。

你可以打开Chrome DevTools ? Application - Service Worker 中检查SW是否已经启用。

window.addEventListener('load', () => {
    const base = document.querySelector('base');
    let baseUrl = base && base.href || '';
    if (!baseUrl.endsWith('/')) {
        baseUrl = `${baseUrl}/`;
    } 
   
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register(`${baseUrl}sw.js`)
            .then( registration => {
            // Registration was successful
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
        })
        .catch(err => {
            // registration failed :(
            console.log('ServiceWorker registration failed: ', err);
        });
    }
});

以上这段代码主要的作用是检查SW的API在window对象的navigator属性中是否可用。window对象代表浏览器窗口。如果SW在navigator中可用,则在页面加载时立即注册SW。

虽然注册一个SW很简单,但在有些情况下我们依然会遇到无法注册Service Worker的问题,我们来简单看看无法注册SW的原因都有什么并如何解决:

  • 您的应用程序无法在HTTPS下运行。在开发过程中,你可以通过localhost使用SW。但如果将其部署在网站上时,则需要启用HTTPS。
  • SW的路径不正确。

没有勾选Update on reload。 

如何开发一个渐进式Web应用程序PWA

Service Worker 事件

除了install和activate事件外,其他事件还有message、fetch、sync和push事件。

如何开发一个渐进式Web应用程序PWA

将以下代码添加到你的SW中以监听生命周期事件(安装和激活):

self.addEventListener('install', event => {
    console.log('[Service Worker] Installing Service Worker ...', event);
    event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', event => {
    console.log('[Service Worker] Activating Service Worker ...', event);
    return self.clients.claim();
});

install回调调用skipWaiting()函数来触发activate事件,并告诉Service Worker立即开始工作,而无需等待用户浏览或重新加载页面。

skipWaiting()函数强制等待中的Service Worker成为活动的Service Worker。self.skipWaiting()函数也可以和self.clients.claim()函数一起使用,以确保对底层Service Worker的更新立即生效。

在这种情况下,self-property 代表窗口对象(即你的浏览器窗口)。

添加到主屏幕按钮

"添加到主屏幕按钮" 允许用户在其设备上安装PWA。为了真正用这个按钮安装PWA,你必须在SW中定义一个fetch事件处理程序。让我们在sw.js中解决这个问题。

self.addEventListener('fetch', event => {
    console.log('[Service Worker] Fetching something ....', event);
    // This fixes a weird bug in Chrome when you open the Developer Tools
    if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
        return;
    }
    event.respondWith(fetch(event.request));
});

Service Worker缓存

Service Worker的强大之处在于其拦截HTTP请求的能力。在这一步中,我们使用这个选项来拦截HTTP请求和响应,直接从缓存为用户提供闪电般快速的响应。

在Service Worker安装期间进行预缓存

当用户第一次访问你的网站时,SW会开始自行安装。在这个安装阶段,你可以将PWA使用的所有页面、脚本和样式文件下载并缓存起来,以下是完成这项工作的sw.js文件代码:

const CACHE_STATIC_NAME = 'static';
const URLS_TO_PRECACHE = [
    '/',
    'index.html',
    'src/js/app.js',
    'src/js/feed.js',
    'src/lib/material.min.js',
    'src/css/app.css',
    'src/css/feed.css',
    'src/images/main-image.jpg',
    'https://fonts.googleapis.com/css?family=Roboto:400,700',
    'https://fonts.googleapis.com/icon?family=Material+Icons',
];
self.addEventListener('install', event => {
    console.log('[Service Worker] Installing Service Worker ...', event);
    event.waitUntil(
        caches.open(CACHE_STATIC_NAME)
            .then(cache => {
                console.log('[Service Worker] Precaching App Shell');
                cache.addAll(URLS_TO_PRECACHE);
            })
            .then(() => {
                console.log('[ServiceWorker] Skip waiting on install');
                return self.skipWaiting();
            })
    );
});

这段代码使用安装事件,并在安装阶段添加了一个URLS_TO_PRECACHE数组。一旦调用开启缓存函数(caches.open),你就可以使用cache.addAll()函数来缓存数组中的文件。通过event.waitUntil()方法使用JavaScript promise来知道安装需要多长时间以及是否成功。

安装事件会调用self.skipWaiting()直接激活SW。如果所有文件都已被成功缓存,SW就会被安装。但如果其中一个文件无法下载,则安装步骤将会失败。在Chrome开发者工具中,你可以检查缓存(在Cache Storage中)是否被URLS_TO_PRECACHE数组中的静态文件填充。

如何开发一个渐进式Web应用程序PWA

但是,如果你查看Network选项卡,文件仍然是通过网络获取的。原因是虽然缓存已经准备就绪了,但我们并没有从缓存中读取引用资源。所以为了完成这部分工作,我们首先要监听应用的fetch事件,然后拦截并从缓存中获取资源,让我们看看下面的代码吧:

self.addEventListener('fetch', event => {
    console.log('[Service Worker] Fetching something ....', event);
    event.respondWith(
        caches.match(event.request)
            .then(response => {
                if (response) {
                    console.log(response);
                    return response;
                }
                return fetch(event.request);
            })
    );
});

我们使用caches.match()函数检查传入的URL是否与当前缓存中可能存在的资源匹配。如果匹配,我们就返回该缓存资源,但如果该资源不存在于缓存中,我们就像正常情况下一样继续获取请求的资源。

在Service Worker安装并激活后,刷新页面并再次检查网络选项卡。现在,Service Worker将拦截HTTP请求,并从缓存中即时加载相应的资源,而不是向服务器发出网络请求。

现在,如果我们在网络选项卡中设置离线模式,我们的应用也依然能正常访问。

后台传输

Background Fetch API是SW的后台功能,它允许用户在后台下载大文件、视频和音乐等资源。在获取/传输过程中,你的用户即便关闭标签,乃至关闭整个浏览器,也不会清除传输任务。当用户再次打开浏览器后,传输过程将恢复。这个API也可以将传输的进度可以显示给用户,用户可以取消或暂停这个过程。

如何开发一个渐进式Web应用程序PWA

默认情况下,后台传输功能是不可用的,你必须通过url(chrome://flags/#enable-experimental-web-platform-features)允许chrome的“Experimental Web Platform features”选项

如何开发一个渐进式Web应用程序PWA

以下是如何实现此类后台传输的示例。

在你的index.html文件中添加ID为“ bgFetchButton”的按钮

<button id="bgFetchButton">Store assets locally</button>

然后,在加载事件处理程序中的app.js中添加用于执行后台传输的代码

window.addEventListener(‘load', () => {
...
       bgFetchButton = document.querySelector(‘#bgFetchButton');
       bgFetchButton.addEventListener(‘click', async event => {
         try {
            const registration = await navigator.serviceWorker.ready;
            registration.backgroundFetch.fetch(‘my-fetch', [new              Request(`${baseUrl}src/images/main-image-lg.jpg`)]);
         } catch (err) {
            console.error(err);
         }
     });
...
});

上面的代码在以下条件下开始执行后台传输:

  • 用户点击ID为bgFetchButton的按钮
  • SW已注册

后台传输必须在异步函数中执行,因为传输过程不能阻塞用户界面。

传输完成后放入缓存

self.addEventListener(‘backgroundfetchsuccess', event => {
  console.log(‘[Service Worker]: Background Fetch Success', event.registration);   event.waitUntil(
   (async function() {
     try {
     // Iterating the records to populate the cache
       const cache = await caches.open(event.registration.id); const records =          await event.registration.matchAll(); const promises = records.map(async          record => {
         const response = await record.responseReady;
         await cache.put(record.request, response);
       });
       await Promise.all(promises);
     } catch (err) {
       console.log(‘[Service Worker]: Caching error');
     }
   })()
  );
});

这段代码由以下步骤组成:

  • 当Background Fetch传输完成,你的SW将收到Background Fetch成功事件。
  • 创建并打开一个与registration.id同名的新缓存。
  • 通过registration.matchAll()获取所有记录并遍历。

最后,通过Promise.all(),执行所有的承诺。

如何开发一个渐进式Web应用程序PWA

总结

在本文中我们讨论了PWA的基础组成部分的其中两部分:Manifest、Service Worker的基础功能介绍,因为HTTPS之前我们已经有过一些讨论了https://www.cnblogs.com/powertoolsteam/p/http2https.html

以上就是如何开发一个渐进式Web应用程序PWA的详细内容,更多关于开发一个渐进式Web应用程序的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
在IE浏览器中resize事件执行多次的解决方法
Jul 12 Javascript
全面兼容的javascript时间格式化函数(比较实用)
May 14 Javascript
利用jquery操作Radio方法小结
Oct 20 Javascript
node.js中的fs.futimes方法使用说明
Dec 17 Javascript
JavaScript点击按钮后弹出透明浮动层的方法
May 11 Javascript
jquery实现页面虚拟键盘特效
Aug 08 Javascript
再谈Javascript中的异步以及如何异步
Aug 19 Javascript
ES2015 Symbol 一种绝不重复的值
Dec 25 Javascript
bootstrap是什么_动力节点Java学院整理
Jul 14 Javascript
详解Vue开发微信H5微信分享签名失败问题解决方案
Aug 09 Javascript
javascript+css实现进度条效果
Mar 25 Javascript
uni-app使用微信小程序云函数的步骤示例
May 22 Javascript
Html5生成验证码的示例代码
May 10 #Javascript
Vue详细的入门笔记
如何理解Vue前后端数据交互与显示
Vue实现下拉加载更多
May 09 #Vue.js
微信小程序实现拍照和相册选取图片
微信小程序实现录音Record功能
如何用JavaScipt测网速
May 09 #Javascript
You might like
神族 Protoss 剧情介绍
2020/03/14 星际争霸
完美的2个php检测字符串是否是utf-8编码函数分享
2014/07/28 PHP
php使用fgetcsv读取csv文件出现乱码的解决方法
2014/11/08 PHP
自写的利用PDO对mysql数据库增删改查操作类
2018/02/19 PHP
php设计模式之享元模式分析【星际争霸游戏案例】
2020/03/23 PHP
javascript 二维数组的实现与应用
2010/03/16 Javascript
jquery.cvtooltip.js 基于jquery的气泡提示插件
2010/11/19 Javascript
一个挺有意思的Javascript小问题说明
2011/09/26 Javascript
在javascript中关于节点内容加强
2013/04/11 Javascript
js焦点文字滚动效果代码分享
2015/08/25 Javascript
轻松实现JavaScript图片切换
2016/01/12 Javascript
jquery.validate提示错误信息位置方法
2016/01/22 Javascript
jQuery 判断是否包含在数组中Array[]的方法
2016/08/03 Javascript
微信小程序 Template详解及简单实例
2017/01/05 Javascript
使用react-router4.0实现重定向和404功能的方法
2017/08/28 Javascript
Vue工程模板文件 webpack打包配置方法
2017/12/26 Javascript
简述vue路由打开一个新的窗口的方法
2018/11/29 Javascript
vue.js 打包时出现空白页和路径错误问题及解决方法
2019/06/26 Javascript
JS实现滚动条触底加载更多
2019/09/19 Javascript
JavaScript中EventBus实现对象之间通信
2020/10/18 Javascript
Echarts在Taro微信小程序开发中的踩坑记录
2020/11/09 Javascript
[10:54]Team Spirit vs Navi
2018/06/07 DOTA
[51:27]LGD vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
在Python中操作列表之list.extend()方法的使用
2015/05/20 Python
python遍历文件夹找出文件夹后缀为py的文件方法
2018/10/21 Python
python如何将两个txt文件内容合并
2019/10/18 Python
python:HDF和CSV存储优劣对比分析
2020/06/08 Python
python 读txt文件,按‘,’分割每行数据操作
2020/07/05 Python
使用python编写一个语音朗读闹钟功能的示例代码
2020/07/14 Python
do you have any Best Practice for testing
2016/06/04 面试题
主要负责人任命书
2014/06/06 职场文书
机械专业技术员求职信
2014/06/14 职场文书
2014年平安建设工作总结
2014/11/19 职场文书
裁员通知
2015/04/25 职场文书
龙猫观后感
2015/06/09 职场文书
java实现对Hadoop的操作
2021/07/01 Java/Android