使用 offline-plugin 搭配 webpack 轻松实现 PWA

栏目: JavaScript · 发布时间: 5年前

内容简介:写于 2017.08.15谈起PWA,许多人可能还只停留在“了解”的层面,比较少在实践中真正地尝试过,更多的仅仅是对着网上的教程和例子大概玩过。然而,网络上的例子多是简单的demo,鲜有与真正的开发相结合,例如和webpack的工程化结合。这篇文章将会从一个webpack plugin出发,谈一谈如何使用这个名为由于PWA相关的文章太多,所以本文不再对“什么是PWA”,“PWA的生命周期”等基础内容再次赘述。

写于 2017.08.15

使用 offline-plugin 搭配 webpack 轻松实现 PWA

谈起PWA,许多人可能还只停留在“了解”的层面,比较少在实践中真正地尝试过,更多的仅仅是对着网上的教程和例子大概玩过。然而,网络上的例子多是简单的demo,鲜有与真正的开发相结合,例如和webpack的工程化结合。这篇文章将会从一个webpack plugin出发,谈一谈如何使用这个名为 offline-plugin 的webpack插件轻松实现PWA。

由于PWA相关的文章太多,所以本文不再对“什么是PWA”,“PWA的生命周期”等基础内容再次赘述。

offline-plugin 相关链接:

一、自动生成 service-worker.js

PWA的核心可谓是 service-worker (以后简称SW),任何一个PWA都有且只有一个 service-worker.js 文件,用于为SW添加资源列表,进行注册、激活等生命周期操作。但是在webpack构建的项目中,生成一个 service-worker.js 可能会面临两个较大的问题:

  • 1、webpack生成的资源多会生成一串hash,sw的资源列表里面需要同步更新这些带hash的资源;
  • 2、每次更新代码,都需要通过更新sw文件版本号来通知客户端对所缓存的资源进行更新。(其实只要这一次的sw代码和上一次的sw代码不一样即可触发更新,但使用明确的版本号会更加合适)。

看到这你可能已经想到,万能的webpack社区是否已经提供了相应的plugin来帮我们自动处理这些事情呢?答案是肯定的。除了官方推荐的 sw-precache-webpack-plugin 之外,还有我们今天的主角 offline-plugin

相比与 sw-precache-webpack-plugin ,个人认为 offline-plugin 具有如下优点:

manifest

二、基本使用

安装

npm install offline-plugin [--save-dev]
复制代码

初始化

第一步,进入 webpack.config :

// webpack.config.js example

var OfflinePlugin = require('offline-plugin');

module.exports = {
  // ...

  plugins: [
    // ... other plugins
    // it's always better if OfflinePlugin is the last plugin added
    new OfflinePlugin()
  ]
  // ...
}
复制代码

第二步,把 runtime 添加到你的入口js文件当中:

require('offline-plugin/runtime').install();
复制代码

ES6/Babel/TypeScript

import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
复制代码

经过上面的步骤, offline-plugin 已经集成到项目之中,通过webpack构建即可。

三、配置

前面说过, offline-plugin 支持细致的配置,以满足不同的需求。下面将介绍几个比较常用的配置项,方便大家进一步使用。

  • Caches: 'all' | Object

    告诉插件应该缓存什么东西,并以何种方式进行缓存 all : 意味着所有webpack构建出来的资源,以及在 externals 选项中的资源都会被缓存。 Object : 包含三个数组或正则的配置对象( main , additional , optional ),它们都是可选的,且默认为空。 默认: all

  • externals: Array 允许开发者指定一些外部资源(比如CDN引用,或者不是通过webpack生成的资源)。配合 Cachesadditional 项,能够实现缓存外部资源的功能。

    默认: null 举例: ['fonts/roboto.woff']

  • ServiceWorker: Object | null | false 该对象包含多个配置项,这里仅列举最常用的。

    events :布尔值。允许runtime接受来自sw的消息,默认值为false。 navigateFallbackURL :当一个URL请求从缓存或网络都无法被获取时,将会重定向到该选项所指向的URL。

  • AppCache: Object | null | false

    offline-plugin 默认支持 AppCache ,但是 AppCache 草案已经被web标准所废弃,不建议使用。 但是由于仍然有部分浏览器支持,所以插件默认提供这个功能。

四、runtime

上一节介绍了 offline-plugin 在webpack当中的配置,这一节将介绍runtime的一些用法。 若要使 offline-plugin 生效,用户必须在入口js文件中通过runtime进行初始化操作:

// 通过AMD方式
require('offline-plugin/runtime').install();

// 或者通过ES6/Babel/TypeScript方式

import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
复制代码

OfflinePluginRuntime 对象提供了下列三个方法:

  • install(options: Object) 开启ServiceWorker/AppCache的安装流程。这个方法是安全的,并且必须在页面初始化的时候就被调用。另外请勿把它放在任何的条件语句之内。(这句话不全对,在后面的降级方案里面会详细介绍)

  • applyUpdate() 接受当前所安装的sw的更新信息。

  • update() 检查新版本的ServiceWorker/AppCache的更新信息。

runtime.install() 方法接受一个配置对象参数,用于处理sw各个生命周期里面的事件:

  • onInstalled 当ServiceWorker/AppCache被install时执行,可用于展示“APP已经支持离线访问”。

  • onUpdating AppCache不支持该方法 当更新信息被获取且浏览器正在进行资源更新时触发。在这个时刻,一些资源正在被下载。

  • onUpdateReadyonUpdating 事件完成时触发。这时,所有资源都已经下载完毕。 通过调用 runtime.applyUpdate() 方法来触发更新。

  • onUpdateFailedonUpdating 事件因为某些原因失败时触发。 这时没有任何资源被下载,同时所有的资源更新进程都应该被取消或跳过。

  • onUpdated 当更新被接受时触发。

五、降级方案

当某些时候我们需要撤掉sw进行降级的时候,我们需要主动注销sw。然而 offline-plugin 默认没有提供注销sw的 unregister() 方法,所以我们需要自己实现。

其实要主动注销sw非常简单,我们可以直接调用 ServiceWorkerContainer.getRegistrations() 方法来拿到 registration 实例,然后调用 registration.unregister() 方法即可,具体代码如下:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.getRegistration().then((registration) => {
    registration && registration.unregister().then((boolean) => {
      boolean ? alert('注销成功') : alert('注销失败')
    });
  })
}
复制代码

在调用该方法后,sw已经被注销,刷新一下页面就能看到资源是重新从网络获取的了。

在真实的生产环境中,我们可以通过调用接口,来决定是否使用降级方案:

fetch(URL).then((switch) => {
  if (switch) {
    OfflinePluginRuntime.install()
  } else {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.getRegistration().then((registration) => {
        registration && registration.unregister().then((boolean) => {
          boolean ? alert('注销成功') : alert('注销失败')
        })
      })
    }
  }
})
复制代码

六、遇到的坑

在具体实践中,遇到一个比较大的坑,就是 sw.js 文件的更新。

在service worker的设计中,浏览器每一次加载站点的URL,都会重新请求一遍 sw.js 。若发现这一次的 sw.js 内容和上一次的不一样,就会判定为资源更新,重新触发sw的生命周期。然而, sw.js 也是一个普通的js资源文件,会默认使用服务器设置的expired时间,也就是它的 max-age 。在理解了service worker的设计后,我们不难发现, sw.jsmax-age 应该尽可能短,以便浏览器能够及时更新资源列表。

这也是我在研究阶段直接使用 http-server 时所发现的问题。后来在官方的例子中,我发现 npm script 里面是这么写的:

"start": "http-server ./dist -p 7474 -c no-cache"
复制代码

直接指定了所有资源都不使用缓存,这一点值得我们注意。

另外, webpack-dev-server 里无法正常使用 offline-plugin ,因为它需要具体的文件去生成 sw.js ,但是通过 webpack-dev-server 构建的项目,其文件是存放在内存中的,所以无法和 offline-plugin 正常搭配使用。建议仅在 生产模式 内使用 offline-plugin

七、添加到主屏

手机浏览器都提供了“添加到主屏”的功能,但普通的网站添加到主屏,仅仅是把网站的书签放到桌面。如果要想把网站以PWA的形式添加到主屏,我们需要一个manifest.json文件:

{
  "name": "offline-plugin",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#181743",
  "background_color": "#181743",
  "start_url": "/",
  "display": "standalone"
}
复制代码

然后,把这个 manifest.json 和其他 静态资源 一并打包到网站根目录即可:

使用 offline-plugin 搭配 webpack 轻松实现 PWA

示例地址:

使用 offline-plugin 搭配 webpack 轻松实现 PWA

打开chrome开发者工具,进入到 Application 一列,选择 Manifest ,就可以看到效果了:

使用 offline-plugin 搭配 webpack 轻松实现 PWA

截止到 目前(2017年8月15日) ,我所使用的 iOS10.3.2 版本的iPhone7手机,已经支持PWA了,效果如下: 经过查阅大量的资料,到目前为止,iOS并不支持PWA,但是可以通过在html里面添加几个标签,实现web页面和原生APP相似的体验效果:

应用图标:
<link rel="apple-touch-icon" href=“/custom_icon.png">

启动画面:
<link rel="apple-touch-startup-image" href="/launch.png">

应用名称:
<meta name="apple-mobile-web-app-title" content="AppTitle">

全屏效果:
<meta name="apple-mobile-web-app-capable" content="yes">

设置状态栏颜色:
<meta name="apple-mobile-web-app-status-bar-style" content="black">
复制代码

使用safari打开

使用 offline-plugin 搭配 webpack 轻松实现 PWA

添加到主屏后打开

使用 offline-plugin 搭配 webpack 轻松实现 PWA

离线后从主屏打开

使用 offline-plugin 搭配 webpack 轻松实现 PWA

打开任务管理器

使用 offline-plugin 搭配 webpack 轻松实现 PWA

可以看到,PWA无论从表现还是功能,都像一个独立的APP那样存在。

八、尾声

原来一直以为苹果对PWA支持不好,但通过这次实践,可以知道其实PWA也取得了极大的推进,开发者们可以开心地搭建自己的PWA啦! 结论不能下太早。。。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

超简单!一学就懂的互联网金融

超简单!一学就懂的互联网金融

视觉图文 / 人民邮电出版社 / 2015-2-1 / 45.00元

零基础、全图解,通过130多个精辟的知识点、220多张通俗易懂的逻辑图表,让您一书在手,即可彻底看懂、玩转互联网金融从菜鸟成为达人,从新手成为互联网金融高手! 本书主要特色:最简洁的版式+最直观的图解+最实用的内容。 本书细节特色:10章专题内容详解+80多个特别提醒奉献+130多个知识点讲解+220多张图片全程图解,深度剖析互联网金融的精华之处,帮助读者在最短的时间内掌握互联网金融知......一起来看看 《超简单!一学就懂的互联网金融》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具