PWA基础知识整理及实践

简介: PWA 能做到原生应用的体验不是靠特指某一项技术,而是经过应用一些新技术进行改进,在安全、性能和体验三个方面都有很大提升,PWA 本质上是 Web App,借助一些新技术也具备了 Native App 的一些特性,兼具 Web App 和 Native App 的优点。

PWA

Progressive Web App, 简称 PWA,是提升 Web App 的体验的一种新方法,能给用户原生应用的体验。

PWA 能做到原生应用的体验不是靠特指某一项技术,而是经过应用一些新技术进行改进,在安全、性能和体验三个方面都有很大提升,PWA 本质上是 Web App,借助一些新技术也具备了 Native App 的一些特性,兼具 Web App 和 Native App 的优点。

PWA 的主要特点包括下面三点:
可靠 - 即使在不稳定的网络环境下,也能瞬间加载并展现
体验 - 快速响应,并且有平滑的动画响应用户的操作
粘性 - 像设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面
PWA 本身强调渐进式,并不要求一次性达到安全、性能和体验上的所有要求,开发者可以通过 PWA Checklist 查看现有的特征。
如何开始呢?一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用。随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能。
接下来,开始一个简单的教程。

准备

建议安装live-server npm包和ngrok神器;live-server用于修改代码,浏览器自动刷新;ngrok用于内网转发渗透,且支持HTTPS协议,对外网接入PWA可以提供HTTPS;好处不言而喻。

生命周期

sw_lifecycle

From MDN

  • 安装( installing ):这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存。
  1. 事件回调中有两个方法:
  2. event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
  3. self.skipWaiting():self 是当前 context 的 global 变量,执行该方法表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态。
  • 安装后( installed ):Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。
  • 激活( activating ):在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装,并且清除了其他的 worker 以及关联缓存的旧缓存资源,等待新的 Service Worker 线程被激活。

activate 回调中有两个方法:

  1. event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
  2. self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。
  • 激活后( activated ):在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。
  • 废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束。

sw_events

From MDN

  • install:Service Worker 安装成功后被触发的事件,在事件处理函数中可以添加需要缓存的文件。
  • activate:当 Service Worker 安装完成后并进入激活状态,会触发 activate 事件。通过监听 activate 事件你可以做一些预处理,如对旧版本的更新、对无用缓存的清理等。
  • message:Service Worker 运行于独立 context 中,无法直接访问当前页面主线程的 DOM 等信息,但是通过 postMessage API,可以实现他们之间的消息传递,这样主线程就可以接受 Service Worker 的指令操作 DOM。

Service Work变量

  1. self: 表示 Service Worker 作用域, 也是全局变量
  2. caches: 表示缓存
  3. skipWaiting: 表示强制当前处在 waiting 状态的脚本进入 activate 状态
  4. clients: 表示 Service Worker 接管的页面

建立PWA工程、简单的Html页、 css 及 manifest.json ,并将 manifest.json通过如下代码引入hmtl。

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

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="./main.css">
    <link rel="manifest" href="./manifest.json">
    <title>Document</title>
</head>
<body>
    <h3>Hello PWA</h3>
</body>
</html>

manifest.json

{
  "name": "My PWA",
  "short_name": "MyWA",
  "display": "standalone",
  "start_url": "/",
  "theme_color": "#88eeff",
  "background_color": "#ccaaff",
  "icons": [
    {
      "src": "e.png",
      "sizes": "256x256",
      "type": "image/png"
    }
  ]
}

main.css

h3{
    color: #f00
}

添加 Service Worker

Service Worker 在网页已经关闭的情况下还可以运行, 用来实现页面的缓存和离线, 后台通知notification API等等功能。

if (navigator.serviceWorker != null) {
    navigator.serviceWorker.register('sw.js')
    .then(function(registration) {
      console.log('Registered events at scope: ', registration.scope);
    });
  }

定义要缓存的内容

在根目录新建sw.js文件,并写入如下内容。

let cacheStorageKey = 'pwa-1'; // 用于更新SW
let cacheList = [ // 缓存文件列表
    "/",
    "./index.html",
    "./main.css",
    "./icon.png",
    "./manifest.json"
];

缓存资源

使用Service Work,在注册后完成安装Server Work时,进行资源获取和缓存。

self.addEventListener("install", e=> {
    e.waitUntil(
        caches.open(cacheStorageKey) // 创建这个版本的缓存
        .then(cache=> cache.addAll(cacheList)) // 缓存资源
        .then(()=>self.skipWaiting()) // 为了在页面更新的过程当中, 新的 Service Worker 脚本能立即激活和生效。
    )
});

网络请求处理

在正式的网页中,可能伴随着从服务器端获取最新数据这样的业务;在Service Work 中是通过HTML5 fetch来拦截客户端请求的,如果本地缓存如 localStorage 或者 Cookie中有内容,则从其中获取,否则向服务器发送请求,将得到的数据返回页面并将其存储起来,供下次使用。

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.match(event.request).then(function (response) {
            // 在这代理可以搞一些代理的事情
            // 如果 Service Worker 有自己的返回,就直接返回,减少一次 http 请求
            if (response) {
                return response;
            }
            // 如果 service worker 没有返回,那就得直接请求真实远程服务
            var request = event.request.clone(); // 把原始请求拷过来
            return fetch(request).then(function (httpRes) {
                // http请求的返回已被抓到,可以处置了。
                // 请求失败了,直接返回失败的结果就好了。
                if (!httpRes || httpRes.status !== 200) {
                    return httpRes;
                }
                // 请求成功的话,将请求缓存起来。
                var responseClone = httpRes.clone();
                caches.open('my-test-cache-v1').then(function (cache) {
                    cache.put(event.request, responseClone);
                });
                return httpRes;
            });
        })
    );
});

更新资源

我们的PWA上线以后,肯定会进行更新,包括页面更新、数据更新等等的。那么怎么保证我们浏览器中的PWA程序和我们服务器上的程序是最新的呢?关键就在于最开始的 cacheStorageKey 。只要浏览器中的这个版本号和服务器端的版本号不一致,浏览器中的 Service Work 就会从服务器获取最新的,还有一点,浏览器中的 Service Work 每隔24小时自动会从服务器请求一次,从而保证本地的 PWA 是最新的,这也省去了用户更新的麻烦。

在什么时候更新呢,当然是PWA在激活状态时,直接上代码。

self.addEventListener('activate', function(e) {
    e.waitUntil(
        // 清理旧版本
        caches.keys().then(cacheNames=> {
            return Promise.all(
                cacheNames.filter(cacheNames=> {
                    return cacheNames !== cacheStorageKey
                }).map(cacheNames=> {
                    // 清理过期的缓存文件
                    return caches.delete(cacheNames);
                })
            ).then(()=>{
                return self.clients.clain(); // 更新所有客户端上的 Service Worker
            })
        })
    );
});

如果要进行版本跟新,只需要更改cacheStorageKey的值与之前的值不一致即可。
在新安装的 Service Worker 中通过调用 self.clients.claim() 取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面之后会被停止。

启动应用

通过 CMD 或者 shell 进入项目根目录,执行如下代码,即可查看到网页,并且还可以用ngrok进行代理,用手机或者其他设备即可访问带有HTTPS协议的PWA了。

live-server --port=4000 ## 启动4000端口

# 启动另一个 CMD 窗口或者 shell 窗口
ngrok http 4000 ## 代理本地的4000端口,输出一下内容

ngrok by @inconshreveable                                                                              (Ctrl+C to quit)

Session Status                online
Session Expires               7 hours, 59 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://cc14503e.ngrok.io -> localhost:4000
Forwarding                    https://cc14503e.ngrok.io -> localhost:4000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00

由此可以看到ngrok已经代理成功,我们可以打开如下带有https的地址进行访问。

Forwarding                    https://cc14503e.ngrok.io -> 

效果

sw

资源地址

PWA

目录
相关文章
|
2月前
|
缓存 前端开发 JavaScript
前端性能优化:从基础到进阶的实践指南
【10月更文挑战第4天】在前端开发中,性能优化至关重要,尤其随着Web应用的复杂化,用户对加载速度和响应性的要求日益提高。本文从基础知识入手,涵盖代码压缩、图片优化及缓存策略,并深入探讨代码拆分、懒加载和Web Workers等进阶技巧,帮助开发者全面提升Web应用的用户体验。通过这些方法,不仅能够减少页面加载时间,还能提升响应性和渲染性能,为用户提供更流畅的使用体验。
77 1
|
1月前
|
自然语言处理 安全 PHP
深入浅出PHP编程:从基础到实战
【10月更文挑战第36天】本文将带你走进PHP的奇妙世界,无论你是初学者还是有一定经验的开发者,都将从中获益。文章首先介绍PHP的基础概念和语法,然后通过实际代码示例,展示如何利用PHP进行高效的Web开发。最后,我们将探讨一些高级主题,如面向对象编程、数据库操作以及安全性问题。让我们一起开启PHP的学习之旅吧!
|
1月前
|
存储 Serverless PHP
PHP编程入门:从基础到实战
【10月更文挑战第35天】本文将带你走进PHP的世界,从最基本的语法开始,逐步深入到实际应用。我们将通过简单易懂的语言和实际代码示例,让你快速掌握PHP编程的基础知识。无论你是初学者还是有一定经验的开发者,都能在这篇文章中找到你需要的内容。让我们一起探索PHP的魅力吧!
|
1月前
|
存储 缓存 前端开发
前端技术探索:从基础到进阶的旅程
【10月更文挑战第23天】前端技术探索:从基础到进阶的旅程
26 0
|
1月前
|
存储 Java PHP
PHP编程之旅:从基础到实战
【10月更文挑战第23天】本文将带你走进PHP的世界,探索这个流行的服务器端脚本语言的魅力。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的信息和实用的代码示例。我们将从PHP的基本概念开始,然后深入到实际应用中,最后通过一个实战项目来巩固所学知识。让我们一起开启这段激动人心的PHP编程之旅吧!
26 0
|
4月前
|
移动开发 测试技术 开发工具
探索移动应用开发之旅:从概念到实战
【8月更文挑战第29天】在数字化时代的潮流中,移动应用已成为我们日常生活和工作中不可或缺的一部分。本文将引导读者了解移动应用的开发流程,包括设计思路、开发工具的选择以及操作系统的基本知识。我们将通过实际案例,深入探讨如何将一个想法转化为现实中的应用,同时确保内容的通俗易懂和结构的清晰性,为初学者提供一扇进入移动开发世界的大门。
37 1
|
7月前
|
存储 移动开发 前端开发
【Uniapp 专栏】Uniapp 架构设计与原理探究
【5月更文挑战第12天】Uniapp是一款用于跨平台移动应用开发的框架,以其高效性和灵活性脱颖而出。它基于HTML、CSS和Vue.js构建视图层,JavaScript处理逻辑层,管理数据层,实现统一编码并支持原生插件扩展。通过抽象平台特性,开发者能专注于业务逻辑,提高开发效率。尽管存在兼容性和复杂性挑战,但深入理解其架构设计与原理将助力开发者创建高质量的跨平台应用。随着技术进步,Uniapp将继续在移动开发领域扮演重要角色。
245 1
【Uniapp 专栏】Uniapp 架构设计与原理探究
|
6月前
|
算法 安全 Java
技术经验分享:JavaSecurity:Java加密框架(JCA)简要说明
技术经验分享:JavaSecurity:Java加密框架(JCA)简要说明
|
7月前
|
缓存 监控 前端开发
前端性能优化:从基础到进阶
前端性能优化:从基础到进阶
55 0
|
XML IDE 编译器
【C++】C++ 基础进阶【二】开发技巧
C++基础进阶,关于开发环境开发工具的一些便捷使用方式,提高生产力
171 0
【C++】C++ 基础进阶【二】开发技巧