SlideShare a Scribd company logo
1 of 72
Download to read offline
Старший инженер-разработчик клиентских приложений
Даньшин Артем
Кэширование данных с помощью Service Worker
Проблема
3
Очень много инструментов
4
Долго грузятся ресурсы
5
Плохое соединение или его отсутствие
Service Worker
Немного базы
8
Упрощенная схема работы SW
Сервер Service Worker Клиент
9
Это самый простой подключаемый JS файл
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/offline-service-worker.js');
}
10
Упрощенный жизненный цикл
11
Основан на событиях
this.addEventListener('install', function(event) {
...
});
this.addEventListener('activate', function(event) {
...
});
this.addEventListener('fetch', function(event) {
...
});
Статическое кэширование
13
Имеем приложение и его список файлов
14
Цель - сделать оффлайн режим
15
План действий
1. Кэшируем файлы у клиента
2. При запросе к этим файлам - отдать их из кэша
3. При отсутствии интернета - перевести клиента на оффлайн режим
4. При обновлении сборки, обновлять файлы в кэше
16
Придумываем имя для нашего хранилища
const CACHE_NAME = '0.1.0';
const CACHE_FILES = [
'/offline/index.html',
'/offline/offline.css',
'/offline/vendors.js',
'/offline/offline.js',
'/offline/offline.sprite.svg',
'/offline/fonts/pt_sans-regular.woff2',
'/offline/fonts/pt_serif-regular.woff2'
];
17
Кладем список файлов для кэширования в массив
const CACHE_NAME = '0.1.0';
const CACHE_FILES = [
'/offline/index.html',
'/offline/offline.css',
'/offline/vendors.js',
'/offline/offline.js',
'/offline/offline.sprite.svg',
'/offline/fonts/pt_sans-regular.woff2',
'/offline/fonts/pt_serif-regular.woff2'
];
18
Начинаем слушать событие установки
self.addEventListener('install', function(event) {
event.waitUntil(caches.open(CACHE_NAME).then(function(cache) {
return cache.addAll(CACHE_FILES);
})
)
});
19
Добавляем файлы в Cache API
self.addEventListener(‘install’, function(event) {
event.waitUntil(caches.open(CACHE_NAME).then(function(cache) {
return cache.addAll(CACHE_FILES);
})
)
});
20
Начинаем слушать событие запроса
self.addEventListener('fetch', function(event) {
event.respondWith(finalResponse(event.request));
});
function finalResponse(request) {
const path = new Url(request.url).pathname
const positionFileInCache = CACHE_FILES.indexOf(path);
if (positionFileInCache > -1) {
return caches.match(CACHE_FILES[positionFileInCache]);
}
return fetch(request)
.catch(() => caches.match(CACHE_FILES[0]));
}
21
Выполняем запрос вместо браузера (перехватываем)
self.addEventListener('fetch', function(event) {
event.respondWith(finalResponse(event.request));
});
function finalResponse(request) {
const path = new Url(request.url).pathname
const positionFileInCache = CACHE_FILES.indexOf(path);
if (positionFileInCache > -1) {
return caches.match(CACHE_FILES[positionFileInCache]);
}
return fetch(request)
.catch(() => caches.match(CACHE_FILES[0]));
}
22
Ловим запросы и проверяем их адрес
self.addEventListener('fetch', function(event) {
event.respondWith(finalResponse(event.request));
});
function finalResponse(request) {
const path = new Url(request.url).pathname
const positionFileInCache = CACHE_FILES.indexOf(path);
if (positionFileInCache > -1) {
return caches.match(CACHE_FILES[positionFileInCache]);
}
return fetch(request)
.catch(() => caches.match(CACHE_FILES[0]));
}
Request {…}
bodyUsed: false
credentials: "include"
headers: Headers {}
integrity: ""
method: "GET"
mode: "no-cors"
redirect: "follow"
referrer: "http://localhost:
8080/"
referrerPolicy: "no-referrer-
when-downgrade"
url: "http://localhost:8080/
offline/offline.css"

23
Если совпадает, то отдаем файл из хранилища
self.addEventListener('fetch', function(event) {
event.respondWith(finalResponse(event.request));
});
function finalResponse(request) {
const path = new Url(request.url).pathname
const positionFileInCache = CACHE_FILES.indexOf(path);
if (positionFileInCache > -1) {
return caches.match(CACHE_FILES[positionFileInCache]);
}
return fetch(request)
.catch(() => caches.match(CACHE_FILES[0]));
}
24
Если нет файла - отправляем запрос в интернет
self.addEventListener('fetch', function(event) {
event.respondWith(finalResponse(event.request));
});
function finalResponse(request) {
const path = new Url(request.url).pathname
const positionFileInCache = CACHE_FILES.indexOf(path);
if (positionFileInCache > -1) {
return caches.match(CACHE_FILES[positionFileInCache]);
}
return fetch(request)
.catch(() => caches.match(CACHE_FILES[0]));
}
25
Если нет интернета - отдаем страницу оффлайн режима
self.addEventListener('fetch', function(event) {
event.respondWith(finalResponse(event.request));
});
function finalResponse(request) {
const path = new Url(request.url).pathname
const positionFileInCache = CACHE_FILES.indexOf(path);
if (positionFileInCache > -1) {
return caches.match(CACHE_FILES[positionFileInCache]);
}
return fetch(request)
.catch(() => caches.match(CACHE_FILES[0]));
}
26
Начинаем слушать событие активации
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (CACHE_NAME !== key) {
return caches.delete(key);
}
}));
})
)
});
27
Удаляем старый кэш приложения
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (CACHE_NAME !== key) {
return caches.delete(key);
}
}));
})
)
});
28
Плюсы:
• Простота
• Жесткое кэширование файлов
29
Плюсы:
• Простота
• Жесткое кэширование файлов
Минусы:
• Необходимость вручную менять название хранища
30
Области применения:
• Кэширование наиболее статичных файлов
• Offline режимы сайтов
• Полноценные веб-приложения, с возможностью обновления
Динамическое кэширование
33
Цель - кэшировать и отдавать часто используемые ресурсы
в обход интернета
34
План действий
• Определяем, какие файлы будем кэшировать
• Ловим запросы, находим совпадения в URL
• Если запрос к нужному нам файлу - обрабатываем его
• Ищем запрос в кэше, если находим - отдаем клиенту
• Если в кэше нет - выполняем запрос
• Полученный ответ отправляем в кэш
• После - отправляем этот ответ клиенту
35
Формируем массив файлов для кэширования
const CACHE_FILES = [
'desktop.css',
'desktop.js',
'vendors.js',
'sprite.svg',
'pt_sans-bold.woff',
'pt_sans-regular.woff',
'pt_sans-caption-regular.woff',
'pt_sans-caption-bold.woff',
'pt_serif-bold.woff',
'pt_serif-italic.woff',
'pt_serif-regular.woff'
];
36
Начинаем слушать событие запроса
self.addEventListener('fetch', function(event) {
const request = event.request;
const fileName = request.url.split('/').pop().split('#')[0].split('?')[0];
const fileNameWithoutHash = fileName.substring(fileName.indexOf('.') + 1);
if (CACHE_FILES.includes(fileNameWithoutHash)) {
event.respondWith(finalResponse(fileNameWithoutHash, request));
}
});
37
Находим название нашего файла без hash’а
self.addEventListener('fetch', function(event) {
const request = event.request;
const fileName = getFileNameWithoutHash(request.url);
if (CACHE_FILES.includes(fileName)) {
event.respondWith(finalResponse(fileNameWithoutHash, request));
}
});
38
Ищем его в массиве файлов
self.addEventListener('fetch', function(event) {
const request = event.request;
const fileName = getFileNameWithoutHash(request.url);
if (CACHE_FILES.includes(fileName)) {
event.respondWith(finalResponse(fileNameWithoutHash, request));
}
});
39
Начинаем обрабатывать запрос
self.addEventListener('fetch', function(event) {
const request = event.request;
const fileName = getFileNameWithoutHash(request.url);
if (CACHE_FILES.includes(fileName)) {
event.respondWith(finalResponse(fileNameWithoutHash, request));
}
});
40
Пробуем взять файл из кэша
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromCache(cacheName, request) {
return caches.open(cacheName)
.then(cache => {
return cache.match(request)
.then(response => {
if (response) return response;
return Promise.reject();
});
});
}
41
Открываем хранилище
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromCache(cacheName, request) {
return caches.open(cacheName)
.then(cache => {
return cache.match(request)
.then(response => {
if (response) return response;
return Promise.reject();
});
});
}
42
Ищем запрос в хранилище
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromCache(cacheName, request) {
return caches.open(cacheName)
.then(cache => {
return cache.match(request)
.then(response => {
if (response) return response;
return Promise.reject();
});
});
}
43
Возвращаем ответ из кэша или оповещаем об отсутствии запроса
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromCache(cacheName, request) {
return caches.open(cacheName)
.then(cache => {
return cache.match(request)
.then(response => {
if (response) return response;
return Promise.reject();
});
});
}
44
При отсутствии запроса в кэше идем в интернет
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
45
Выполняем запрос
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
46
Если запрос прошел успешно - делаем дубликат ответа
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
47
Кэшируем полученный ответ
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
48
Удаляем старое хранилище/запрос
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
function cacheFile(cacheName, request, response) {
return caches.delete(cacheName)
.then(() => caches.open(cacheName))
.then(cache => cache.add(request, response));
}
49
Создаем новое хранилище
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
function cacheFile(cacheName, request, response) {
return caches.delete(cacheName)
.then(() => caches.open(cacheName))
.then(cache => cache.add(request, response));
}
50
Добавляем запрос и ответ в кэш
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
function cacheFile(cacheName, request, response) {
return caches.delete(cacheName)
.then(() => caches.open(cacheName))
.then(cache => cache.add(request, response));
}
51
Отдаем пользователю ответ
function finalResponse(fileName, request) {
return fromCache(fileName, request)
.catch(() => fromInternetAndCache(fileName, request));
}
function fromInternetAndCache(cacheName, request) {
return fetch(request)
.then(response => {
if (response.ok) {
const copyResponse = response.clone();
return cacheFile(cacheName, request, copyResponse)
.then(() => response);
}
return response;
});
}
52
При обновлении Service Worker’а
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(caches.delete));
})
);
});
В итоге
54
Файлы лежат в кэше
Плюсы
• Гибкое кэширование множества файлов
56
Плюсы
• Гибкое кэширование множества файлов
Минусы
• Достаточно сложная реализация
• Ответственность
57
Область применения
• Гибкое кэширование ресурсов
• Гибкое кэширование запросов
Инструменты
59
Инструменты для работы с Service Worker API
• - https://workboxjs.org/
• sw-precache -https://github.com/GoogleChromeLabs/sw-precache
• sw-toolbox -https://googlechromelabs.github.io/sw-toolbox/
• Webpack offline-plugin -https://github.com/NekR/offline-plugin
Нюансы
61
Требования и поддержка
• Забудьте об IE и Safari, Android 5.0 <
• Самая нижняя планка - Chrome 45(Сентябрь 2015)
• В результате: меньше размер кода, меньше костылей
62
Фильтрация запросов внутри события fetch
Проблема: SW ловит запросы ко всем сайтам
В результате:
• Может ошибочно выдать файл из хранилища
• Засоряет вкладу Network в Dev Tools
63
Создание черных и белых листов доменов
Whitelist - домены, запросы к которым SW будет обрабатывать
Blacklist - ограничение для поддоменов
const HOST_WHITELIST = [
"localhost",
"lenta.ru"
];
const HOST_BLACKLIST = [
"api",
"icdn"
];
function isSWRequest(url) {
const checkWhiteHost = HOST_WHITELIST.find(function(host) {
return url.host.indexOf(host) > -1;
});
const checkBlackHost = HOST_BLACKLIST.find(function(host) {
return url.host.indexOf(host) > -1;
});
return checkWhiteHost && !checkBlackHost;
}
Примеры
65
Lenta.ru (https://m.lenta.ru)
66
Financial Times (https://app.ft.com)
67
Smashing Magazine (https://www.smashingmagazine.com/)
Выводы
69
Service Worker…
• Мощный инструмент
• Достаточно простой
• Помогает стать ближе к уровню приложений
70
Полезные ссылки по теме:
• The offline cookbook - Jace Archibald -https://jakearchibald.com/2014/offline-cookbook/
• Репозиторий со ссылками на множество материалов по PWA - https://github.com/hemanth/
awesome-pwa
• Множество разборов кейсов использования Service Worker API - https://serviceworke.rs/
• Перевод статьи Making A Service Worker: A Case Study на Прогрессоре - http://prgssr.ru/
development/sozdaem-service-worker.html
71
Ссылки на материалы:
• https://goo.gl/AgZx6f - Код статического метода кэширования
• https://goo.gl/JJoeom - Код динамического метода кэширования
• https://yadi.sk/d/w4IOFx233P7hPc - ссылка на презентацию
Даньшин Артем
Старший инженер-разработчик клиентских приложений
Контакты:
Github: https://github.com/ArtDanshin
В контакте: https://vk.com/artdanshin
Спасибо за внимание

More Related Content

What's hot

Школа-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиШкола-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с данными
Глеб Тарасов
 
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
Andrey Rebrov
 
PowerShell Web Access Руководство по использованию
PowerShell Web Access Руководство по использованиюPowerShell Web Access Руководство по использованию
PowerShell Web Access Руководство по использованию
Andrey Markin
 

What's hot (10)

Школа-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиШкола-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с данными
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + Retrofit
 
RxJava+RxAndroid (Lecture 20 – rx java)
RxJava+RxAndroid (Lecture 20 – rx java)RxJava+RxAndroid (Lecture 20 – rx java)
RxJava+RxAndroid (Lecture 20 – rx java)
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"
 
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
WebCamp: Developer Day: Parse'им бэкенд - Аким ХалиловWebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
 
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
 
Next Gen Applications
Next Gen ApplicationsNext Gen Applications
Next Gen Applications
 
PowerShell Web Access Руководство по использованию
PowerShell Web Access Руководство по использованиюPowerShell Web Access Руководство по использованию
PowerShell Web Access Руководство по использованию
 
[Expert Fridays] Dmitry Isaev - Функциональные велосипеды в Java
[Expert Fridays] Dmitry Isaev - Функциональные велосипеды в Java[Expert Fridays] Dmitry Isaev - Функциональные велосипеды в Java
[Expert Fridays] Dmitry Isaev - Функциональные велосипеды в Java
 
Управление файловым сервером с помощью PowerShell
Управление файловым сервером с помощью PowerShellУправление файловым сервером с помощью PowerShell
Управление файловым сервером с помощью PowerShell
 

Similar to Кэширование данных с помощью Service Worker

Yii development
Yii developmentYii development
Yii development
MageCloud
 
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
Yandex
 
FPUG Dzyga presentation
FPUG Dzyga presentationFPUG Dzyga presentation
FPUG Dzyga presentation
Ivan Filimonov
 
JavaScript. Async (in Russian)
JavaScript. Async (in Russian)JavaScript. Async (in Russian)
JavaScript. Async (in Russian)
Mikhail Davydov
 
константин лебедев
константин лебедевконстантин лебедев
константин лебедев
kuchinskaya
 
Java осень 2012 лекция 8
Java осень 2012 лекция 8Java осень 2012 лекция 8
Java осень 2012 лекция 8
Technopark
 
Ilia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решенийIlia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решений
rit2010
 
Разработка расширяемых приложений на Django
Разработка расширяемых приложений на DjangoРазработка расширяемых приложений на Django
Разработка расширяемых приложений на Django
MoscowDjango
 

Similar to Кэширование данных с помощью Service Worker (20)

Yii development
Yii developmentYii development
Yii development
 
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. Асинхронность
 
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
Организация работы с API на Vue.js, Виталий Копачёв
Организация работы с API на Vue.js, Виталий КопачёвОрганизация работы с API на Vue.js, Виталий Копачёв
Организация работы с API на Vue.js, Виталий Копачёв
 
FPUG Dzyga presentation
FPUG Dzyga presentationFPUG Dzyga presentation
FPUG Dzyga presentation
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
Асинхронный JavaScript
Асинхронный JavaScriptАсинхронный JavaScript
Асинхронный JavaScript
 
JavaScript. Async (in Russian)
JavaScript. Async (in Russian)JavaScript. Async (in Russian)
JavaScript. Async (in Russian)
 
FileAPI 2.0
FileAPI 2.0FileAPI 2.0
FileAPI 2.0
 
Jsfwdays 2013-2
Jsfwdays 2013-2Jsfwdays 2013-2
Jsfwdays 2013-2
 
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
 
константин лебедев
константин лебедевконстантин лебедев
константин лебедев
 
Java осень 2012 лекция 8
Java осень 2012 лекция 8Java осень 2012 лекция 8
Java осень 2012 лекция 8
 
JS утиліти WordPress на практиці
JS утиліти WordPress на практиціJS утиліти WordPress на практиці
JS утиліти WordPress на практиці
 
Ilia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решенийIlia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решений
 
QA Fest 2017. Яна Кокряшкина. Интеграция автоматизированных тестов с инструме...
QA Fest 2017. Яна Кокряшкина. Интеграция автоматизированных тестов с инструме...QA Fest 2017. Яна Кокряшкина. Интеграция автоматизированных тестов с инструме...
QA Fest 2017. Яна Кокряшкина. Интеграция автоматизированных тестов с инструме...
 
course js day 4
course js day 4course js day 4
course js day 4
 
Разработка расширяемых приложений на Django
Разработка расширяемых приложений на DjangoРазработка расширяемых приложений на Django
Разработка расширяемых приложений на Django
 

Recently uploaded

Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Ирония безопасности
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
Хроники кибер-безопасника
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
Хроники кибер-безопасника
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
Ирония безопасности
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
Хроники кибер-безопасника
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
Хроники кибер-безопасника
 

Recently uploaded (9)

MS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdfMS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdf
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
 
Ransomware_Q3 2023. The report [RU].pdf
Ransomware_Q3 2023.  The report [RU].pdfRansomware_Q3 2023.  The report [RU].pdf
Ransomware_Q3 2023. The report [RU].pdf
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
 
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdfMalware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
 

Кэширование данных с помощью Service Worker

  • 1. Старший инженер-разработчик клиентских приложений Даньшин Артем Кэширование данных с помощью Service Worker
  • 5. 5 Плохое соединение или его отсутствие
  • 8. 8 Упрощенная схема работы SW Сервер Service Worker Клиент
  • 9. 9 Это самый простой подключаемый JS файл if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/offline-service-worker.js'); }
  • 11. 11 Основан на событиях this.addEventListener('install', function(event) { ... }); this.addEventListener('activate', function(event) { ... }); this.addEventListener('fetch', function(event) { ... });
  • 13. 13 Имеем приложение и его список файлов
  • 14. 14 Цель - сделать оффлайн режим
  • 15. 15 План действий 1. Кэшируем файлы у клиента 2. При запросе к этим файлам - отдать их из кэша 3. При отсутствии интернета - перевести клиента на оффлайн режим 4. При обновлении сборки, обновлять файлы в кэше
  • 16. 16 Придумываем имя для нашего хранилища const CACHE_NAME = '0.1.0'; const CACHE_FILES = [ '/offline/index.html', '/offline/offline.css', '/offline/vendors.js', '/offline/offline.js', '/offline/offline.sprite.svg', '/offline/fonts/pt_sans-regular.woff2', '/offline/fonts/pt_serif-regular.woff2' ];
  • 17. 17 Кладем список файлов для кэширования в массив const CACHE_NAME = '0.1.0'; const CACHE_FILES = [ '/offline/index.html', '/offline/offline.css', '/offline/vendors.js', '/offline/offline.js', '/offline/offline.sprite.svg', '/offline/fonts/pt_sans-regular.woff2', '/offline/fonts/pt_serif-regular.woff2' ];
  • 18. 18 Начинаем слушать событие установки self.addEventListener('install', function(event) { event.waitUntil(caches.open(CACHE_NAME).then(function(cache) { return cache.addAll(CACHE_FILES); }) ) });
  • 19. 19 Добавляем файлы в Cache API self.addEventListener(‘install’, function(event) { event.waitUntil(caches.open(CACHE_NAME).then(function(cache) { return cache.addAll(CACHE_FILES); }) ) });
  • 20. 20 Начинаем слушать событие запроса self.addEventListener('fetch', function(event) { event.respondWith(finalResponse(event.request)); }); function finalResponse(request) { const path = new Url(request.url).pathname const positionFileInCache = CACHE_FILES.indexOf(path); if (positionFileInCache > -1) { return caches.match(CACHE_FILES[positionFileInCache]); } return fetch(request) .catch(() => caches.match(CACHE_FILES[0])); }
  • 21. 21 Выполняем запрос вместо браузера (перехватываем) self.addEventListener('fetch', function(event) { event.respondWith(finalResponse(event.request)); }); function finalResponse(request) { const path = new Url(request.url).pathname const positionFileInCache = CACHE_FILES.indexOf(path); if (positionFileInCache > -1) { return caches.match(CACHE_FILES[positionFileInCache]); } return fetch(request) .catch(() => caches.match(CACHE_FILES[0])); }
  • 22. 22 Ловим запросы и проверяем их адрес self.addEventListener('fetch', function(event) { event.respondWith(finalResponse(event.request)); }); function finalResponse(request) { const path = new Url(request.url).pathname const positionFileInCache = CACHE_FILES.indexOf(path); if (positionFileInCache > -1) { return caches.match(CACHE_FILES[positionFileInCache]); } return fetch(request) .catch(() => caches.match(CACHE_FILES[0])); } Request {…} bodyUsed: false credentials: "include" headers: Headers {} integrity: "" method: "GET" mode: "no-cors" redirect: "follow" referrer: "http://localhost: 8080/" referrerPolicy: "no-referrer- when-downgrade" url: "http://localhost:8080/ offline/offline.css"

  • 23. 23 Если совпадает, то отдаем файл из хранилища self.addEventListener('fetch', function(event) { event.respondWith(finalResponse(event.request)); }); function finalResponse(request) { const path = new Url(request.url).pathname const positionFileInCache = CACHE_FILES.indexOf(path); if (positionFileInCache > -1) { return caches.match(CACHE_FILES[positionFileInCache]); } return fetch(request) .catch(() => caches.match(CACHE_FILES[0])); }
  • 24. 24 Если нет файла - отправляем запрос в интернет self.addEventListener('fetch', function(event) { event.respondWith(finalResponse(event.request)); }); function finalResponse(request) { const path = new Url(request.url).pathname const positionFileInCache = CACHE_FILES.indexOf(path); if (positionFileInCache > -1) { return caches.match(CACHE_FILES[positionFileInCache]); } return fetch(request) .catch(() => caches.match(CACHE_FILES[0])); }
  • 25. 25 Если нет интернета - отдаем страницу оффлайн режима self.addEventListener('fetch', function(event) { event.respondWith(finalResponse(event.request)); }); function finalResponse(request) { const path = new Url(request.url).pathname const positionFileInCache = CACHE_FILES.indexOf(path); if (positionFileInCache > -1) { return caches.match(CACHE_FILES[positionFileInCache]); } return fetch(request) .catch(() => caches.match(CACHE_FILES[0])); }
  • 26. 26 Начинаем слушать событие активации self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(keyList) { return Promise.all(keyList.map(function(key) { if (CACHE_NAME !== key) { return caches.delete(key); } })); }) ) });
  • 27. 27 Удаляем старый кэш приложения self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(keyList) { return Promise.all(keyList.map(function(key) { if (CACHE_NAME !== key) { return caches.delete(key); } })); }) ) });
  • 28. 28 Плюсы: • Простота • Жесткое кэширование файлов
  • 29. 29 Плюсы: • Простота • Жесткое кэширование файлов Минусы: • Необходимость вручную менять название хранища
  • 30. 30 Области применения: • Кэширование наиболее статичных файлов • Offline режимы сайтов • Полноценные веб-приложения, с возможностью обновления
  • 31.
  • 33. 33 Цель - кэшировать и отдавать часто используемые ресурсы в обход интернета
  • 34. 34 План действий • Определяем, какие файлы будем кэшировать • Ловим запросы, находим совпадения в URL • Если запрос к нужному нам файлу - обрабатываем его • Ищем запрос в кэше, если находим - отдаем клиенту • Если в кэше нет - выполняем запрос • Полученный ответ отправляем в кэш • После - отправляем этот ответ клиенту
  • 35. 35 Формируем массив файлов для кэширования const CACHE_FILES = [ 'desktop.css', 'desktop.js', 'vendors.js', 'sprite.svg', 'pt_sans-bold.woff', 'pt_sans-regular.woff', 'pt_sans-caption-regular.woff', 'pt_sans-caption-bold.woff', 'pt_serif-bold.woff', 'pt_serif-italic.woff', 'pt_serif-regular.woff' ];
  • 36. 36 Начинаем слушать событие запроса self.addEventListener('fetch', function(event) { const request = event.request; const fileName = request.url.split('/').pop().split('#')[0].split('?')[0]; const fileNameWithoutHash = fileName.substring(fileName.indexOf('.') + 1); if (CACHE_FILES.includes(fileNameWithoutHash)) { event.respondWith(finalResponse(fileNameWithoutHash, request)); } });
  • 37. 37 Находим название нашего файла без hash’а self.addEventListener('fetch', function(event) { const request = event.request; const fileName = getFileNameWithoutHash(request.url); if (CACHE_FILES.includes(fileName)) { event.respondWith(finalResponse(fileNameWithoutHash, request)); } });
  • 38. 38 Ищем его в массиве файлов self.addEventListener('fetch', function(event) { const request = event.request; const fileName = getFileNameWithoutHash(request.url); if (CACHE_FILES.includes(fileName)) { event.respondWith(finalResponse(fileNameWithoutHash, request)); } });
  • 39. 39 Начинаем обрабатывать запрос self.addEventListener('fetch', function(event) { const request = event.request; const fileName = getFileNameWithoutHash(request.url); if (CACHE_FILES.includes(fileName)) { event.respondWith(finalResponse(fileNameWithoutHash, request)); } });
  • 40. 40 Пробуем взять файл из кэша function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromCache(cacheName, request) { return caches.open(cacheName) .then(cache => { return cache.match(request) .then(response => { if (response) return response; return Promise.reject(); }); }); }
  • 41. 41 Открываем хранилище function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromCache(cacheName, request) { return caches.open(cacheName) .then(cache => { return cache.match(request) .then(response => { if (response) return response; return Promise.reject(); }); }); }
  • 42. 42 Ищем запрос в хранилище function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromCache(cacheName, request) { return caches.open(cacheName) .then(cache => { return cache.match(request) .then(response => { if (response) return response; return Promise.reject(); }); }); }
  • 43. 43 Возвращаем ответ из кэша или оповещаем об отсутствии запроса function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromCache(cacheName, request) { return caches.open(cacheName) .then(cache => { return cache.match(request) .then(response => { if (response) return response; return Promise.reject(); }); }); }
  • 44. 44 При отсутствии запроса в кэше идем в интернет function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); }
  • 45. 45 Выполняем запрос function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); }
  • 46. 46 Если запрос прошел успешно - делаем дубликат ответа function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); }
  • 47. 47 Кэшируем полученный ответ function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); }
  • 48. 48 Удаляем старое хранилище/запрос function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); } function cacheFile(cacheName, request, response) { return caches.delete(cacheName) .then(() => caches.open(cacheName)) .then(cache => cache.add(request, response)); }
  • 49. 49 Создаем новое хранилище function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); } function cacheFile(cacheName, request, response) { return caches.delete(cacheName) .then(() => caches.open(cacheName)) .then(cache => cache.add(request, response)); }
  • 50. 50 Добавляем запрос и ответ в кэш function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); } function cacheFile(cacheName, request, response) { return caches.delete(cacheName) .then(() => caches.open(cacheName)) .then(cache => cache.add(request, response)); }
  • 51. 51 Отдаем пользователю ответ function finalResponse(fileName, request) { return fromCache(fileName, request) .catch(() => fromInternetAndCache(fileName, request)); } function fromInternetAndCache(cacheName, request) { return fetch(request) .then(response => { if (response.ok) { const copyResponse = response.clone(); return cacheFile(cacheName, request, copyResponse) .then(() => response); } return response; }); }
  • 52. 52 При обновлении Service Worker’а self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(keyList) { return Promise.all(keyList.map(caches.delete)); }) ); });
  • 55. Плюсы • Гибкое кэширование множества файлов
  • 56. 56 Плюсы • Гибкое кэширование множества файлов Минусы • Достаточно сложная реализация • Ответственность
  • 57. 57 Область применения • Гибкое кэширование ресурсов • Гибкое кэширование запросов
  • 59. 59 Инструменты для работы с Service Worker API • - https://workboxjs.org/ • sw-precache -https://github.com/GoogleChromeLabs/sw-precache • sw-toolbox -https://googlechromelabs.github.io/sw-toolbox/ • Webpack offline-plugin -https://github.com/NekR/offline-plugin
  • 61. 61 Требования и поддержка • Забудьте об IE и Safari, Android 5.0 < • Самая нижняя планка - Chrome 45(Сентябрь 2015) • В результате: меньше размер кода, меньше костылей
  • 62. 62 Фильтрация запросов внутри события fetch Проблема: SW ловит запросы ко всем сайтам В результате: • Может ошибочно выдать файл из хранилища • Засоряет вкладу Network в Dev Tools
  • 63. 63 Создание черных и белых листов доменов Whitelist - домены, запросы к которым SW будет обрабатывать Blacklist - ограничение для поддоменов const HOST_WHITELIST = [ "localhost", "lenta.ru" ]; const HOST_BLACKLIST = [ "api", "icdn" ]; function isSWRequest(url) { const checkWhiteHost = HOST_WHITELIST.find(function(host) { return url.host.indexOf(host) > -1; }); const checkBlackHost = HOST_BLACKLIST.find(function(host) { return url.host.indexOf(host) > -1; }); return checkWhiteHost && !checkBlackHost; }
  • 69. 69 Service Worker… • Мощный инструмент • Достаточно простой • Помогает стать ближе к уровню приложений
  • 70. 70 Полезные ссылки по теме: • The offline cookbook - Jace Archibald -https://jakearchibald.com/2014/offline-cookbook/ • Репозиторий со ссылками на множество материалов по PWA - https://github.com/hemanth/ awesome-pwa • Множество разборов кейсов использования Service Worker API - https://serviceworke.rs/ • Перевод статьи Making A Service Worker: A Case Study на Прогрессоре - http://prgssr.ru/ development/sozdaem-service-worker.html
  • 71. 71 Ссылки на материалы: • https://goo.gl/AgZx6f - Код статического метода кэширования • https://goo.gl/JJoeom - Код динамического метода кэширования • https://yadi.sk/d/w4IOFx233P7hPc - ссылка на презентацию
  • 72. Даньшин Артем Старший инженер-разработчик клиентских приложений Контакты: Github: https://github.com/ArtDanshin В контакте: https://vk.com/artdanshin Спасибо за внимание