摘要:配合的项,能够实现缓存外部资源的功能。允许接受来自的消息,默认值为。检查新版本的的更新信息。这也是我在研究阶段直接使用时所发现的问题。建议仅在生产模式内使用。
谈起PWA,许多人可能还只停留在“了解”的层面,比较少在实践中真正地尝试过,更多的仅仅是对着网上的教程和例子大概玩过。然而,网络上的例子多是简单的demo,鲜有与真正的开发相结合,例如和webpack的工程化结合。这篇文章将会从一个webpack plugin出发,谈一谈如何使用这个名为offline-plugin的webpack插件轻松实现PWA。
由于PWA相关的文章太多,所以本文不再对“什么是PWA”,“PWA的生命周期”等基础内容再次赘述。
offline-plugin相关链接:
offline-plugin
demo
一、自动生成service-worker.jsPWA的核心可谓是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具有如下优点:
1、更多的可选配置项,满足更加细致的配置要求;
2、更为详细的文档和例子;
3、更新频率相对更高,star数更多;
4、自动处理生命周期,用户无需纠结生命周期的坑;
*5、支持AppCache;
6、自动生成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生成的资源)。配合`Caches`的`additional`项,能够实现缓存外部资源的功能。 默认:`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不支持该方法 当更新信息被获取且浏览器正在进行资源更新时触发。在这个时刻,一些资源正在被下载。
onUpdateReady
当`onUpdating`事件完成时触发。这时,所有资源都已经下载完毕。 通过调用`runtime.applyUpdate()`方法来触发更新。
onUpdateFailed
当`onUpdating`事件因为某些原因失败时触发。 这时没有任何资源被下载,同时所有的资源更新进程都应该被取消或跳过。
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.js的max-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和其他静态资源一并打包到网站根目录即可:
示例地址:
打开chrome开发者工具,进入到Application一列,选择Manifest,就可以看到效果了:
截止到目前(2017年8月15日),我所使用的iOS10.3.2版本的iPhone7手机,已经支持PWA了,效果如下:
经过查阅大量的资料,到目前为止,iOS并不支持PWA,但是可以通过在html里面添加几个标签,实现web页面和原生APP相似的体验效果:
应用图标: 启动画面: 应用名称: 全屏效果: 设置状态栏颜色:
使用safari打开
添加到主屏后打开
离线后从主屏打开
打开任务管理器
可以看到,PWA无论从表现还是功能,都像一个独立的APP那样存在。
八、尾声原来一直以为苹果对PWA支持不好,但通过这次实践,可以知道其实PWA也取得了极大的推进,开发者们可以开心地搭建自己的PWA啦!
结论不能下太早。。。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/91391.html
摘要:前端日报精选理解的专题之偏函数译理解事件驱动机制游戏开发前端面试中的常见的算法问题发布中文前端页面传参尚妆产品技术刊读基础系列二之实现大转盘抽奖掘金指南众成翻译编程插入排序众成翻译源码讲解函数技术风暴初体验个人文 2017-08-16 前端日报 精选 理解 JavaScript 的 async/awaitJavaScript专题之偏函数[译]理解 Node.js 事件驱动机制Pokem...
摘要:而且,在接下来页面的异步请求中,还能进行缓存尝试这里配置的文件清单在安装激活阶段不会进行缓存,只有在监听到网络请求的时候才进行缓存。 基本知识普及请参考https://www.jianshu.com/p/623...https://zhuanlan.zhihu.com/p/... 下面简单介绍一下插件的使用以下是我在项目中使用的配置webpack.prod.conf.js中 { ...
摘要:本篇不包含所有坑,暂时只记录自己踩到的部分坑一安装安装最新版本安装新增依赖这个在中,本身和它的是在同一个包中,中将两个分开管理。我记录下自己更新这个的过程,以下前半部分可以直接跳过。以下记录踩坑过程。 本篇不包含所有坑,暂时只记录自己踩到的部分坑 一.安装 安装webpack4最新版本 npm install --save-dev webpack@4 安装新增依赖 webpack-c...
阅读 2555·2021-09-22 15:25
阅读 2962·2021-09-14 18:03
阅读 1211·2021-09-09 09:33
阅读 1699·2021-09-07 09:59
阅读 2930·2021-07-29 13:50
阅读 1500·2019-08-30 15:44
阅读 1715·2019-08-29 16:22
阅读 1287·2019-08-29 12:49