开发一个渐进式Web应用程序(PWA)前都需要了解什么?

转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

原文出处:https://dzone.com/articles/how-to-build-a-progressive-web-app-pwa-with-javasc

 

自苹果推出了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库:

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

然后,到以下目录中  

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

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

1
npm i && npm start

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

 

 

应用的网址

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

使用以下命令安装ngrok: 

1
npm  install  -g ngrok

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

1
ngrok http 8080

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

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

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

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

 

 

Manifest清单文件

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

 

 

创建一个Manifest.json

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

JSON格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
   "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中。

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

Manifest 属性介绍

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

  • nameshort_name:指定Web应用的名称,short_name是该应用的简称,当没有足够空间展示应用的name属性时,系统就会使用short_name 。

  • l  display:display属性指定Web应用的显示模式,它有四个值可供配置:fullscreen、standalone、minimal-ui和browser,但一般常用的属性就是fullscreen和standalone。

    • fullscreen:全屏显示

    • standalone:这种模式下打开的应用不会出现浏览器的地址栏,所以因此看起来更像是一个原生应用

    • minimal-uibrowser:和使用浏览器访问区别不大。

  • l  orientation:控制Web应用的显示的方向及禁止手机转屏。

  • l  iconsbackground_color:icon用于指定应用图标,background_color是应用加载完成前的背景色,通过设置这两个属性,可组合成应用的Splash Screen。

  • l  theme_color:定义应用程序的默认主题颜色。

  • l  description:设置应用的一段描述内容。


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

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

 

 

 

什么是Service Worker

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

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

 

 

 

Service Worker生命周期

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

  • 首先应注册SW,如果SW已注册,浏览器会根据于安装事件自动开始安装。

  • 安装SW后,它将收到激活事件。此激活事件可用于清理SW早期版本的中使用的资源。

 

 

 

 

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

1
< base  href=”/”>

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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。  

 

 

Service Worker 事件

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

 

 

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

 

1
2
3
4
5
6
7
8
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中解决这个问题。

1
2
3
4
5
6
7
8
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文件代码:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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数组中的静态文件填充。

 

 

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

 

1
2
3
4
5
6
7
8
9
10
11
12
13
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也可以将传输的进度可以显示给用户,用户可以取消或暂停这个过程。

 

 

 

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

 

 

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

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

1
< button  id=”bgFetchButton”>Store assets locally</ button >

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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已注册

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

传输完成后放入缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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’);
      }
    })()
   );
});

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

  • l  当Background Fetch传输完成,你的SW将收到Background Fetch成功事件。

  • l  创建并打开一个与registration.id同名的新缓存。

  • l  通过registration.matchAll()获取所有记录并遍历。

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

 

总结 

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

后面如果大家感兴趣,我们可用一起讨论一些PWA的高级功能,这些功能可以为你的应用提供调用硬件的能力。如果有任何问题,欢迎通过评论区留言告诉我。 


猜你喜欢

转载自blog.51cto.com/powertoolsteam/2531747