PWA 基础

543 阅读6分钟

一、基本介绍

1.1 介绍

  • PWA:渐进式web应用,MDN地址: 渐进式 Web 应用(PWA) | MDN (mozilla.org)
  • Progressive Web APP,简称 PWA,是提升 Web App的体验的一种新方法,能给用户原生应用的体验。
  • PWA 运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web应用程序。
  • PWA能做到原生应用的体验不是靠特指某一项技术,而是经过应用一些新技术进行改进
  • 只要你拥有一个webapp,那么PWA的旅程就开始了
  • 现在 vue 和 react 的脚手架中都已经集成了PWA功能,你还在等什么?

1.2 优势

  • 渐进式-适用于所有浏览器,因为它是以渐进式增强作为宗旨开发的
  • 流畅-能够借助Service Worker在离线或者网络较差的情况下正常访问
  • 可安装-用户可以添加常用的webapp到面,免去去应用商店下载的麻烦
  • 原生体验-可以和app一样,拥有首屏加载动画,可以隐藏地址栏等沉浸式体验。
  • 粘性-通过推送离线通知等,可以让用户回流

二、核心技术

  • web app manifest是PWA技术集合中的一部分

  • web app manifest可以让网站安装到设备的主屏幕,而不需要用户通过应用商店进行下载

  • web app manifest,在一个JSON文本文件中提供有关应用程序的信息(如名称,作者,图标和描述)

  • 传统的web app入口:1.网址 2.书签,收藏夹 3.直接搜索

  • Web app manifest:

    可以添加到桌面,有唯一的图标和名称

    有启动时界面,避免生硬的过渡

    隐藏浏览器相关的UI,比如地址栏等

2.1 manifest 使用步骤

  • 在项目根目录下创建一个 manifest.json 文件
  • 在 index.html 中引入 manifest.json 文件
  • 在 manifest.json 文件中提供常见的配置
  • 需要在 https 协议或者 http://localhost 下访问项目
<link rel="manifest" href="manifest.json" />

创建 index.html

<html>
    <head>
        <!-- 引入 manifest 配置 -->
        <link rel="manifest" href="manifest.json"></link>
    </head>
  <body>
    <div>hello world</div>
  </body>
</html>

创建 manifest.json

{
    "name":"myapp"
}

控制台输入npm install http-server -g

2024-08-06_23-51-23.png

启动服务,控制台输入http-server

2024-08-06_23-52-30.png

2.2 manifest.json 常见配置

  • name: 用于指定应用的名称,用户安装横幅提示的名称,和启动画面中的文

  • shortname: 应用的短名称,用于主屏幕显示

  • start_url:指定用户从设备启动应用程序时加载的URL。可以是绝对路径和相对路径

  • icons: 用于指定可在各种环境中用作应用程序图标的图像对象数组,144x144

  • background_color: 用户指定启动动画的背景颜色

  • theme_color: 用于指定应用程序的主题颜色

  • display: 用于指定app的显示模式

    fullscreen 全屏显示,所有可用的显示区域都被使用,并且不显示状态栏

    standalone 让这个应用看起来像一个独立的应用程序,包括具有不同的窗口,在应用程序启动器中拥有自己的图标等。

    minimal-ui该应用程序将看起来像一个独立的应用程序,但会有浏览器地址栏。

2024-08-07_23-38-22.png

2.3 service worker

2.3.1 基础介绍

  • 一个标准的PWA程序,必须包含3个部分

    https服务器或者 http://localhost

    emanifest.json

    service worker

  • W3C组织早在 2014年5月就提出过Service Worker 这样的一个HTML5API,主要用来做持久的离线缓存。

  • 前端有很多性能优化的手段: CDN、CSS Sprite、文件的合并压缩、异步加载、资源缓存等等,这些手段都是用来做性能优化的,但是如果断网了,会发生什么?

  • service worker允许web应用在网络环境比较差或者是离线的环境下依旧可以使用

  • service worker可以极大的提升web app的用户体验

  • service worker是一个独立的 worker 线程,独立于当前网页进程,是一种特殊的web worier

2.3.2 service worker 介绍

  • 浏览器中的javaScript 都是运行在一个单一主线程上的,在同一时间内只能做一件事情
  • 随着Web业务不断复杂,我们逐渐在is中加了很多耗资源、耗时间的复杂运算过程,如果在主线程中计算,就会造成性能问题
  • w3c提供了web worker的api, Web Worker是脱离在主线程之外的,将一些复杂的耗时的活交给它干
  • 完成后通过 postMessage方法告诉主线程
  • Web worker是一个独立的运行环境,不能操作DOM和BOM

2.3.3 web worker 使用

  • 创建web worker:
  • var worker = new Worker('work.js')
  • 在web work中进行复杂的计算
  • Web work计算结束,通过self.postMessage(msg)给主线程发消息
  • 主线程通过workeronmessage=function(msg){}监听消息
  • 主线程也可以用同样的方式来给webworker进行通讯

创建 index.html

<html>
    <head>
        <!-- 引入 manifest 配置 -->
        <link rel="manifest" href="manifest.json"></link>
    </head>
  <body>
    <script>
      console.log('start')
      // 创建一个 web worker
      const worker = new Worker('work.js')
      worker.addEventListener('message',e=>{
        console.log(e.data)
      })
      console.log('end')

    </script>
  </body>
</html>

创建 work.js

// 注意: web worker 只是一个独立的线程,不能操作 dom 和 bom
// 适合做大量的计算
let total = 0
for(var i=0;i<1000000;i++){
    total += i
}

// 发消息给主线程,把结果给他
self.postMessage({result:total})

启动服务,控制台输入: http-server

2024-08-12_22-54-56.png

从控制台结果来看,web worker 是独立于主线程之外的,适合于做大量的计算,且可以和主线程经常沟通

2.4 web worker

2.4.1 基本介绍

  • Web Worker 是临时的,每次做的事的结果还不能被持久存下来,如果下次有同样的复杂操作,还得费时间的重新来一遍
  • 一旦被install,就永远存在,除非被手动unregister
  • 用到的时候可以直接唤醒,不用的时候自动睡眠
  • 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)
  • 离线内容开发者可控
  • 必须在HTTPS环境下才能工作
  • 异步实现,内部大都是通过Promise实现
2024-08-12_22-59-48.png

2.4.2 使用步骤

  • 在window.onload中注册service worker,防止与其他资源竞争
  • navigator对象中内置了serviceWorker属性
  • service worker在老版本的浏览器中不支持,需要进行浏览器兼容if('serviceWorker'innavigator){}
  • 注册service worker navigator.serviceWorker.register('./sw.js’),返回一个promise对象

创建 index.html

<html>
    <head>
        <!-- 引入 manifest 配置 -->
        <link rel="manifest" href="manifest.json"></link>
    </head>
  <body>
    <script>
      // 需要在网页加载完成的时候,注册service worker
      window.addEventListener('load',()=>{
        // 兼容性检查
        if('serviceWorker' in navigator){
          navigator.serviceWorker
          .register('./sw.js')
          .then(res=>{
            console.log(res)
          }).catch(err=>{
            console.log(err)
          })
        }
      })
    </script>
  </body>
</html>

创建 ws.js

console.log('loaded')

浏览器输出结果如下:

2024-08-12_23-22-36.png

2.4.3 生命周期

  • install 事件会在service worker注册成功的时候触发,主要用于缓存资源
  • activate 事件会在service worker激活的时候触发,主要用于删除旧的资源
  • fetch 事件会在发送请求的时候触发,主要用于操作缓存或者读取网络资源
  • 如果sw.js发生了改变,install事件会重新触发
  • activate事件会在install事件后触发,但是如果现在已经存在service worker了,那么就处于等待状态,直到当前service worker终止
  • 可以通过self.skipWaiting()方法跳过等待,返回一个promise对象
  • 可以通过event.waitUntil()方法扩的参数是一个promise对象,会在promise结束后才会结束当前生命周期函数,防止浏览器在异步操作之前就停止了生命周期
  • service worker激活后,会在下一次刷新页面的时候生效,可以通过self.clients.claim()立即获取控制权