使用 Workbox 创建 PWA 应用

关注 前端瓶子君,回复“交流”

加入我们一起学习,天天进步

来源:悖论 

https://juejin.cn/post/6895306314747281421

前言

最近公司项目迭代逐渐放缓,下班时间逐渐变早,所以本着渐进增加的理念,在下班后,将公司项目进行了一下PWA改造

为何要改造成PWA

  1. 用户需求。我们的用户有许多电脑小白,不想记网址,又不会使用浏览器的收藏功能。以前使用的同类软件都有桌面版,有一种觉得桌面版比网页版可靠,使用简单的错觉,曾多次在钉钉售后群里反映,如何将网页保存至桌面,方便他下次直接在桌面打开

  2. PWA是渐进式的,如果用户的浏览器不支持ServiceWorker等构建PWA所需的API,并不会对其造成使用上的影响,并且通过埋点平台获知,我们用户Chrome浏览器数量占到80%左右

  3. 离线缓存,可安装,可拦截fetch等功能,对我个人有一定的吸引力,希望学习使用

开始改造

为了快速改造成PWA, 我这里选择使用了谷歌推出的PWA工具库workbox, 并且结合webpack创建serviceWorker文件

安装依赖

npm install --save-dev workbox-webpack-plugin
npm install --save workbox-core workbox-routing workbox-strategies workbox-precaching workbox-expiration workbox-cacheable-response

workbox-webpack-plugin里提供了两种插件,GenerateSW以及InjectManifest

GenerateSW

GenerateSW插件可以通过配置直接编译生成对应的serviceWorker文件,不需要我们直接编写serviceWorker文件。使用方式大致如下:

import { InjectManifest } from 'workbox-webpack-plugin';

new GenerateSW({
    skipWaiting: true,
    clientsClaim: true,
    mode: 'development',
    runtimeCaching: [
      {
        urlPattern: /^https?\:\/\/.+?\.alicdn.com\/.+$/,
        handler: 'StaleWhileRevalidate'
      },
    ],
});

通过GenerateSW编译生成serviceWorker文件虽然简单,但不够灵活,所以实际上我使用了另一个InjectManifestPlugin插件

InjectManifest

InjectManifest主要做了两件事

  1. 将webpack编译生成的资源文件清单,以变量self.__WB_MANIFEST的形式注入到我们提供的serviceWorker模板文件中

  2. 编译我们提供的模板文件,生成目标serviceWorker文件

使用方式大致如下:

const { InjectManifest } = require('workbox-webpack-plugin');
new InjectManifest({
    swSrc: path.resolve('src/sw.js'),
    swDest: path.resolve(BUILD_DEST, 'sw.js'),
}),

编写serviceWorker模板文件

预缓存静态资源

预缓存会在serviceWork激活后,立即请求并缓存所有预缓存清单中的文件, 之后下载请求同一资源时,会使用缓存优先策略,优先使用已经预缓存的资源

workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

路由请求缓存
  1. 使用NavigationRoute来缓存html文件
    
registerRoute(
  new NavigationRoute(
    new NetworkFirst({
      cacheName: 'navigation-cache',
      plugins: [
        new CacheableResponsePlugin({
          statuses: [200]
        }),
        new ExpirationPlugin({
          maxEntries: 300,
          maxAgeSeconds: 7 * 24 * 60 * 60,
        }),
      ],
    }),
  ),
);

  1. 缓存本地静态资源文件
    
registerRoute(
  /\.(css|js|png|jpg|jpeg|svg|webp)$/,
  new CacheFirst({
    cacheName: 'static-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [200]
      }),
      new ExpirationPlugin({
        maxEntries: 300,
        maxAgeSeconds: 7 * 24 * 60 * 60,
      }),
    ],
  }),
);

  1. 缓存cdn中的静态资源文件
    
registerRoute(
  /^https?\:\/\/.*?\.alicdn.com\/.+?\.(css|js|png|jpg|jpeg|svg|gif|webp)$/,
  new CacheFirst({
    cacheName: 'alicdn-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [200]
      }),
      new ExpirationPlugin({
        maxEntries: 300,
        maxAgeSeconds: 7 * 24 * 60 * 60,
      }),
    ],
  }),
);

这里有一个需要注意的点,alicdn静态资源与我司网页域名不是同域名,存在跨域,当请求静态资源的时候,会返回不透明响应(opaque response); 当我们使用Cache-First策略缓存不透明响应时,workbox会提示我们不要使用这个策略来缓存不透明响应,因为不透明响应对JavaScript来说是一个黑盒,无法获取到正确的status code, headers, body, 所以我们缓存中的资源是不可靠的;并且当我们缓存不透明响应时,缓存所占有的空间远大于实际资源的大小,容易造成DOMException: Quota exceeded. 所以需要处理下不透明响应的缓存

不透明响应变成透明响应

既然不透明响应会造成问题,那只要把不透明响应变成透明响应,那就应该没问题了。
经过查看,我发现alicdn的响应头会返回access-control-allow-origin: *, 后端是支持cors跨域资源共享的。既然如此,只要当我们请求静态资源的时候,让请求走cors应该就可以了。于是,我尝试在其中一个img标签中,启用cors

<img crossorigin="anonymous" />

不透明响应成功变成透明响应。但如果给所有<img /><script /><link />标签添加crossorigin, 这工作量也太大了。有没有统一处理的方法呢?有。可以通过拦截fetch请求来统一处理, 在使用workbox的场景下,可以通过设置缓存策略类中fetchOptions来实现

registerRoute(
  /^https?\:\/\/.*?\.alicdn.com\/.+?\.(css|js|png|jpg|jpeg|svg|gif|webp)$/,
  new CacheFirst({
    cacheName: 'alicdn-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [200]
      }),
      new ExpirationPlugin({
        maxEntries: 300,
        maxAgeSeconds: 7 * 24 * 60 * 60,
      }),
    ],
    // 添加如下fetch options
    fetchOptions: {
        mode: 'cors',
        credentials: 'omit',
    },
  }),
);

创建manifest.json文件

通过manifest配置文件,可以指定pwa应用的图标,初始页面,背景色,主题色,显示模式等内容

// manifest.json
{   
    "name": "xxx",
    "short_name": "xxx",
    "icons": [
        {
            "src": "/static/images/favicon@144x144.png",
            "sizes": "144x144",
            "type": "image/png"
        }
    ],
    "start_url": "/index.html",
    "display": "standalone",
    "background_color": "#000",
    "theme_color": "#000"
}

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

结语

最后,我们的PWA应用改造就完成了。PWA技术是一系列技术的集合,这里,我只用到了serviceWorker, manifest,push/notification等没有涉及到,如果日后有这个必要,再增加相应功能

延伸扩展

什么是不透明响应(opaque response)

简单的说,不透明响应就是当我们使用fetch,并且设置no-cors,来请求跨域资源时获取到的响应

fetch('https://www.baidu.com/img/flexible/logo/pc/result@2.png', {
  mode: 'no-cors'
}).then(response => {
  return console.log(response)
}).catch(error => {
  return console.log(error)
});

打印的结果为

Response {
  body: null
  bodyUsed: false
  headers: {},
  ok: false
  redirected: false
  status: 0
  statusText: ""
  type: "opaque"
  url: ""
}

从Response中,我们可以发现不透明响应

  1. status为0,而非200等http status code

  2. statusText为空

  3. headers也为空

  4. body也为空

总之,我们(JavaScript)获取不到这个Response中的内容

最后

欢迎关注「前端瓶子君」,回复「交流」加入前端交流群!

欢迎关注「前端瓶子君」,回复「算法」自动加入,从0到1构建完整的数据结构与算法体系!

另外,每周还有手写源码题,瓶子君也会解答哟!

》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue Workbox 是一个 Vue 插件,它和 Google 的 Workbox 库结合使用,提供了一种简单的方式来实现 Progressive Web App (PWA) 的功能。 Workbox 是一套用于构建 PWA 的工具库,它提供了一些预设的功能与策略,用于缓存、离线、推送等方面的处理。Vue WorkboxWorkbox 的功能集成到 Vue 项目中,使得开发者可以更方便地使用 Workbox,并和 Vue 的生态系统进行无缝的集成。 通过 Vue Workbox,开发者可以简单地在 Vue 项目中配置 Service Worker,该 Service Worker 可以用来缓存项目中的资源,包括 HTMLCSSJavaScript、图片等等。这样,当用户第一次访问应用程序时,这些资源将被缓存下来,下次用户再次访问应用程序时会更快地加载。 另外,Vue Workbox 还支持设置离线页面,在用户无法连接到互联网时展示一个离线提示页面,提供离线的浏览体验。这对于一些需要经常离线使用应用程序非常有用,比如笔记应用、待办事项应用等。 除了上述功能外,Vue Workbox 还提供了一些高级功能,比如支持 Web Push 推送通知,允许应用程序在用户离线时向其发送通知。这可以用于实现一些需要及时通知用户的功能,比如新消息提醒、提醒用户完成某些任务等。 总之,Vue Workbox 是一个非常有用的插件,它帮助开发者轻松地将 PWA 功能集成到 Vue 项目中,提供了一种更好的用户体验,使得 Web 应用程序具备了类似原生应用的功能和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值