当前位置: 首页 > news >正文

[译]前端离线指南(上)

原文链接:The offline cookbook 作者:Jake Archibald

[译]前端离线指南(下)

使用AppCache可以为我们提供几种支持内容离线工作的模式。如果这些模式正是你所需要的,那么恭喜你,你中了APPCache的大奖(尽管头等奖依然无人认领),但我们这些其余的人都挤在角落里来回摇摆(译者注:作者指的是由于设计上的原因,AppCache逐渐地被Web标准移除,虽然现在依然有浏览器支持这个功能,但最好不要再使用它了)

对于ServiceWorker(介绍),我们放弃尝试去解决离线问题,并且给开发者们提供灵活的组件从而让他们自己去解决离线问题。它为您提供了控制缓存和处理请求的方式。这就意味着您可以创建您自己的模式。接下来让我们来看一下几个隔离环境下的可行模式,但是在实践中,您可能会根据URL和context以串联方式用到其中的多个模式。

目前,除非另有说明,所有的示例代码都可以运行在Chrome和Firefox浏览器中。关于ServiceWorker支持程度的完整详情,请查阅"Is Service Worker Ready?"。

有关对于其中部分模式的运行演示,请查阅Trained-to-thrill,并且此处的视频将向您展示性能影响。

缓存机-何时开始存储资源?

您可以通过ServiceWorker来独立地从缓存中处理请求,所以我们要先单独地研究一下它们。首先,我们啥时候应该进行缓存呢?

安装时——以依赖的形式

ServiceWorker提供给您一个install事件,您可以使用它把资源准备好,即在处理其他事件之前必须要提前准备好的东西。但是当这些操作正在进行中的时候,任何旧版本的ServiceWorker仍旧在运行并且提供给页面,因此您在此处进行的操作一定不能中断它们。

适用于: CSS、图片、字体、JS文件、模板等,基本包含了你认为网站在当前“版本”中应该需要的所有静态资源。

如果未能获取上述资源,那么您的网站完全无法运行,对应的本机应用会将这些对象包含在初始下载中。

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('mysite-static-v3').then(function(cache) {
      return cache.addAll([
        '/css/whatever-v3.css',
        '/css/imgs/sprites-v6.png',
        '/css/fonts/whatever-v8.woff',
        '/js/all-min-v4.js'
        // etc
      ]);
    })
  );
});
复制代码

event.waitUntil接受一个promise对象作参数,来定义安装时长和安装是否成功,如果promise状态为rejected,则认为此次安装失败,并且抛弃ServiceWorker(如果一个旧版本的ServiceWorker正在运行,则它将保持不变)。caches.opencaches.addAll都返回promise对象,如果其中有任何一个资源获取失败,则caches.addAll会调用reject。 在trained-to-thrill 上,我使用此方法缓存静态资源。

安装时——不作为依赖

此方式与上述相似,但区别是:即使缓存失败,既不会延迟安装也不会导致安装失败。

适用于: 体积较大的,且暂时用不到的资源,比如用于游戏的较高级别的资源。

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('mygame-core-v1').then(function(cache) {
      cache.addAll(
        // levels 11-20
      );
      return cache.addAll(
        // core assets & levels 1-10
      );
    })
  );
});
复制代码

我们没有将levels 11-20的cache.addAll promise对象,返回给event.waitUntil,所以事件即使失败,游戏在离线的时候依然可以使用。当然,您必须考虑到缺少这些level的情况,如果缺少它们,则尝试重新缓存它们。

在当level 11-20正在下载的时候,ServiceWorker可能会终止,因为它已经完成处理事件。这就意味着它们就不会被缓存下来。未来,我们计划添加一个在后台下载的API以处理类似这样的情况,以及下载像电影一样的大体积文件。

激活时

适用于: 清理和迁移

在新的ServiceWorker已经被安装,并且较早版本的sw没有在使用的情况下,则新的ServiceWorker会被激活,您就会得到一个activate事件。由于旧版本的退出,所以此时非常适合处理 IndexedDB 中的架构迁移和删除未使用的缓存。

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          // 如果您想删除缓存,则返回true,
          // 但是请记住缓存在该域名内的所有页面之间
          // 是共享的
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
});
复制代码

在激活的过程中,诸如fetch等事件会被放置在一个队列中,所以一个长时间的激活可能会阻塞页面加载。保证您的激活尽可能地简洁,仅用于旧版本处于活动状态时无法执行的操作。

在trained-to-thrill上,我使用此方法移除旧缓存。

在用户交互时

适用于: 如果整个站点无法离线工作,您可以允许用户选择需要离线的可用内容,比如,YouTube上的某个视频,维基百科上的某篇文章,Flickr上的某张图片等等。

为用户提供一个“稍后阅读”或者“离线保存”的按钮。当点击按钮,从网络中获取您所需要的内容并把它放进缓存中。

document.querySelector('.cache-article').addEventListener('click', function(event) {
  event.preventDefault();

  var id = this.dataset.articleId;
  caches.open('mysite-article-' + id).then(function(cache) {
    fetch('/get-article-urls?id=' + id).then(function(response) {
      // /get-article-urls returns a JSON-encoded array of
      // resource URLs that a given article depends on
      return response.json();
    }).then(function(urls) {
      cache.addAll(urls);
    });
  });
});
复制代码

cacheAPI在页面既可以在ServiceWorker中获取到,也可以在页面中获取到,这就意味着你不必一定要通过ServiceWorker来向缓存中添加内容。

网络响应时

适用于: 频繁更新的资源,比如用户收件箱,或者文章内容。同样适用于不重要但需要谨慎处理的内容,比如用户头像。

如果请求的资源与缓存中的任何资源均不匹配,则从网络中获取,将其发送到页面中,同时将其添加到缓存中。

如果您针对一系列网址执行此操作,如头像,那么您需要谨慎,不要使域名下的存储变得臃肿,如果用户需要回收磁盘空间,您不会想成为主要候选对象。请确保将缓存中不再需要的项目删除。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
      return cache.match(event.request).then(function (response) {
        return response || fetch(event.request).then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});
复制代码

为了高效使用内存,只允许读取一次responserequestbody,在上面的代码中,使用.clone来创建能够单独地读取数据的额外副本。

在trained-to-thrill上,我使用此方法缓存Flickr图像。

Stale-while-revalidate

适用于: 频繁更新,但却没必要获取最新的资源。用户头像就属于此类。

如果缓存中已经有一个可用的版本,直接使用该版本,但是会为了下一次的请求而获取一个更新版本。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
      return cache.match(event.request).then(function(response) {
        var fetchPromise = fetch(event.request).then(function(networkResponse) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        })
        return response || fetchPromise;
      })
    })
  );
});
复制代码

它和 HTTP 的 stale-while-revalidate 非常相似。

推送消息时

注意: Chrome暂时还不支持Push。(译者注:Chrome 50及之后的版本开始支持,更多信息请参考 can i use)

Push API是基于ServiceWorker构建的另一个功能。它允许唤醒ServiceWorker以响应来自系统服务的消息,即使用户没有为您的站点打开一个标签,Push API也同样可以工作。只有ServiceWorker被唤醒。您从页面请求执行此操作权限的同时,用户也将收到提示。

适用于: 与通知有关的内容,比如聊天消息,突发新闻,或者Email等。同样适用于不经常更改的可立即同步的内容,例如待办事项更新或者日程表的更改。

用户常见的页面表现是,出现一个通知,当点击它的时候,会打开或者聚焦到相关的页面,但是在点击它之前,务必要更新缓存。显然,用户在收到推送消息的时候,一定是在线的,但是,当他们最终与通知交互时可能已经离线,因此,允许离线访问此内容非常重要。Twitter原生应用在大多数情况下都是非常好的离线优先例子,但在这点上却有点小问题。

如果没有网络连接,Twitter无法提供与推送消息相关的内容。但是点按通知会移除通,从而使用户获取的信息比点按通知之前还要少。不要这么做!

下面的代码会在展示通知之前更新缓存。

self.addEventListener('push', (event) => {
  if (event.data.text() == 'new-email') {
    event.waitUntil(async function() {
      const cache = await caches.open('mysite-dynamic');
      const response = await fetch('/inbox.json');
      await cache.put('/inbox.json', response.clone());
      const emails = await response.json();
      registration.showNotification("New email", {
        body: "From " + emails[0].from.name
        tag: "new-email"
      });
    }());
  }
});

self.addEventListener('notificationclick', function(event) {
  if (event.notification.tag == 'new-email') {
    // Assume that all of the resources needed to render
    // /inbox/ have previously been cached, e.g. as part
    // of the install handler.
    new WindowClient('/inbox/');
  }
});
复制代码

后台同步时

注意: 后台同步尚未加入到Chrome稳定版本中。(译者注:Chrome 49及之后的版本中开始支持,但FireFox、Safari尚未支持,更多信息请参考 can i use)

后台同步是基于ServiceWorker来构建的另一个功能。它允许您一次性地,或者按照(非常具有启发性的)时间间隔来请求后台数据同步。即使用户没有为您的站点打开一个标签,后台同步也同样可以工作。只有ServiceWorker被唤醒。您从页面请求执行此操作权限的同时,用户也将收到提示。

适用于: 不紧急的更新,尤其是那些定期进行的更新,每次更新都发送一个推送消息显得太频繁,比如社交时间表和新闻资讯。

请继续阅读: [译]前端离线指南(下)

相关文章:

  • 在应用开发中,网易云音乐如何兼顾质量和效益
  • 高级软件工程第八次作业:“两只小熊队”团队作业-5
  • JS基础(一)dom小实例
  • GitHub文件的克隆与上传
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • Git同时提交到多个远程仓库
  • 如果2020年出5G网络了,现在的手机是不是都被淘汰了?
  • 近似推断---高斯的变分混合
  • css教程
  • JSTL、EL、ONGL、Struts标签的区别与使用
  • 简单易用的leetcode开发测试工具(npm)
  • vue 轮播图插件 Vue-Awesome-Swiper
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • 示例vue 的keep-alive缓存功能的实现
  • API Test WebApiTestClient工具安装及使用
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • Angular 2 DI - IoC DI - 1
  • css属性的继承、初识值、计算值、当前值、应用值
  • extract-text-webpack-plugin用法
  • Magento 1.x 中文订单打印乱码
  • SpingCloudBus整合RabbitMQ
  • Vue 重置组件到初始状态
  • 后端_MYSQL
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 学习HTTP相关知识笔记
  • 正则表达式
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • # Maven错误Error executing Maven
  • #define,static,const,三种常量的区别
  • $(selector).each()和$.each()的区别
  • (02)Hive SQL编译成MapReduce任务的过程
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (39)STM32——FLASH闪存
  • (4)STL算法之比较
  • (Git) gitignore基础使用
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (四) 虚拟摄像头vivi体验
  • (一)VirtualBox安装增强功能
  • ***详解账号泄露:全球约1亿用户已泄露
  • .bat批处理(一):@echo off
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • [ 第一章] JavaScript 简史
  • [3300万人的聊天室] 作为产品的上游公司该如何?
  • [ABC294Ex] K-Coloring
  • [ANT] 项目中应用ANT
  • [Ariticle] 厚黑之道 一 小狐狸听故事