关于前端:使用Workbox创建PWA应用

前言

最近公司我的项目迭代逐步放缓,下班时间逐步变早,所以本着渐进减少的理念,在上班后,将公司我的项目进行了一下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中的内容

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理