说明
浏览器工作原理与实践专栏学习笔记
什么是 PWA
PWA,全称是 Progressive Web App,翻译过来就是渐进式网页应用。根据字面意思,它就是“渐进式 +Web 应用”。
PWA 所支持的首先是一个 Web 页面。
“渐进式”的理解:
站在 Web 应用开发者来说,PWA 提供了一个渐进式的过渡方案,让 Web 应用能逐步具有本地应用的能力。采取渐进式可以降低站点改造的代价,使得站点逐步支持各项新技术,而不是一步到位。
站在技术角度来说,PWA 技术也是一个渐进式的演化过程,在技术层面会一点点演进,比如逐渐提供更好的设备特性支持,不断优化更加流畅的动画效果,不断让页面的加载速度变得更快,不断实现本地应用的特性。
PWA 的定义:它是一套理念,渐进式增强 Web 的优势,并通过技术手段渐进式缩短和本地应用或者小程序的距离。
Web 应用 VS 本地应用
Web 缺陷
Web 应用缺少离线使用能力,在离线或者在弱网环境下基本上是无法使用的。而用户需要的是沉浸式的体验,在离线或者弱网环境下能够流畅地使用是用户对一个应用的基本要求。
Web 应用还缺少了消息推送的能力,因为作为一个 App 厂商,需要有将消息送达到应用的能力。
Web 应用缺少一级入口,也就是将 Web 应用安装到桌面,在需要的时候直接从桌面打开 Web 应用,而不是每次都需要通过浏览器来打开。
PWA 两种解决方案
- 通过引入
Service Worker
来试着解决离线存储和消息推送的问题 - 通过引入
manifest.json
来解决一级入口的问题
PWA 提供了 manifest.json 配置文件,可以让开发者自定义桌面的图标、显示名称、启动方式等信息,还可以设置启动画面、页面主题颜色等信息。
一个manifest.json格式如下:
{ // 必须的字段3个 "name": "MyExtension", // 扩展名称 "version": "1.0", // 版本。由1到4个整数构成。多个整数间用"."隔开 "manifest_version": 2, // manifest文件版本号。Chrome18开始必须为2 // 建议提供的字段3个 "description": "", // 描述。132个字符以内 "icons": { "16": "image/icon-16.png", "48": "image/icon-48.png", "128": "image/icon-128.png" }, //扩展图标。推荐大小16,48,128 "default_locale": "en", // 国际化 // 以下字段多选一,或者都不提供 "browser_action": { "default_icon": "image/icon-128.png", "default_title": "My Test", "default_popup": "html/browser.html" }, //地址栏右侧图标管理。含图标及弹出页面的设置等 "page_action": { "default_icon": "image/icon-48.png", "default_title": "My Test", "default_popup": "html/page.html" }, //地址栏最后附加图标。含图标及行为等 "theme": {}, // 主题,用于更改整个浏览器的外观 "app": {}, // 指定扩展需要跳转到的URL // 根据需要提供 "background": { "scripts": [ "lib/jquery-3.3.1.min.js", "js/background.js" ] , "page":"html/background.html" }, // 指定扩展进程的background运行环境 "chrome_url_overrides": { "pageToOverride": "html/overrides.html" }, //替换页面。详见注释1 "content_scripts": [{ "matches": ["https://www.baidu.com/*"], "css": ["css/mystyles.css"], "js": ["lib/jquery-3.3.1.min.js", "js/content.js"] }], // 指定在web页面运行的脚本。详见注释2 "content_security_policy": "", // 安全策略 "file_browser_handlers": [], "homepage_url": "http://xxx", // 扩展的官方主页 "incognito": "spanning", // 或"split"。详见注释3 "intents": {}, // 用户操作意图描述 "key": "", // 扩展唯一标识。不需要人为指定 "minimum_chrome_version": "1.0", // 扩展所需chrome的最小版本 "nacl_modules": [], // 消息与本地处理模块映射 "offline_enabled": true, // 是否允许脱机运行 "omnibox": { "keyword": "myKey" }, //ominbox即地址栏。用于响应地址栏的输入事件 "options_page": "aFile.html", // 选项页。用于在扩展管理页面跳转到选项设置 "permissions": [ "https://www.baidu.com/*", "background", "tabs" ], //权限。详见注释4 "plugins": [{ "path": "extension_plugin.dll", "public": true }], // 扩展。可调用第三方扩展 "requirements": {}, // 指定所需要的特殊技术。目前只支持"3D" "update_url": "http://path/to/updateInfo.xml", // 自动升级 "web_accessible_resources": [] // 指定资源路径,为String数组 }
什么是 Service Worker
在 2014 年的时候,标准委员会就提出了 Service Worker 的概念,它的主要思想是在页面和网络之间增加一个拦截器,用来缓存和拦截请求。
在 Service Worker 之前,WHATWG 小组就推出过用 App Cache 标准来缓存页面,不过在使用过程中 App Cache 所暴露的问题比较多,遭到多方吐槽。
Service Worker 的主要功能就是拦截请求和缓存资源。
Service Worker 结构示意图
下图可以看出:WebApp 请求资源时,会先通过 Service Worker,让它判断是返回 Service Worker 缓存的资源还是重新去网络请求资源。
Service Worker 的设计思路
1. 架构
Service Worker 来自 Web Worker 的一个核心思想:让其运行在主线程之外。
前面聊过:Web Worker 的目的是让 JavaScript 能够运行在页面主线程之外,不过由于 Web Worker 中是没有当前页面的 DOM 环境的,所以在 Web Worker 中只能执行一些和 DOM 无关的 JavaScript 脚本,并通过 postMessage 方法将执行的结果返回给主线程。
Service Worker 需要在 Web Worker 的基础之上加上储存功能
Service Worker 需要会为多个页面提供服务,不能把 Service Worker 和单个页面绑定起来
2. 消息推送
推荐文章:Service Worker学习与实践(三)——消息推送
下面部分来自该文:
Service Worker 中的消息推送是基于 Notification API的,这一API的使用首先需要用户授权,通过在 Service Worker 注册时的serviceWorkerRegistration.pushManager.subscribe 方法来向用户申请权限,如果用户拒绝了消息推送,应用程序也需要相关处理。
消息推送是基于谷歌云服务的,因此,在国内,收到 GFW (防火长城计划)的限制,这一功能的支持并不好,Google提供了一系列推送相关的库。
例如:Node.js中,使用 web-push来实现。
The common use case for this library is an application server using a GCM API key and VAPID keys.
const webpush = require('web-push'); // VAPID keys should be generated only once. const vapidKeys = webpush.generateVAPIDKeys(); webpush.setGCMAPIKey('<Your GCM API Key Here>'); webpush.setVapidDetails( 'mailto:example@yourdomain.org', vapidKeys.publicKey, vapidKeys.privateKey ); // This is the same output of calling JSON.stringify on a PushSubscription const pushSubscription = { endpoint: '.....', keys: { auth: '.....', p256dh: '.....' } }; webpush.sendNotification(pushSubscription, 'Your Push Payload Text');
一般原理是:在服务端生成公钥和私钥,并针对用户将其公钥和私钥存储到服务端,客户端只存储公钥。Service Worker的swReg.pushManager.subscribe 可以获取到 subscription,并发送给服务端,服务端利用 subscription 向指定的用户发起消息推送。
消息推送功能可以配合 clients API 做特殊处理。
如果用户安装了PWA应用,即使用户关闭了应用程序,Service Worker也在运行,即使用户未打开应用程序,也会收到消息通知。
3. 安全
关于安全,其中最为核心的一条就是 HTTP。
HTTP 采用的是明文传输信息,存在被窃听、被篡改和被劫持的风险
HTTPS 的通信数据都是经过加密的,即便拦截了数据,也无法破解数据内容,而且 HTTPS 还有校验机制,通信双方很容易知道数据是否被篡改
要使站点支持 Service Worker,首先必要的一步就是要将站点升级到 HTTPS。
Service Worker 还需要同时支持 Web 页面默认的安全策略,诸如同源策略、内容安全策略(CSP)等