https://jakearchibald.com/2014/offline-cookbook/

在install中对依赖进行缓存

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
]);
})
);
});

  waitUntil接收的参数是一个promise,这个promise决定了install阶段的持续时长和是否成功。

在install中对非依赖进行缓存

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
);
})
);
});

  以上waitUntil接收到的promise,仅仅是对应加载了核心的缓存。虽然也加载了非关键的缓存(levels11-20),但是对install的状态无影响。

在activate阶段清除缓存

self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});

  Keep your activation as lean as possible, only use it for things you couldn't do while the old version was active

处理用户的交互

  给用户一个按钮,让用户选择保存某些资源以离线访问。资源的唯一标识与某个cache要一一对应

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);
});
});
});

  以上代码与sw是否被激活无关。也就是说cache和sw的运行是分开的两部分。

拦截网络请求

  当一个请求的响应不在cache中,就从网络获取,显示到界面上,最后保存到cache中。

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;
});
});
})
);
});

  以上的clone是为了让页面对response的读取与cache对response的读取分离开来,互不影响。

更新cache

  总是先更新缓存

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;
})
})
);
});

消息推送

后台同步

只使用缓存

self.addEventListener('fetch', function(event) {
// If a match isn't found in the cache, the response
// will look like a connection error
event.respondWith(caches.match(event.request));
});

只使用网络

self.addEventListener('fetch', function(event) {
event.respondWith(fetch(event.request));
// or simply don't call event.respondWith, which
// will result in default browser behaviour
});

缓存优先,后访问网络

self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
})

访问缓存和访问网络同时开始

  对于某些情况,古老的硬盘+病毒扫描软件+快速的网络。访问网络的速度可能比访问硬盘更快。

self.addEventListener('fetch', function(event) {
event.respondWith(
promiseAny([
caches.match(event.request),
fetch(event.request)
])
);
});

访问网络失败后才取缓存

self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function() {
return caches.match(event.request);
})
);
});

  用于资源经常更新的站点,当离线访问时才使用旧的资源。

访问缓存后再访问网络

var networkDataReceived = false;

startSpinner();

// fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
return response.json();
}).then(function(data) {
networkDataReceived = true;
updatePage();
}); // fetch cached data
caches.match('/data.json').then(function(response) {
if (!response) throw Error("No data");
return response.json();
}).then(function(data) {
// don't overwrite newer network data
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
// we didn't get cached data, the network is our last hope:
return networkUpdate;
}).catch(showErrorMessage).then(stopSpinner);

generic fallback

self.addEventListener('fetch', function(event) {
event.respondWith(
// Try the cache
caches.match(event.request).then(function(response) {
// Fall back to network
return response || fetch(event.request);
}).catch(function() {
// If both fail, show a generic fallback:
return caches.match('/offline.html');
// However, in reality you'd have many different
// fallbacks, depending on URL & headers.
// Eg, a fallback silhouette image for avatars.
})
);
});

模板

importScripts('templating-engine.js');

self.addEventListener('fetch', function(event) {
var requestURL = new URL(event.request); event.respondWith(
Promise.all([
caches.match('/article-template.html').then(function(response) {
return response.text();
}),
caches.match(requestURL.path + '.json').then(function(response) {
return response.json();
})
]).then(function(responses) {
var template = responses[0];
var data = responses[1]; return new Response(renderTemplate(template, data), {
headers: {
'Content-Type': 'text/html'
}
});
})
);
});

  大概意思就是app shell缓存在本地,然后请求动态数据,两者渲染,得出结果。

将以上的技巧组合到一起

install阶段进行资源缓存

访问网络后对资源进行缓存

访问资源时,缓存优先,找不到后再访问网络

访问缓存后再访问网络来更新数据

self.addEventListener('fetch', function(event) {
// Parse the URL:
var requestURL = new URL(event.request.url); // Handle requests to a particular host specifically
if (requestURL.hostname == 'api.example.com') {
event.respondWith(/* some combination of patterns */);
return;
}
// Routing for local URLs
if (requestURL.origin == location.origin) {
// Handle article URLs
if (/^\/article\//.test(requestURL.pathname)) {
event.respondWith(/* some other combination of patterns */);
return;
}
if (/\.webp$/.test(requestURL.pathname)) {
event.respondWith(/* some other combination of patterns */);
return;
}
if (request.method == 'POST') {
event.respondWith(/* some other combination of patterns */);
return;
}
if (/cheese/.test(requestURL.pathname)) {
event.respondWith(
new Response("Flagrant cheese error", {
status: 512
})
);
return;
}
} // A sensible default pattern
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});

完。

《offline coolbook》笔记的更多相关文章

  1. HTML+CSS笔记 CSS笔记集合

    HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...

  2. CSS笔记--选择器

    CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...

  3. HTML+CSS笔记 CSS中级 一些小技巧

    水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...

  4. HTML+CSS笔记 CSS中级 颜色&长度值

    颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...

  5. HTML+CSS笔记 CSS中级 缩写入门

    盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...

  6. HTML+CSS笔记 CSS进阶再续

    CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...

  7. HTML+CSS笔记 CSS进阶续集

    元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...

  8. HTML+CSS笔记 CSS进阶

    文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...

  9. HTML+CSS笔记 CSS入门续集

    继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...

  10. HTML+CSS笔记 CSS入门

    简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...

随机推荐

  1. javascript 中not defined 和undefined有什么区别

    概念上的解释:undefined是javascript语言中定义的五个原始类中的一个,换句话说,undefined并不是程序报错,而是程序允许的一个值.not defined是javascript在运 ...

  2. scrapy框架中Item Pipeline用法

    scrapy框架中item pipeline用法 当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的pyt ...

  3. JS面向对象方法(一): 使用原生JS 实现导航栏下多级分类弹出效果

    利用二级菜单的onmouseover/out事件 重新构建一级菜单 ".hover" 样式类 代码如下: CSS部分: 在原来的目标:hover样式中 增加 .hover状态 li ...

  4. 洛谷 P4174 [NOI2006]最大获利 && 洛谷 P2762 太空飞行计划问题 (最大权闭合子图 && 最小割输出任意一组方案)

    https://www.luogu.org/problemnew/show/P4174 最大权闭合子图的模板 每个通讯站建一个点,点权为-Pi:每个用户建一个点,点权为Ci,分别向Ai和Bi对应的点连 ...

  5. Machine Learning Codeforces - 940F(带修莫队) && 洛谷P4074 [WC2013]糖果公园

    以下内容未验证,有错请指正... 设块大小为T,则块数为$\frac{n}{T}$ 将询问分为$(\frac{n}{T})^2$块(按照左端点所在块和右端点所在块分块),同块内按时间从小到大依次处理 ...

  6. DTO和ViewModel的区别

    Data Transfer Object 数据传输对象 ViewModel 视图实体(我们在新建MVC项目是会发现Model文件夹下会有一些ViewModel实体) 简单的理解一下两者之间的区别,举个 ...

  7. 20170308web作业1

    代码20170308001: <%@ page language="java" import="java.util.*" pageEncoding=&qu ...

  8. org.apache.axis2.AxisFault: Service class XXXXX must have public as access Modifier解决方案

    使用Axis2工具生成客户端调用辅助类后,编写客户端调用代码运行时报错,完整错误信息如下: log4j:WARN No appenders could be found for logger (org ...

  9. [windows]命令行关机或重启电脑

    1.关机:菜单--〉运行--〉输入:cmd--〉输入:shutdown -s -t 0 2.重启:菜单--〉运行--〉输入:cmd--〉输入:shutdown -r -t 0 (注:“-r”代表重启, ...

  10. LR中操中MySQL脚本模板

    Action(){ char chQuery[128]; MYSQL *Mconn; int result; //引入mysql动态链接库 lr_load_dll("libmysql.dll ...