大发体育娱乐在线-大发体育娱乐官方网站-大发体育娱乐登录网址
做最好的网站

创建一个非常简单的离线页面,worker实现加速

来源:http://www.dfwstonefabricators.com 作者:前端学习 人气:75 发布时间:2019-09-26
摘要:利用 Service worker 创建一个非常简单的离线页面 2016/06/07 · JavaScript· 1 评论 · ServiceWorker 本文由 伯乐在线 -刘健超-J.c翻译,艾凌风校稿。未经许可,禁止转载! 英文出处:DeanHume。欢迎

利用 Service worker 创建一个非常简单的离线页面

2016/06/07 · JavaScript · 1 评论 · Service Worker

本文由 伯乐在线 - 刘健超-J.c 翻译,艾凌风 校稿。未经许可,禁止转载!
英文出处:Dean Hume。欢迎加入翻译组。

让我们想像以下情景:我们此时在一辆通往农村的火车上,用移动设备看着一篇很棒的文章。与此同时,当你点击“查看更多”的链接时,火车忽然进入了隧道,导致移动设备失去了网络,而 web 页面会呈现出类似以下的内容:

图片 1

这是相当令人沮丧的体验!幸运的是,web 开发者们能通过一些新特性来改善这类的用户体验。我最近一直在折腾 Service Workers,它给 web 带来的无尽可能性总能给我惊喜。Service Workers 的美妙特质之一是允许你检测网络请求的状况,并让你作出相应的响应。

在这篇文章里,我打算用此特性检查用户的当前网络连接状况,如果没连接则返回一个超级简单的离线页面。尽管这是一个非常基础的案例,但它能给你带来启发,让你知道启动并运行该特性是多么的简单!如果你没了解过 Service Worker,我建议你看看此 Github repo,了解更多相关的信息。

在该案例开始前,让我们先简单地看看它的工作流程:

  1. 在用户首次访问我们的页面时,我们会安装 Service Worker,并向浏览器的缓存添加我们的离线 HTML 页面
  2. 然后,如果用户打算导航到另一个 web 页面(同一个网站下),但此时已断网,那么我们将返回已被缓存的离线 HTML 页面
  3. 但是,如果用户打算导航到另外一个 web 页面,而此时网络已连接,则能照常浏览页面

使用Service worker实现加速/离线访问静态blog网站

2017/02/19 · JavaScript · Service Worker

原文出处: Yang Bo   

现在很流行基于Github page和markdown的静态blog,非常适合技术的思维和习惯,针对不同的语言都有一些优秀的静态blog系统出现,如Jekyll/Ruby,Pelican/Python,Hexo/NodeJs,由于静态内容的特性非常适合做缓存来加速页面的访问,就利用Service worker来实现加速,结果是除了PageSpeed,CDN这些常见的服务器和网络加速之外,通过客户端实现了更好的访问体验。

让我们开始吧

假如你有以下 HTML 页面。这虽然非常基础,但能给你总体思路。

XHTML

<!DOCTYPE html>

1
<!DOCTYPE html>

接着,让我们在页面里注册 Service Worker,这里仅创建了该对象。向刚刚的 HTML 里添加以下代码。

JavaScript

<script> // Register the service worker // 注册 service worker if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js').then(function(registration) { // Registration was successful // 注册成功 console.log('ServiceWorker registration successful with scope: ', registration.scope); }).catch(function(err) { // registration failed :( // 注册失败 :( console.log('ServiceWorker registration failed: ', err); }); } </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// Register the service worker
// 注册 service worker
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
    // Registration was successful
    // 注册成功
    console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
    // registration failed :(
    // 注册失败 :(
    console.log('ServiceWorker registration failed: ', err);
   });
}
</script>

然后,我们需要创建 Service Worker 文件并将其命名为 ‘service-worker.js‘。我们打算用这个 Service Worker 拦截全部网络请求,以此检查网络的连接性,并根据检查结果向用户返回最适合的内容。

JavaScript

'use strict'; var cacheVersion = 1; var currentCache = { offline: 'offline-cache' + cacheVersion }; const offlineUrl = 'offline-page.html'; this.addEventListener('install', event => { event.waitUntil( caches.open(currentCache.offline).then(function(cache) { return cache.addAll([ './img/offline.svg', offlineUrl ]); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
'use strict';
 
var cacheVersion = 1;
var currentCache = {
  offline: 'offline-cache' + cacheVersion
};
const offlineUrl = 'offline-page.html';
 
this.addEventListener('install', event => {
  event.waitUntil(
    caches.open(currentCache.offline).then(function(cache) {
      return cache.addAll([
          './img/offline.svg',
          offlineUrl
      ]);
    })
  );
});

在上面的代码中,我们在安装 Service Worker 时,向缓存添加了离线页面。如果我们将代码分为几小块,可看到前几行代码中,我为离线页面指定了缓存版本和URL。如果你的缓存有不同版本,那么你只需更新版本号即可简单地清除缓存。在大概在第 12 行代码,我向这个离线页面及其资源(如:图片)发出请求。在得到成功的响应后,我们将离线页面和相关资源添加到缓存。

现在,离线页面已存进缓存了,我们可在需要的时候检索它。在同一个 Service Worker 中,我们需要对无网络时返回的离线页面添加相应的逻辑代码。

JavaScript

this.addEventListener('fetch', event => { // request.mode = navigate isn't supported in all browsers // request.mode = naivgate 并没有得到所有浏览器的支持 // so include a check for Accept: text/html header. // 因此对 header 的 Accept:text/html 进行核实 if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) { event.respondWith( fetch(event.request.url).catch(error => { // Return the offline page // 返回离线页面 return caches.match(offlineUrl); }) ); } else{ // Respond with everything else if we can // 返回任何我们能返回的东西 event.respondWith(caches.match(event.request) .then(function (response) { return response || fetch(event.request); }) ); } });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
this.addEventListener('fetch', event => {
  // request.mode = navigate isn't supported in all browsers
  // request.mode = naivgate 并没有得到所有浏览器的支持
  // so include a check for Accept: text/html header.
  // 因此对 header 的 Accept:text/html 进行核实
  if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
        event.respondWith(
          fetch(event.request.url).catch(error => {
              // Return the offline page
              // 返回离线页面
              return caches.match(offlineUrl);
          })
    );
  }
  else{
        // Respond with everything else if we can
        // 返回任何我们能返回的东西
        event.respondWith(caches.match(event.request)
                        .then(function (response) {
                        return response || fetch(event.request);
                    })
            );
      }
});

为了测试该功能,你可以使用 Chrome 内置的开发者工具。首先,导航到你的页面,然后一旦安装上了 Service Worker,就打开 Network 标签并将节流(throttling)改为 Offline。(译者注:若将节流设置为 Offline 没效果,则可通过关闭网络或者通过360安全卫士禁止 Chrome 访问网络)

图片 2

如果你刷新页面,你应该能看到相应的离线页面!

图片 3

如果你只想简单地测试该功能而不想写任何代码,那么你可以访问我已创建好的 demo。另外,上述全部代码可以在 Github repo 找到。

我知道用在此案例中的页面很简单,但你的离线页面则取决于你自己!如果你想深入该案例的内容,你可以为离线页面添加缓存破坏( cache busting),如: 此案例。

加速/离线访问只需三步

  • 首页添加注册代码

JavaScript

<script> if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } </script>

1
2
3
4
5
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
  • 复制代码

将保存到你的网站根目录下

  • 修改不缓存域名列表及离线状态页面

在你的sw.js中修改

JavaScript

const ignoreFetch = [ /https?://cdn.bootcss.com//, /https?://static.duoshuo.com//, /https?://www.google-analytics.com//, /https?://dn-lbstatics.qbox.me//, ];

1
2
3
4
5
6
const ignoreFetch = [
  /https?://cdn.bootcss.com//,
  /https?://static.duoshuo.com//,
  /https?://www.google-analytics.com//,
  /https?://dn-lbstatics.qbox.me//,
];

打开Chrome Dev Tools->Source,看看自己的blog都引用了哪些第三方资源,逐个加到忽略列表里。

图片 4

在根目录下添加offline.html,在没有网络且缓存中也没有时使用,效果如下:

图片 5

在根目录下添加offline.svg,在无网络时图片资源请求返回该文件。

拓展阅读

此外,还有几个很棒的离线功能案例。如:Guardian 构建了一个拥有 crossword puzzle(填字游戏)的离线 web 页面 – 因此,即使等待网络重连时(即已在离线状态下),也能找到一点乐趣。我也推荐看看 Google Chrome Github repo,它包含了很多不同的 Service Worker 案例 – 其中一些应用案例也在这!

然而,如果你想跳过上述代码,只是想简单地通过一个库来处理相关操作,那么我推荐你看看 UpUp。这是一个轻量的脚本,能让你更轻松地使用离线功能。

打赏支持我翻译更多好文章,谢谢!

打赏译者

加速效果

首页加速后,网络请求从16降为1,加载时间从2.296s降为0.654s,得到了瞬间加载的结果。

图片 6

基于webpagetest

查看测试结果

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

图片 7 图片 8

1 赞 3 收藏 1 评论

加速/离线原理探索

关于作者:刘健超-J.c

图片 9

前端,在路上... 个人主页 · 我的文章 · 19 ·     

图片 10

什么是 Service worker

图片 11

如上图,Service worker 是一种由Javascript编写的浏览器端代理脚本,位于你的浏览器和服务器之间。当一个页面注册了一个 Service worker,它就可以注册一系列事件处理器来响应如网络请求和消息推送这些事件。Service worker 可以被用来管理缓存,当响应一个网络请求时可以配置为返回缓存还是从网络获取。由于Service worker 是基于事件的,所以它只在处理这些事件的时候被调入内存,不用担心常驻内存占用资源导致系统变慢。

Service worker生命周期

图片 12

Service worker 为网页添加一个类似于APP的生命周期,它只会响应系统事件,就算浏览器关闭时操作系统也可以唤醒Service worker,这点非常重要,让web app与native app的能力变得类似了。

Service worker在Register时会触发Install事件,在Install时可以用来预先获取和缓存应用所需的资源并设置每个文件的缓存策略。

一旦Service worker处于activated状态,就可以完全控制应用的资源,对网络请求进行检查,修改网络请求,从网络上获取并返回内容或是返回由已安装的Service worker预告获取并缓存好的资源,甚至还可以生成内容并返回给网络语法。

所有的这些都用户都是透明的,事实上,一个设计优秀的Service worker就像一个智能缓存系统,加强了网络和缓存功能,选择最优方式来响应网络请求,让应用更加稳定的运行,就算没有网络也没关系,因为你可以完全控制网络响应。

Service worker的控制从第二次页面访问开始

在首次加载页面时,所有资源都是从网络载的,Service worker 在首次加载时不会获取控制网络响应,它只会在后续访问页面时起作用。

图片 13

页面首次加载时完成install,并进入idle状态。

图片 14

页面第二次加载时,进入activated状态,准备处理所有的事件,同时 浏览器会向服务器发送一个异步 请求来检查Service worker本身是否有新的版本,构成了Service worker的更新机制。

图片 15

Service worker处理完所有的事件后,进入idle状态,最终进入terminated状态资源被释放,当有新的事件发生时再度被调用。

特点

  • 浏览器

Google Chrome,Firefox,Opera以及国内的各种双核浏览器都支持,但是 safari 不支持,那么在不支持的浏览器里Service worker不工作。

  • https

网站必须启用https来保证使用Service worker页面的安全性,开发时localhost默认认为是安全的。

  • non-block

Service worker 中的 Javascript 代码必须是非阻塞的,因为 localStorage 是阻塞性,所以不应该在 Service Worker 代码中使用 localStorage。

  • 单独的执行环境

Service worker运行在自己的全局环境中,通常也运行在自己单独的线程中。

  • 没有绑定到特定页面

service work能控制它所加载的整个范围内的资源。

  • 不能操作DOM

跟DOM所处的环境是相互隔离的。

图片 16

  • 没有浏览页面时也可以运行

接收系统事件,后台运行

  • 事件驱动,需要时运行,不需要时就终止

按需执行,只在需要时加载到内存

  • 可升级

执行时会异步获取最新的版本

实现加速/离线

Cache

网页缓存有很多,如HTTP缓存,localStorage,sessionStorage和cacheStorage都可以灵活搭配进行缓存,但操作太繁琐,直接使用更高级Service worker –本文的主人公。

添加Service worker入口

在web app的首页添加以下代码

JavaScript

<script> if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } </script>

1
2
3
4
5
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>

如果浏览器支持serviceWorker就注册它,不支持还是正常浏览,没有Service worker所提供的增强功能。

Service worker控制范围:
简单情况下,将sw.js放在网站的根目录下,这样Service worker可以控制网站所有的页面,,同理,如果把sw.js放在/my-app/sw.js那么它只能控制my-app目录下的页面。
sw.js放在/js/目录呢?更好的目录结构和范围控制呢?
在注册时指定js位置并设置范围。

JavaScript

navigator.serviceWorker.register('/js/sw.js', {scope: '/sw-test/'}).then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful with scope: ', registration.scope); }).catch(function(err) { // registration failed :( console.log('ServiceWorker registration failed: ', err); });

1
2
3
4
5
6
7
navigator.serviceWorker.register('/js/sw.js', {scope: '/sw-test/'}).then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });

Service worker实现

监听三个事件:

JavaScript

self.addEventListener('install', onInstall); self.addEventListener('fetch', onFetch); self.addEventListener("activate", onActivate);

1
2
3
self.addEventListener('install', onInstall);
self.addEventListener('fetch', onFetch);
self.addEventListener("activate", onActivate);

install

JavaScript

////////// // Install ////////// function onInstall(event) { log('install event in progress.'); event.waitUntil(updateStaticCache()); } function updateStaticCache() { return caches .open(cacheKey('offline')) .then((cache) => { return cache.addAll(offlineResources); }) .then(() => { log('installation complete!'); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//////////
// Install
//////////
function onInstall(event) {
  log('install event in progress.');
  event.waitUntil(updateStaticCache());
}
function updateStaticCache() {
  return caches
    .open(cacheKey('offline'))
    .then((cache) => {
      return cache.addAll(offlineResources);
    })
    .then(() => {
      log('installation complete!');
    });
}

install时将所有符合缓存策略的资源进行缓存。

fetch

JavaScript

//////// // Fetch //////// function onFetch(event) { const request = event.request; if (shouldAlwaysFetch(request)) { event.respondWith(networkedOrOffline(request)); return; } if (shouldFetchAndCache(request)) { event.respondWith(networkedOrCached(request)); return; } event.respondWith(cachedOrNetworked(request)); } onFetch做为浏览器网络请求的代理,根据需要返回网络或缓存内容,如果获取了网络内容,返回网络请求时同时进行缓存操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
////////
// Fetch
////////
function onFetch(event) {
  const request = event.request;
  if (shouldAlwaysFetch(request)) {
    event.respondWith(networkedOrOffline(request));
    return;
  }
  if (shouldFetchAndCache(request)) {
    event.respondWith(networkedOrCached(request));
    return;
  }
  event.respondWith(cachedOrNetworked(request));
}
onFetch做为浏览器网络请求的代理,根据需要返回网络或缓存内容,如果获取了网络内容,返回网络请求时同时进行缓存操作。

activate

JavaScript

/////////// // Activate /////////// function onActivate(event) { log('activate event in progress.'); event.waitUntil(removeOldCache()); } function removeOldCache() { return caches .keys() .then((keys) => { return Promise.all( // We return a promise that settles when all outdated caches are deleted. keys .filter((key) => { return !key.startsWith(version); // Filter by keys that don't start with the latest version prefix. }) .map((key) => { return caches.delete(key); // Return a promise that's fulfilled when each outdated cache is deleted. }) ); }) .then(() => { log('removeOldCache completed.'); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
///////////
// Activate
///////////
function onActivate(event) {
  log('activate event in progress.');
  event.waitUntil(removeOldCache());
}
function removeOldCache() {
  return caches
    .keys()
    .then((keys) => {
      return Promise.all( // We return a promise that settles when all outdated caches are deleted.
        keys
         .filter((key) => {
           return !key.startsWith(version); // Filter by keys that don't start with the latest version prefix.
         })
         .map((key) => {
           return caches.delete(key); // Return a promise that's fulfilled when each outdated cache is deleted.
         })
      );
    })
    .then(() => {
      log('removeOldCache completed.');
    });
}

在activate时根据version值来删除过期的缓存。

管理 Service worker

特定网站

  1. Google Chrome

Developer Tools->Application->Service Workers

图片 17

在这里还有三个非常有用的复选框:

  • Offline

模拟断网状态

  • Update on reload
    加载时更新
  • Bypass for network
    总是使用网络内容
  1. Firefox

只有在Settings里有一个可以在HTTP环境中使用Service worker的选项,适应于调试,没有单独网站下的Service worker管理。

图片 18

  1. Opera及其它双核浏览器同Google Chrome
    如果看到多个相同范围内的多个Service worker,说明Service woker更新后,而原有Service worker还没有被terminated。

浏览器全局

看看你的浏览器里都有哪些Service worker已经存在了

  1. Google Chrome

在地址栏里输入:

JavaScript

chrome://serviceworker-internals/

1
chrome://serviceworker-internals/

可以看到已经有24个Service worker了,在这里可以手动Start让它工作,也可以Unregister卸载掉。

图片 19

  1. Firefox

有两种方式进入Service worker管理界面来手动Start或unregister。

  • 菜单栏,Tool->Web Developer->Service workers
  • 地址栏中输入

JavaScript

about:debugging#workers

1
about:debugging#workers

图片 20

  1. Opera及其它双核浏览器同Google Chrome

更多

TODO:

  • Service workers的更新需要手动编辑version,每次发布新文章时需要编辑。
  • 使用AMP让页面渲染速度达到最高。

Ref links

Service Worker Cookbook

Is service worker ready?

Chrome service worker status page

Firefox service worker status page

MS Edge service worker status page

WebKit service worker status page

1 赞 2 收藏 评论

图片 21

本文由大发体育娱乐在线发布于前端学习,转载请注明出处:创建一个非常简单的离线页面,worker实现加速

关键词:

最火资讯