本篇博客是针对PWA以及它所涉及关键技术的入门介绍,希望大家在读完博客以后能达到以下两个目的:
- 解决关于PWA的三个问题 (What、Why、How)
- 结合Angular从零构建一个简单的PWA应用
Q1: PWA是什么?
1. 简介
PWA(Progressive Web Apps 的简称,渐进式网页应用程序),是 Google 在 2015 年推出的一个项目,旨在将 Web 网页服务具备类似原生 App 的使用体验。
从定义上看PWA的本质还是网页应用程序,只是通过一些特定的技术模拟原生App的体验,实现了网页应用的渐进增强。渐进式意味着在不支持PWA的浏览器上,应用还是可以作为普通的Web App使用。
2. 使用步骤
接下来我们演示一下如何将使用了PWA技术的网站添加到手机主屏幕,再通过桌面入口进入以查看PWA应用。
操作系统:iOS 13.4.1 浏览器:Safari 网站:新浪微博、饿了么等使用了PWA技术的网站
操作步骤:
-
在手机端中Safari浏览器输入网址 m.weibo.cn/beta, 打开新浪微博网站。
-
点击浏览器下方操作按钮,弹出操作弹框,选中添加到主屏幕按钮,显示出网站快捷入口信息后点击添加按钮
说明: 由于Android与iOS上各浏览器添加到主屏幕的方式差距较大,需要大家自行探索其他浏览器添加到桌面的方式
3. 关键技术
PWA是一系列Web技术的合集,下面是其中比较关键的三个技术:
- Manifest: 应用清单
- Service Worker: 离线缓存
- Push Notification: 推送通知
Q2: 为什么选择PWA?
1. 优势
我们先来看一下传统Web App与Native App存在的问题,以及相对于前两者PWA的优势在哪。
Web App
- 手机没有入口,用户每次进入需要记住网址
- 不具备离线能力,用户在断网的情况下无法查看网站
- 不具备Native App的消息推送能力
- 界面显示效果和交互体验较差
Native App
- 软件上线需审核
- 软件更新需要将新版本上传到不同的应用商店
- 使用App必须下载
PWA
- 类似原生应用的显示和交互体验:桌面图标、离线缓存、消息推送、沉浸式界面
- 具备Web App操作和发布简单的优点:可持续更新、可链接
- 渐进增强,在不支持PWA的浏览器上仍可以作为普通Web App进行使用
- 无需下载,只需添加桌面入口,用完即走
2. 问题
- 用户缺乏将网页“添加主屏幕”的意识,而且不同浏览器“添加主屏幕”的方式不一样
- 兼容性问题:iOS仅Safari支持部分属性、Android部分浏览器支持
3. 总结
从上面的分析和对比可以看出:PWA规避了Web App和Native App的缺陷,同时将两者的优点结合,成为了功能和体验更强的Web App。虽然存在一定操作和兼容性问题,但是在不支持PWA的浏览器上我们还是可以像普通Web App一样进行使用,不会影响产品的主体功能。读者可以根据产品实际情况选择是否使用PWA。
Q3: 怎么构建PWA应用?
1.前置知识
在构建一个PWA应用前,我们需要对PWA涉及的关键技术有一个大概了解。在前面的章节我们已经知道了PWA涉及到了三个关键技术:Manifest、Service Worker、Push Notification。接下来我们会先简单介绍一下前两项技术,关于Push Notification的相关内容大家可以在入门后再去深入学习。
1.1 Manifest
Web App Manifest(应用清单) 是一个 W3C 规范,定义了一个包含应用信息的JSON List。
Manifest中通过name
、icons
等属性定义了PWA应用添加至桌面后的入口显示名称、图标、启动页配置和应用显示方式等。通过这个简单的JSON配置文件,可以实现类似原生应用的沉浸式界面显示效果。
普通网站与使用了Manifest配置的PWA效果对比图:
兼容性
Manifest中各配置项在iOS和Android的各个浏览器支持度有较大差异。总体来说,Android上Chrome浏览器支持度较高,所有配置项基本都支持。iOS上仅Safari浏览器支持部分属性,需要通过定义一些Meta标签来实现对应效果。
关于Manifest具体的配置项含义及使用我们会在后面的实例中再进行讲解,下面我们先来看看关于另一项关键技术Service Worker的介绍。
1.2 Service worker
Service Worker 就是一段运行在 Web 浏览器中,并为应用管理缓存的脚本。
它的功能就像一个网络代理,拦截所有由应用发出的 HTTP 请求,并选择如何给出响应。Service Worker通过提前缓存网站静态资源及拦截并缓存HTTP请求,实现网站的离线可用。
断网后普通网站与使用了Service Worker缓存网站的对比图:
特点
- 运行在worker线程中,与主浏览器线程独立,所以无法访问DOM等
- 事件驱动,通过监听Service Worker各生命周期事件来进行缓存
- 只能使用HTTPS和Localhost,具备较高安全性
兼容性
Android上大部分浏览器都支持,iOS仅Safari浏览器支持。
原理与实现
下面来分析一下Service Worker的运行周期以及用原生js如何实现Service Worker对网站静态资源的缓存。
- 生命周期
下图演示了Service Worker从注册到激活的整个生命周期,只有理解清楚它存在的各个阶段,才能理解原生js通过Service worker实现网站缓存的原理。
- 注册阶段
要想使用Service worker,首先需要在
index.html
界面注册Service Worker脚本即sw.js
文件。只有注册成功后,sw.js
文件上的缓存设置及事件监听才会生效。
- 安装阶段
当Service worker注册成功且浏览器解析完成后,就会进入到Service Worker的安装阶段,这个时候我们在sw.js
文件里通过监听install
事件,就能进行静态资源的缓存了。
- 激活阶段
当Service Worker安装成功后,Service Worker就被完全激活了,此时在sw.js
就可以通过监听fetch
事件来进行请求的缓存和处理。关于它的具体实现,大家可以自行深入学习实践,在这不做进一步讲解。
2.用Angular来构建PWA应用
对构建PWA项目的关键技术有了基本了解之后,接下来我们通过一个pwa-demo
实例来演示怎么在Angular的基础上构建一个PWA应用。
2.1 构建步骤
- 创建一个Angular项目
$ ng new pwa-demo
- 添加pwa支持
$ ng add @angular/pwa
- 添加本地服务器
$ npm install http-server -g
- 构建项目
$ ng build --prod
- 启动服务
http-server -p 8080 -c-1 dist/pwa-demo
2.2 项目结构
通过向pwa-demo
项目中添加pwa支持,会多出两个文件:manifest.webmanifest
、ngsw-config.json
,分别用于Manifest配置和Service Worker配置。
2.3 manifest配置
我们来看看如何通过PWA的应用信息配置,实现类似原生应用的桌面入口配置及界面效果。
manifest中比较常用的配置项及含义如下:
name
: 网站应用全名,显示在Android欢迎页上short_name
: 显示在主屏入口上的短名icons
: 加到主屏幕之后的图标start_url
: 应用启动的主页面地址display
: 应用的显示方式fullscreen / standalone / minimal-ui / browser
theme_color
: 浏览器UI的颜色background_color
: 启动页背景色
说明:
- 为了使
pwa-demo
项目有沉浸式界面显示效果,设置display
为standalone
,表示隐藏浏览器导航栏与底部操作栏,并作为独立的应用显示。 - 设置
theme_color
需要在index.html
头部设置<meta name="theme-color" content="#11214f">
才会生效 start_url
设为/
,意味着从网站中任意页面点击添加到主屏幕,从主屏幕快捷进入都是进入到网站的根路径icons
需要提供多个像素的icon
图片,Chrome 会要求你至少提供 192x192px 图标和 512x512px 图标,浏览器会选择最合适像素的图标进行入口图标的显示
在manifest.webmanifest
文件中的各配置项:
{
"name": "森亿星空影院",
"short_name": "星空影院",
"theme_color": "#ffffff",
"background_color": "#11214f",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}
在index.html
中引入manifest.webmanifest
文件并针对iOS做兼容配置一些meta
标签:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PwaDemo</title>
<base href="/">
<!--viewport-fit=cover 为适配iphoneX,使页面占满整个屏幕-->
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<!--控制状态栏、浏览器地址栏颜色-->
<meta name="theme-color" content="#11214f">
<!--兼容ios mainfest相关配置-->
<!--应用展示方式 display 通过设置yes进入standalone模式-->
<meta name="apple-mobile-web-app-capable" content="yes">
<!--状态栏样式 default 白色 / black 黑色 / black-translucent 灰色半透明 颜色随body颜色改变-->
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!--桌面应用名称 short_name-->
<!--<meta name="apple-mobile-web-app-title" content="星空影院">-->
<!--桌面图标 icons-->
<link rel="apple-touch-icon" href="assets/icons/icon-96x96.png">
<!--引入mainfest文件-->
<link rel="manifest" href="manifest.webmanifest">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body style="background: #11214f url('./assets/images/guidePage.jpg') no-repeat fixed top; background-size: 100% 100%; height: 100%;">
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>
2.4 Service Worker配置
在Angular中使用Service Worker比较简单,因为Angular框架本身已经封装好了关于Service Worker的缓存实现,开发人员只需通过ngsw-config.json
来针对项目实际情况配置不同的缓存策略。下面我们来看看如何通过ServiceWorker缓存配置,实现网站静态资源离线可用的效果。
Service Worker中配置项及含义如下:
assetGroups
: 静态资源组name
: 标识静态资源组installMode
: 资源最初的缓存方式prefetch / lazy
updateMode
: 更新后的缓存方式prefetch / lazy
resources
: 需要缓存的资源列表
dataGroups
数据资源组
说明:
prefetch
表示提前缓存,即不管资源有没有用到,都提前先缓存下来。lazy
表示当应用请求后再进行缓存- 在
pwa-demo
应用中配置了两种缓存策略,对html
、css
、js
的初次加载进行提前缓存,其他静态资源则请求时再进行缓存
在ngsw-config.json
文件中的各配置项:
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
在app.module.ts
中注册ngsw-worker.js
:
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
// ...
@NgModule({
declarations: [
// ...
],
imports: [
// ...
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
如上步骤就构建了一个简单的PWA项目,项目源码地址在文末会贴出,大家可以下载下来自行尝试。
小结
简单总结一下文章开头提出的三个问题:
- PWA是什么?
渐进式网页程序,通过离线缓存、应用清单、推送通知等技术实现类原生应用的效果。
- 为什么选择PWA?
兼具原生应用与WEB应用的优点,避免了原生应用的软件上线麻烦,又对基本的WEB应用进行了功能的渐进增强。
- 怎么构建PWA?
- 在WEB应用基础上配置Manifest配置文件
- 注册Service Worker,在单独的Worker线程实现应用文件和接口请求的缓存
- 通过 Push 和 Notification API 实现消息推送效果