diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
commit | 074785cea106179cb3305637055ab0a009ca74f2 (patch) | |
tree | e6ae371cccd642aa2b67f39752a2cdf1fd4eb040 /files/ru/web/api/service_worker_api | |
parent | da78a9e329e272dedb2400b79a3bdeebff387d47 (diff) | |
download | translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.gz translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.bz2 translated-content-074785cea106179cb3305637055ab0a009ca74f2.zip |
initial commit
Diffstat (limited to 'files/ru/web/api/service_worker_api')
-rw-r--r-- | files/ru/web/api/service_worker_api/index.html | 280 | ||||
-rw-r--r-- | files/ru/web/api/service_worker_api/using_service_workers/index.html | 524 |
2 files changed, 804 insertions, 0 deletions
diff --git a/files/ru/web/api/service_worker_api/index.html b/files/ru/web/api/service_worker_api/index.html new file mode 100644 index 0000000000..be8e729086 --- /dev/null +++ b/files/ru/web/api/service_worker_api/index.html @@ -0,0 +1,280 @@ +--- +title: Service Worker API +slug: Web/API/Service_Worker_API +tags: + - API + - Landing + - NeedsTranslation + - Offline + - Overview + - Reference + - Service Workers + - TopicStub + - Workers +translation_of: Web/API/Service_Worker_API +--- +<div> +<p>{{ServiceWorkerSidebar}}</p> + +<p class="summary">Service worker фактически выполняет роль прокси-сервера, находящегося между веб-приложением и браузером, а также сетью (если доступна). Он позволяет (кроме прочего) описывать корректное поведение веб-приложения в режиме офлайн, перехватывать запросы сети и принимать соответствующие меры, основываясь на доступности сети, и обновлять данные, находящиеся на сервере при доступе к нему. Также они имеют доступ к push-уведомлениям и API для фоновой синхронизации.</p> +</div> + +<h2 id="Концепция_и_использование_Service_Worker">Концепция и использование Service Worker</h2> + +<p>Service worker — это событийно-управляемый <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker">worker</a>, регистрируемый на уровне источника и пути. Он представляет собой JavaScript-файл, который может контролировать веб-страницу/сайт, с которым он ассоциируется, перехватывать и модифицировать запросы навигации и ресурсов, очень гибко кешировать ресурсы, для того чтобы предоставить вам полный контроль над тем, как приложение ведет себя в определенных ситуациях (например, когда сеть не доступна).</p> + +<p>Service worker запускается в контексте worker'ов, поэтому он не имеет доступа к DOM и работает в потоке отдельном от основного потока JavaScript, управляющего вашим приложением, а следовательно — не блокирует его. Он призван быть полностью асинхронным; как следствие, синхронные API, такие как <a href="/en-US/docs/Web/API/XMLHttpRequest">XHR</a> и <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage">localStorage</a>, в Service Worker'е использовать нельзя.</p> + +<p>Из соображений безопасности service worker'ы работают только по HTTPS (либо, в целях разработки, на <code>localhost</code>). Давать сторонним людям возможность измененять сетевые запросы крайне опасно. Кроме того, Service Worker API недоступен в <a href="https://support.mozilla.org/ru/kb/privatnyj-prosmotr-prosmotr-veb-stranic-bez-sohran">режиме приватного просмотра</a> браузера Firefox.</p> + +<div class="note"> +<p><strong>Заметка</strong>: Service Worker'ы выигрывают у предыдущих решений, таких как <a href="http://alistapart.com/article/application-cache-is-a-douchebag">AppCache,</a> потому что не делают предположений о том, что вы пытаетесь сделать, и не ломаются, в случаях если их предположения не оказываются верными; вы имеете полный контроль над всем.</p> +</div> + +<div class="note"> +<p><strong>Заметка</strong>: Service worker'ы широко используют промисы (<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a>). В общем случае они будут ждать ответа, после которого вернутся с успешным или неудачным завершением. Архитектура на промисах для этого подоходит идеально.</p> +</div> + +<h3 id="Регистрация">Регистрация</h3> + +<p>Service worker сначала регистрируется с помощью метода {{domxref("ServiceWorkerContainer.register()")}}. В случае успешной регистрации, service worker будет загружен клиентом и попытается установиться/активироваться (см. ниже) для всех URL, доступных пользователю, или только для указанного вами подмножества.</p> + +<h3 id="Загрузка_установка_и_активация">Загрузка, установка и активация</h3> + +<p>Service Worker будет следовать следующему жизненному циклу:</p> + +<ol> + <li>Загрузка</li> + <li>Установка</li> + <li>Активация</li> +</ol> + +<p>Когда пользователь впервые запросит доступ к сайту/странице, контролируемой Service Worker'ом, тот моментально будет загружен .</p> + +<p>После этого он будет загружаться каждые 24 часа или около того. Он <em>может</em> загружаться и чаще, но он <strong>должен</strong> загружаться как минимум каждые 24 часа, чтобы предотвратить использование старой версии кода клиентом.</p> + +<p>Установка производится в случае если загружаемый файл признается новым — либо отличным от уже установленного service worker (определяется через побайтовое сравнение), либо первым устанавливаемым service worker'ом для этой страницы/сайта.</p> + +<p>Если это первый раз, когда service worker оказался доступен, будет проведена установка, а после успешного ее завершения — активация.</p> + +<p>Если service worker уже существует, новая версия устанавливается в фоновом режиме, но не активируется — worker переходит в состояние <em>в ожидании</em>. Новая версия активируется только тогда, когда больше не останется загруженных страниц, использующих старый service worker. Как только это случится, новый service worker активируется (станет <em>активным worker'ом</em>). Активация может произойти раньше при использовании {{domxref ("ServiceWorkerGlobalScope.skipWaiting()")}}, а существующие страницы могут быть переведены под контроль активного воркера с помощью {{domxref ("Clients.claim()")}}.</p> + +<p>Вы можете подписаться на {{domxref("InstallEvent")}}; для того чтобы подготовить ваш service worker к использованию, к примеру, чтобы создать кеш при помощи встроенного API хранилища и разместить внутри него данные, которые вам необходимы в вашем приложении для работы офлайн.</p> + +<p>Есть также событие <code>activate</code>. Момент, когда это событие наступает, является удачным для очистки старого кеша и всего, что ассоциировалось с предыдущей версией вашего service worker'а.</p> + +<p>Service worker может отвечать на запросы, используя событие {{domxref("FetchEvent")}}. Вы можете изменять ответ на эти запросы на свое усмотрение используя метод {{domxref("FetchEvent.respondWith") }}.</p> + +<div class="note"> +<p><strong>Заметка</strong>: Так как выполнение <code>oninstall</code>/<code>onactivate</code> может занять время, спецификация service worker предоставляет метод <code>waitUntil</code>, который возвращает промис, когда вызывается <code>oninstall</code> или <code>onactivate</code>. Функциональные события не отправляются service worker, пока промис не завершится успешно.</p> +</div> + +<p>Для полного руководства по созданию рабочего примера читайте <a href="/en-US/docs/Web/API/ServiceWorker_API/Using_Service_Workers">Использование Service Worker</a>.</p> + +<h2 id="Другие_варианты_использования">Другие варианты использования</h2> + +<p>Service worker'ы также предназначены для таких вещей, как:</p> + +<ul> + <li>Фоновая синхронизация данных</li> + <li>Ответ на запросы от других источников</li> + <li>Получение централизованного обновления для данных использующих тяжелые вычисления, таких как геолокация или гироскоп, для того чтобы несколько станиц могли использовать одни и те же данные</li> + <li>Компиляция и управление зависимостями на клиентской стороне для CoffeeScript, less, CJS/AMD модулей и т.д. для целей разработки</li> + <li>Подписка на фоновые сервисы</li> + <li>Кастомная шаблонизация, основанная на определённых паттернах URL</li> + <li>Улучшение производительности, с помощью предварительной загрузки ресурсов, которые понадобятся пользователю в ближайшем будущем, например несколько последующих картинок в фотоальбоме.</li> +</ul> + +<p>В будущем service worker'ы будут способны на многие другие полезные вещи для веб-платформ, приближая их к нативным приложеням. Примечательно, что другие спецификации могут и будут использовать контекст service worker, к примеру для:</p> + +<ul> + <li><a href="https://github.com/slightlyoff/BackgroundSync">Фоновой синхронизации</a>: запускать service worker даже когда ни одного пользователя нет на сайте, чтобы обновить кеш.</li> + <li><a href="/en-US/docs/Web/API/Push_API">Реакции на пуш-сообщения</a>: запускать service worker для отправки сообщений пользователям, чтобы оповестить их о новом доступном контенте.</li> + <li>Реакции на определенное время и дату</li> + <li>Введение гео-ограничений</li> +</ul> + +<h2 id="Интерфейс">Интерфейс</h2> + +<dl> + <dt>{{domxref("Cache") }}</dt> + <dd>Представляет хранилище для объектов {{domxref("Request")}} / {{domxref("Response")}}, которые кешируются, как часть жизненного цикла {{domxref("ServiceWorker")}}.</dd> + <dt>{{domxref("CacheStorage") }}</dt> + <dd>Представляет хранилище для объектов {{domxref("Cache")}}. Он создает главную директорию для всех именовынных кешей, к которым {{domxref("ServiceWorker")}} имеет доступ, и поддерживает отображение строковых имен соответствующего объекта {{domxref("Cache")}}.</dd> + <dt>{{domxref("Client") }}</dt> + <dd>Представляет область видимости клиента service worker. Это либо документ в контексте браузера, либо {{domxref("SharedWorker")}}, который контролируется активным worker'ом.</dd> + <dt>{{domxref("Clients") }}</dt> + <dd>Представлет контейнер для списка объектов {{domxref("Client")}}; основной способ получить доступ к клиентам активного service worker'а текущего источника.</dd> + <dt>{{domxref("ExtendableEvent") }}</dt> + <dd>Расширяет жизненный цикл событий <code>install</code> и <code>activate</code>, отправляемых {{domxref("ServiceWorkerGlobalScope")}} как часть жизненного цикла service worker'а. Это гарантирует, что любое функциональное событие (как {{domxref("FetchEvent")}}) не отправится в {{domxref("ServiceWorker")}}, пока он не обновит шаблон данных, удалив устаревшие данные кеша.</dd> + <dt>{{domxref("ExtendableMessageEvent") }}</dt> + <dd>Обект событий {{event("message_(ServiceWorker)","message")}} запускается в service worker (когда канал сообщений в {{domxref("ServiceWorkerGlobalScope")}} получил новое сообщение из другого контекста) — расширяет жизненный цикл таких событий.</dd> + <dt>{{domxref("FetchEvent") }}</dt> + <dd>Параметр, передающийся в обработчик {{domxref("ServiceWorkerGlobalScope.onfetch")}}, <code>FetchEvent</code> представляет собой событие получения, которое отправляется в {{domxref("ServiceWorkerGlobalScope")}} {{domxref("ServiceWorker")}}. Он содержит информацию о запросе и результирующем ответе и обеспечивает {{domxref("FetchEvent.respondWith", "FetchEvent.respondWith()")}} метод, который позволяет отправить произвольный ответ обратно контролируемой странице.</dd> + <dt>{{domxref("InstallEvent") }}</dt> + <dd>Параметр, передющийся в {{domxref("ServiceWorkerGlobalScope.oninstall", "oninstall")}} обработчик, <code>InstallEvent</code> представляет сообой событие установки, которое отправляется {{domxref("ServiceWorkerGlobalScope")}} {{domxref("ServiceWorker")}}. Как наследник {{domxref("ExtendableEvent")}}, он гарантирует, что функциональные события, такие как {{domxref("FetchEvent")}}, не будут отправлены во время установки. </dd> + <dt>{{domxref("Navigator.serviceWorker") }}</dt> + <dd>Возвращает объект {{domxref("ServiceWorkerContainer")}}, который обеспечивает доступ к регистрации, удалению, обновлению и коммуникации с объектами {{domxref("ServiceWorker")}}<a href="https://html.spec.whatwg.org/multipage/browsers.html#concept-document-window">ассоциируемого документа</a>.</dd> + <dt>{{domxref("NotificationEvent") }}</dt> + <dd>Параметр, передаваемый в обработчик {{domxref("ServiceWorkerGlobalScope.onnotificationclick", "onnotificationclick")}}, интерфейс <code>NotificationEvent</code> представляет событие уведомления на клик, которое отправлется в {{domxref("ServiceWorkerGlobalScope")}} service worker'а.</dd> + <dt>{{domxref("ServiceWorker") }}</dt> + <dd>Представляет service worker. Несколько контекстов браузера (страницы, worker'ы, и т.д.) могут быть ассоциированы с одним объектом <code>ServiceWorker</code>.</dd> + <dt>{{domxref("ServiceWorkerContainer") }}</dt> + <dd>Предоставляет объект, описывающий service worker как общий блок в экосистеме сети, включая возможность регистрировать, отключать и обновлять service worker'ы, и предоставляет доступ к состоянию текущего и других зарагестрованных service worker'ов.</dd> + <dt>{{domxref("ServiceWorkerGlobalScope") }}</dt> + <dd>Представляет глобальный контекст исполнения service worker'а.</dd> + <dt>{{domxref("ServiceWorkerMessageEvent")}}</dt> + <dd>Содержит информацию о событии, отправленном целевому {{domxref("ServiceWorkerContainer")}}. </dd> + <dt>{{domxref("ServiceWorkerRegistration") }}</dt> + <dd>Представляет регистрацию service worker'а.</dd> + <dt>{{domxref("SyncEvent")}} {{non-standard_inline}}</dt> + <dd> + <p>SyncEvent предсталяет синхронное действие, котрое отправляется {{domxref("ServiceWorkerGlobalScope")}} ServiceWorker. </p> + </dd> + <dt>{{domxref("SyncManager")}} {{non-standard_inline}}</dt> + <dd>Обеспечивает интерфейс регистрации и перечисления синхронных рагистраций.</dd> + <dt>{{domxref("WindowClient") }}</dt> + <dd>Предствляет область видимости клинетского service worker'а, представленного в виде документа в контекте браузера, контролируемого активным worker'ом. Это особый тип объекта {{domxref("Client")}} с некоторыми дополнительными методами и свойствами.</dd> +</dl> + +<h2 id="Спецификации_характеристики">Спецификации (характеристики)</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Спецификации</th> + <th scope="col"> Статус</th> + <th scope="col">Комментарий</th> + </tr> + <tr> + <td>{{SpecName('Service Workers')}}</td> + <td>{{Spec2('Service Workers')}}</td> + <td>Изначальное определение.</td> + </tr> + </tbody> +</table> + +<h2 id="Совместимость">Совместимость</h2> + +<h4 id="Таблица_совместимости"><strong>Таблица совместимости</strong></h4> + +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Свойства</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Internet Explorer</th> + <th>Opera</th> + <th>Safari (WebKit)</th> + </tr> + <tr> + <td>Базовая поддержка</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoDesktop("44.0") }}<sup>[1]</sup></td> + <td>{{ CompatNo() }}</td> + <td>24</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>События установки/активации</td> + <td>{{ CompatChrome(40.0) }}</td> + <td>{{ CompatGeckoDesktop("44.0") }}<sup>[1]</sup></td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>Событие fetch/request/<br> + <code>respondWith()</code></td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoDesktop("44.0") }}<sup>[1]</sup></td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>Кеш</td> + <td> + <p class="p1">{{CompatChrome(42.0)}}</p> + </td> + <td>{{ CompatGeckoDesktop("39.0") }}<sup>[1]</sup></td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Chrome for Android</th> + <th>Firefox Mobile (Gecko)</th> + <th>Firefox OS</th> + <th>IE Phone</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + </tr> + <tr> + <td>Базовая поддержка</td> + <td></td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("44.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>События установки/активации</td> + <td>{{ CompatNo() }}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("44.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>Событие fetch/request/<br> + <code>respondWith()</code></td> + <td>{{ CompatNo() }}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("44.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>Кеш</td> + <td>{{ CompatNo() }}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("39.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + </tbody> +</table> +</div> + +<p>[1] Service workers (и <a href="/en-US/docs/Web/API/Push_API">Push</a>) были отключены в <a href="https://www.mozilla.org/en-US/firefox/organizations/">Firefox 45 Extended Support Release</a> (ESR.)</p> + +<h2 id="Смотрите_также">Смотрите также:</h2> + +<ul> + <li>Кулинарная книга <a href="https://serviceworke.rs">ServiceWorker </a></li> + <li>Использование<a href="/en-US/docs/Web/API/ServiceWorker_API/Using_Service_Workers"> Service Workers</a></li> + <li>Основные примеры кода <a href="https://github.com/mdn/sw-test">Service workers</a></li> + <li>Готов ли <a href="https://jakearchibald.github.io/isserviceworkerready/">ServiceWorker?</a></li> + <li>Перспектива</li> + <li><a href="/en-US/docs/Web/Guide/Performance/Using_web_workers">Using web workers</a></li> +</ul> diff --git a/files/ru/web/api/service_worker_api/using_service_workers/index.html b/files/ru/web/api/service_worker_api/using_service_workers/index.html new file mode 100644 index 0000000000..4eaa4dfc01 --- /dev/null +++ b/files/ru/web/api/service_worker_api/using_service_workers/index.html @@ -0,0 +1,524 @@ +--- +title: Использование Service Worker +slug: Web/API/Service_Worker_API/Using_Service_Workers +tags: + - основы ServiceWorker Workers руководство +translation_of: Web/API/Service_Worker_API/Using_Service_Workers +--- +<p>{{ServiceWorkerSidebar}}</p> + +<div class="summary"> +<p>В данной статье содержится информация о начале работы с сервис-воркерами, включая базовую архитектуру, процесс регистрации, а также установку и активацию новых сервис-воркеров, обновление существующих сервис-воркеров, управление кешем и настраиваемые ответы и все это в контексте простого приложения с offline-функциональностью.</p> +</div> + +<h2 id="Предпосылки_появления_Service_Workers">Предпосылки появления Service Workers</h2> + +<p>Одной из важнейших проблем, от которой страдали пользователи веб-приложений, была работа в условиях потери связи. Лучшее в мире веб-приложение оставит ужасное впечатление от использования, если вы не сможете его загрузить. Предпринималось много попыток создания технологий, которые бы решили эту проблему, и если верить страницам нашего <a href="/en-US/Apps/Build/Offline">Форума</a>, некоторые из вопросов были решены. Но все же наиважнейшей проблемой по-прежнему является отсутствие хорошего механизма для управления кешем ресурсов и настраиваемыми сетевыми запросами.<br> + <br> + Предыдущей попыткой была технология AppCache, казавшаяся хорошей идеей, потому как позволяла действительно просто указывать ресурсы для кеширования. Однако, эта технология допускает много предположений о том, что вы пытаетесь сделать, и затем с грохотом ломается, когда ваше приложение работает не в точности с этими допущениями. Чтобы получить больше информации по этой теме, прочитайте (неудачно названную, но хорошо написанную) статью Джейка Арчибальда <a href="http://alistapart.com/article/application-cache-is-a-douchebag">Application Cache is a Douchebag</a>.</p> + +<div class="note"> +<p><strong>На заметку</strong>: Начиная с Firefox 44, когда используется <a href="/ru/docs/Web/HTML/%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BA%D1%8D%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9">AppCache</a> для предоставления странице поддержки offline-режима, в консоли разработчика отображается предупреждение о том, что нужно использовать технологию <a href="/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service Workers</a> ({{bug("1204581")}}.)</p> +</div> + +<p>Технология Service Workers должна в итоге решить озвученные выше вопросы. Синтаксис Service Worker более сложен, чем тот же AppCache, но взамен вы можете посредством JavaScript контролировать AppCache-подразумеваемое поведение с высокой степенью детализации, что позволяет вам решить описанную проблему и многие другие. Используя Service Worker, вы можете без труда получить приложение, использующее в первую очередь кешированные ресурсы, предоставляя тем самым поведение по умолчанию в автономном режиме, до того как будет получено по сети больше данных (такой подход называется <a href="http://offlinefirst.org/">Offline First</a>). Так обычно работают нативные приложения, что часто является причиной выбора пользователя в их пользу.</p> + +<h2 id="Что_нужно_настроить_чтобы_поиграть_с_Service_Worker">Что нужно настроить, чтобы поиграть с Service Worker</h2> + +<p>Многие функции Service Worker теперь включены по умолчанию в новых браузерах, поддерживающих эту технологию. Однако, если вы обнаружите, что демонстрационный код не работает в вашей версии браузера, вам может понадобиться их включить:</p> + +<ul> + <li><strong>Firefox Nightly</strong>: Перейдите в раздел <code>about:config</code> и установите параметр <code>dom.serviceWorkers.enabled</code> в значение true; затем перезапустите браузер.</li> + <li><strong>Chrome Canary</strong>: Перейдите в раздел <code>chrome://flags</code> и включите <code>experimental-web-platform-features</code>; перезапустите браузер (заметьте, что некоторые функции теперь включены по умолчанию в браузере Chrome.)</li> + <li><strong>Opera</strong>: Перейдите в раздел <code>opera://flags</code> и включите <code>Support for ServiceWorker</code>; перезапустите браузер.</li> + <li><strong>Microsoft Edge</strong>: Перейдите в раздел <code>about:flags</code> и поставьте галочку <code>Enable service workers</code>; перезапустите браузер.</li> +</ul> + +<p>Также вам необходимо предоставлять ваш код по протоколу HTTPS — Service Worker требует этого по соображениям безопасности. По этой причине GitHub — хороший выбор для экспериментов, поскольку он поддерживает протокол HTTPS по умолчанию. Для облегчения локальной разработки браузеры считают <code>localhost</code> также безопасным origin.</p> + +<h2 id="Базовая_архитектура">Базовая архитектура</h2> + +<p>Чтобы сделать базовую настройку Service Worker, как правило, нужно пройти следующие шаги:</p> + +<ol> + <li>URL сервис-воркера опрашивается и регистрируется посредством вызова метода {{domxref("ServiceWorkerContainer.register()")}}.</li> + <li>Если регистрация прошла успешно, то сервис-воркер начинает работать внутри {{domxref("ServiceWorkerGlobalScope") }}; это, по сути, особый вид контекста воркера, работающий вне главного потока браузера, без доступа к DOM.</li> + <li>Теперь сервис-воркер может обрабатывать события.</li> + <li>Установка сервис-воркера начинается после того, как все контролируемые им страницы закешированы и доступны для последующего использования. Событие <code>install</code> всегда посылается первым воркеру (оно может быть использовано для запуска начальной загрузки данных в IndexedDB, для кеширования ресурсов). Данный этап сродни процедуре установки нативного или FirefoxOS-приложения — все делается доступным для использования в оффлайн-режиме.</li> + <li>Как только обработчик события <code>oninstall</code> завершит свою работу, сервис-воркер считается установленным.</li> + <li>Далее следует активация. После того как воркер установлен, он получает событие <code>onactivate</code>, которое обычно используется для очистки ресурсов, задействованных в предыдущей версии скрипта сервис-воркера.</li> + <li>Сервис-воркер здесь может контролировать страницы, но только в случае, если те открыты после успешного вызова <code>register()</code>. То есть документ может начать жизнь с сервис-воркером или даже без него и продолжать нормально работать. Поэтому документы должны быть перезагружены, чтобы действительно быть подконтрольными сервис-воркеру.</li> +</ol> + +<p><img alt="" src="https://mdn.mozillademos.org/files/12636/sw-lifecycle.png" style="display: block; height: 867px; margin: 0px auto; width: 579px;"></p> + +<p>Следующий рисунок кратко показывает доступные события Service Worker:</p> + +<p><img alt="install, activate, message, fetch, sync, push" src="https://mdn.mozillademos.org/files/12632/sw-events.png" style="display: block; margin: 0px auto;"></p> + +<h3 id="Промисы_обещания">Промисы (обещания)</h3> + +<p><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Промисы</a> — отличный механизм для запуска асинхронных операций, которые могут успешно зависеть друг от друга. Промисы имеют большое значение в работе сервис-воркеров.</p> + +<p>Промисы могут делать много всего, но сейчас вам достаточно знать, что если что-то возвращает промис, то вы можете дописать вызов метода <code>.then()</code>, передав ему функцию на случай удачного выполнения, и вызов метода <code>.catch()</code>, если хотите передать функцию при неуспешном выполнении.</p> + +<p>Давайте сравним структуру традиционного синхронного обратного вызова с его промис-эквивалентом.</p> + +<h4 id="sync">sync</h4> + +<pre class="brush: js notranslate">try { + const value = myFunction(); + console.log(value); +} catch(err) { + console.log(err); +}</pre> + +<h4 id="async">async</h4> + +<pre class="brush: js notranslate">myFunction().then((value) => { + console.log(value); +}).catch((err) => { + console.log(err); +});</pre> + +<p>В первом примере код, идущий за вызовом функции <code>myFunction()</code>, будет ждать завершения вызова и возврата значения. Во втором примере <code>myFunction()</code> возвращает промис для <code>value</code>, в этом случае, последующий код сможет выполняться, не дожидаясь завершения основной работы функции. Когда промис разрешится, код, переданный методу <code>then</code>, будет выполнен асинхронно.</p> + +<p>А вот вам реальный пример: что, если мы хотим загружать изображения динамически, к тому же мы желаем удостовериться, что изображения загрузились до того, как они будут показаны? То, что мы хотим сделать, является стандартной задачей, но она все же может доставить головной боли. Мы можем использовать <code>.onload</code>, чтобы показать изображение только после загрузки, но что делать с событиями, которые могут произойти до того, как мы начнем их слушать? Мы могли бы использовать <code>.complete</code>, но оно все еще ненадежно, да и что делать с повторяющимися изображениями? И наконец все это работает синхронно, блокируя главный поток.</p> + +<p>Вместо этого мы можем написать собственный промис для работы с подобными случаями. (Вы можете найти исходный код в нашем примере <a href="https://github.com/mdn/promises-test">Promises test</a> или взглянуть на <a href="https://mdn.github.io/promises-test/">живое демо</a>.)</p> + +<p>{{note("Реальные реализации сервис-воркеров скорее всего будут использовать <code>onfetch</code>, а не устаревающий XMLHttpRequest API. Эти возможности не используются здесь, так что можете сосредоточиться на изучении промисов.")}}</p> + +<pre class="brush: js notranslate">const imgLoad = (url) => { + return new Promise((resolve, reject) => { + var request = new XMLHttpRequest(); + request.open('GET', url); + request.responseType = 'blob'; + + request.onload = () => { + if (request.status == 200) { + resolve(request.response); + } else { + reject(Error('Image didn\'t load successfully; error code:' + request.statusText)); + } + }; + + request.onerror = () => { + reject(Error('There was a network error.')); + }; + + request.send(); + }); +}</pre> + +<p>Мы возвращаем новый промис, созданный конструктором <code>Promise()</code>, который в качестве аргумента принимает функцию с параметрами <code>resolve</code> и <code>reject</code>. Где-то внутри функции мы должны определить случаи, при которых промис должен быть разрешен или отклонен, — в нашем случае, в зависимости от того, вернулся ли статус 200 ОК или нет, будут вызваны <code>resolve</code> в случае успеха или <code>reject</code> при неудаче. Последующее содержимое этой функции — вполне стандартное XHR-наполнение, поэтому на данный момент не стоит о нем волноваться.</p> + +<p>Вызывая функцию <code>imgLoad()</code>, мы ожидаемо передаем в качестве параметра url изображения, которое хотим загрузить, но далее код немного отличается:</p> + +<pre class="brush: js notranslate">let body = document.querySelector('body'); +let myImage = new Image(); + +imgLoad('myLittleVader.jpg').then((response) => { + var imageURL = window.URL.createObjectURL(response); + myImage.src = imageURL; + body.appendChild(myImage); +}, (Error) => { + console.log(Error); +});</pre> + +<p>После вызова функции мы "цепляем" к ней вызов промис-метода <code>then()</code>, которому в качестве параметров передаем две функции - первая будет вызвана в случае выполнения промиса, созданного вызовом функции <code>imgLoad()</code>, вторая функция будет вызвана в случае отклонения этого промиса. В случае выполнения мы показываем изображение в элементе <code>myImage</code>, который прикрепляем к body (аргументом является <code>request.response</code>, помещенный в промис-методе <code>resolve</code>); в случае отклонения промиса в консоли будет отображено сообщение об ошибке.</p> + +<p>Все это происходит асинхронно.</p> + +<div class="note"> +<p><strong>На заметку</strong>: Вы можете также объединять вызов нескольких промис-методов в одну цепочку, как в этом примере:<br> + <code>myPromise().then(success, failure).then(success).catch(failure);</code></p> +</div> + +<div class="note"> +<p><strong>На заметку</strong>: Вы можете получить гораздо больше информации о промисах, прочитав превосходную статью Джейка Арчибальда (Jake Archibald’s) <a href="http://www.html5rocks.com/en/tutorials/es6/promises/">JavaScript Promises: there and back again</a>.</p> +</div> + +<h2 id="Демонстрация_Service_Workers">Демонстрация Service Workers</h2> + +<p>Чтобы продемонстрировать только базовые моменты регистрации и установки сервис-воркеров, мы создали простое демо-приложение, названое <a href="https://github.com/mdn/sw-test">sw-test</a>. Это простая галерея изображений "Star wars Lego". Оно использует промис-функции, чтобы прочитать из JSON-объекта и загрузить, используя технологию Ajax, изображения, находящиеся далее нижнего края страницы, до того как они будут показаны. В приложении также еще регистрируется, устанавливается и активируется сервис-воркер, и, в случае если браузер поддерживает спецификацию Service Worker, запрашиваемые ресурсы будут закешированы, и приложение будет работать в offline-режиме!</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/8243/demo-screenshot.png" style="display: block; height: 410px; margin: 0px auto; width: 480px;"><br> + <br> + Вы можете посмотреть <a href="https://github.com/mdn/sw-test/">исходный код на GitHub</a>, а также <a href="https://mdn.github.io/sw-test/">этот живой пример</a>. Единственное, что мы тут рассмотрим, это промис (смотрите <a href="https://github.com/mdn/sw-test/blob/gh-pages/app.js#L22-L47">app.js строки 22-47</a>), модифицированная версия того, о котором вы читали выше в разделе <a href="https://github.com/mdn/promises-test">Тестовая демонстрация промисов</a>. Разница в следующем:</p> + +<ol> + <li>Ранее мы передавали параметром лишь URL изображения, которое мы хотели загрузить. Теперь же, мы передаем JSON-фрагмент, содержащий все данные для изображения (посмотрите, как это выглядит в <a href="https://github.com/mdn/sw-test/blob/gh-pages/image-list.js">image-list.js</a>). Это сделано потому, что все данные для выполнения каждого промиса должны быть переданы ему, так как он выполняется асинхронно. Если же вы передали лишь URL, а чуть позже попытались получить доступ к другим атрибутам в JSON-фрагменте внутри цикла <code>for()</code>, это бы не сработало, так как этот промис не был бы выполнен во время текущей итерации цикла (это синхронный процесс).</li> + <li>Теперь мы выполняем промис с параметром-массивом, так как дальше мы хотим сделать загруженные данные изображения доступными для разрешающей функции, одновременно с именем файла, данными авторства и замещающим текстом (см. <a href="https://github.com/mdn/sw-test/blob/gh-pages/app.js#L31-L34">app.js строки 31-34</a>). Промисы будут выполняться со всего одним аргументом, поэтому, если вы хотите выполнить их с несколькими параметрами, вы должны использовать массив/объект.</li> + <li>Затем, чтобы получить доступ к выполненным значениям промисов, мы обращаемся к ним так, как было задумано (смотрите <a href="https://github.com/mdn/sw-test/blob/gh-pages/app.js#L60-L64">app.js строки 60-64</a>). По началу это может выглядеть немного странно, но именно так и работают промисы.</li> +</ol> + +<h2 id="Погружение_в_Service_Worker">Погружение в Service Worker</h2> + +<p>Итак, переходим к Service Worker!</p> + +<h3 id="Регистрация_воркеров">Регистрация воркеров</h3> + +<p>Ниже представлен первый блок кода файла app.js. Это точка входа в Service Worker.</p> + +<pre class="brush: js notranslate">if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('./sw-test/sw.js', {scope: './sw-test/'}) + .then((reg) => { + // регистрация сработала + console.log('Registration succeeded. Scope is ' + reg.scope); + }).catch((error) => { + // регистрация прошла неудачно + console.log('Registration failed with ' + error); + }); +}</pre> + +<ol> + <li>Внешний условный блок выполняет проверку на поддержку Service Worker, чтобы убедиться что технология доступна, до того как начать регистрацию.</li> + <li>Далее, чтобы зарегистрировать сервис-воркера для этого сайта, мы используем функцию {{domxref("ServiceWorkerContainer.register()") }}. Сервис-воркер представляет собой JavaScript-файл приложения (обратите внимание, что URL указывается относительно "корня", а не места расположения JS-файла, регистрирующего сервис-воркер).</li> + <li>Параметр scope - не обязателен, он может быть использован для указания подмножества контента, которое вы хотите отдать под контроль сервис-воркера. В нашем случае, мы указали <code>'./sw-test/'</code>. Если вы не укажете его, то будет использовано значение по умолчанию; мы же указали его только в целях иллюстрации.</li> + <li>Метод <code>.then()</code> был использован для обработки успешной регистрации. Если промис разрешится успешно, то код, переданный этому методу, будет выполнен.</li> + <li>Ну и наконец, в конец нашего промиса мы добавляем функцию <code>.catch()</code>, которая будет выполнена в случае, если промис будет отклонен.</li> +</ol> + +<p>Предыдущий код регистрирует сервис-воркера, который работает в worker-контексте, и следовательно, не имеет доступа к DOM. Затем вы запускате код в сервис-воркере, вне ваших страниц, чтобы контролировать их загрузку.</p> + +<p>Один сервис-воркер может контролировать несколько страниц. Каждый раз, когда загружается страница, находящаяся в пределах области видимости, сервис-воркер будет установлен на ней и начнет работу. Поэтому будьте осторожны с применением глобальных переменных в скриптах сервис-воркеров, потому как у каждой страницы нет своего уникального экземпляра сервис-воркера.</p> + +<div class="note"> +<p><strong>На заметку</strong>: Сервис-воркеры функционально похожи на прокси-серверы, они позволяют модифицировать запросы и ответы, замещая записями из собственного кеша, и многое другое.</p> +</div> + +<div class="note"> +<p><strong>На заметку</strong>: Есть одна очень хорошая особенность при работе с сервис-воркерами. В случае, если вы используете функционал проверки поддержки Service Worker, то приложение в браузерах, не имеющих поддержки, продолжат нормально работать с ожидаемым поведением. Кроме того, если браузер поддерживает только AppCache, то будет использована эта технология. В случае, если браузер поддерживает и AppCache и Service Worker, то будет использована Service Worker.</p> +</div> + +<h4 id="Почему_мой_сервис-воркер_не_прошел_регистрацию">Почему мой сервис-воркер не прошел регистрацию?</h4> + +<p>Это может произойти по следующим причинам:</p> + +<ol> + <li>Приложение загружено не по протоколу HTTPS.</li> + <li>Путь к сервис-воркеру указан некорректно — он должен быть написан относительно origin запроса, а не вашей корневой директории с приложением. В нашем примере воркер расположен в <code>https://mdn.github.io/sw-test/sw.js</code>, корневая папка — <code>https://mdn.github.io/sw-test/</code>. Но в качестве пути к сервис-воркеру нужно указывать <code>/sw-test/sw.js</code>, а не <code>/sw.js</code>.</li> + <li>Origin сервис-воркера отличается от origin вашего приложения. Это также запрещено.</li> +</ol> + +<p><img alt="" src="https://mdn.mozillademos.org/files/12630/important-notes.png" style="display: block; height: 277px; margin: 0px auto; width: 574px;"></p> + +<p>Также обратите внимание:</p> + +<ul> + <li>В сервис-воркер будут попадать только те запросы, которые соответствуют его scope.</li> + <li>Максимальная видимость scope сервис-воркера равна его location.</li> + <li>Если ваш сервис-воркер работает на клиенте, которому был передан заголовок <code>Service-Worker-Allowed</code>, вы можете указать список максимальных scope'ов для этих воркеров.</li> +</ul> + +<h3 id="Установка_и_активация_заполнение_кеша">Установка и активация: заполнение кеша</h3> + +<p>После того как ваш сервис-воркер будет зарегистрирован, браузер может попробовать установить его и активировать на странице/сайте.</p> + +<p>Событие install возникает после того как установка успешно завершится. Это событие используется главным образом для того, чтобы заполнить кеш браузера ресурсами, необходимыми для успешного запуска в offline-режиме. Для этого используется новый API хранилища Service Worker — {{domxref("cache")}} — глобальный для всех сервис-воркеров, который позволяет нам хранить результаты запросов, используя в качестве ключа для их получения сами запросы. Этот API работает аналогично стандартному кешу браузера, но только для вашего домена. Данные в кеше сохраняются до тех пор, пока вы сами не решите их удалить — вы имеете полный контроль.</p> + +<div class="note"> +<p><strong>На заметку</strong>: Cache API поддерживается не всеми браузерами (смотрите раздел {{anch("Browser support")}} чтобы получить больше информации). Если вы хотите сейчас использовать эту технологию, то можете рассмотреть возможность использования полифила, который доступен в <a href="https://github.com/Polymer/topeka/blob/master/sw.js">Google's Topeka demo</a>, или можете хранить ресурсы в <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a>.</p> +</div> + +<p>Давайте начнем этот раздел посмотрев на фрагмент кода ниже — это <a href="https://github.com/mdn/sw-test/blob/gh-pages/sw.js#L1-L17">первый блок кода, который вы увидите в нашем сервис-воркере</a>:</p> + +<pre class="brush: js notranslate">self.addEventListener('install', (event) => { + event.waitUntil( + caches.open('v1').then((cache) => { + return cache.addAll([ + './sw-test/', + './sw-test/index.html', + './sw-test/style.css', + './sw-test/app.js', + './sw-test/image-list.js', + './sw-test/star-wars-logo.jpg', + './sw-test/gallery/', + './sw-test/gallery/bountyHunters.jpg', + './sw-test/gallery/myLittleVader.jpg', + './sw-test/gallery/snowTroopers.jpg' + ]); + }) + ); +});</pre> + +<ol> + <li>Здесь мы добавляем обработчик события <code>install</code> к сервис-воркеру (отныне <code>self</code>), и затем вызываем метод {{domxref("ExtendableEvent.waitUntil()") }} объекта события. Такая конструкция гарантирует, что сервис-воркер не будет установлен, пока код, переданный внутри <code>waitUntil()</code>, не завершится с успехом.</li> + <li><span style="line-height: 1.5;">Внутри <code>waitUntil()</code> мы используем метод </span><a href="/en-US/docs/Web/API/CacheStorage/open">caches.open()</a>, чтобы создать новый кеш, который назовём <code>v1</code>, это будет первая версия кеша ресурсов. Этот метод возвращает промис для созданного кеша; когда он выполнится, у объекта созданного кеша мы вызовем метод <code>addAll()</code>, который в качестве параметра ожидает получить массив origin-относительных URL всех ресурсов, которые мы хотим хранить в кеше.</li> + <li>Если промис будет отклонен, то установка будет завершена неудачно, и воркер ничего не сделает. Это хорошо, потому как вы можете исправить свой код и затем попробовать провести регистрацию в следующий раз.</li> + <li>После успешной установки сервис-воркер активируется. Этот момент не очень важен при первоначальной установке/активации сервис-воркера, в то же время он имеет большое значение, когда происходит обновление воркера (смотрите раздел {{anch("Обновление вашего сервис-воркера")}}, находящийся ниже).</li> +</ol> + +<div class="note"> +<p><span style="font-size: 14px; line-height: 21px;"><strong>На заметку</strong></span>: <a href="/en-US/docs/Web/Guide/API/DOM/Storage">localStorage</a> работает схожим образом, но в синхронном режиме, поэтому запрещен в сервис-воркерах.</p> +</div> + +<div class="note"> +<p><span style="font-size: 14px; line-height: 21px;"><strong>На заметку</strong></span>: При необходимости хранить данные в сервис-воркерах вы можете использовать <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a>.</p> +</div> + +<h3 id="Настраиваемые_ответы_на_запросы">Настраиваемые ответы на запросы</h3> + +<p>Теперь ресурсы вашего сайта находятся в кеше и вам необходимо указать сервис-воркеру, что делать с этим контентом. Это легко сделать, обработав событие <code>fetch</code>.</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/12634/sw-fetch.png" style="display: block; margin: 0 auto;"></p> + +<p>Событие <code>fetch</code> возникает каждый раз, когда запрашиваются любые подконтрольные сервис-воркеру ресурсы, к которым относятся документы из области видимости и другие ресурсы, связанные с этими документами (например, если в index.html происходит кросс-доменный запрос для загрузки изображения, то он тоже попадет в сервис-воркер).</p> + +<p>Вы можете подключить к сервис-воркеру обработчик события <code>fetch</code> и внутри него на объекте события вызвать метод <code>respondWith()</code>, чтобы заменить ответы и показать собственную "магию".</p> + +<pre class="brush: js notranslate">self.addEventListener('fetch', (event) => { + event.respondWith( + // магия происходит здесь + ); +}); +</pre> + +<p>Для начала, на каждый сетевой запрос мы можем отдать в ответ ресурс, чей url соответствует запросу:</p> + +<pre class="brush: js notranslate">self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request) + ); +});</pre> + +<p><code>caches.match(event.request)</code> позволяет нам проверять сетевой запрос ресурса на соответствие какому-либо доступному в кеше ресурсу, если такой ресурс имеется. Соответствие проверяется по url и изменяемым заголовкам.</p> + +<p>Давайте рассмотрим несколько других вариантов реализации нашей магии (чтобы получить больше информации об интерфейсах {{domxref("Request")}} и {{domxref("Response")}} смотрите <a href="/en-US/docs/Web/API/Fetch_API">документацию к Fetch API</a>.)</p> + +<ol> + <li> + <p>Конструктор <code>{{domxref("Response.Response","Response()")}}</code> позволяет вам создавать собственные ответы. В данном случае, мы всего лишь возвращаем простую текстовую строку:</p> + + <pre class="brush: js notranslate">new Response('Hello from your friendly neighbourhood service worker!'); +</pre> + + <p>В этом более сложном объекте Response показано, как вы можете передать набор заголовков в свой ответ, эмулируя стандартный HTTP-ответ. Здесь мы просто сообщаем браузеру, чем является содержимое ответа:</p> + + <pre class="brush: js notranslate">new Response('<p>Hello from your friendly neighbourhood service worker!</p>', { + headers: { 'Content-Type': 'text/html' } +});</pre> + </li> + <li> + <p>Если совпадение не было найдено в кеше, вы можете попросить браузер {{domxref("GlobalFetch.fetch","загрузить")}} тот же ресурс, чтобы получить новый файл через обычную сеть, если она доступна:</p> + + <pre class="brush: js notranslate">fetch(event.request);</pre> + </li> + <li> + <p>Если информация, соответствующая запросу, в кеше не найдена, а также сеть не доступна, то вы можете просто ответить на запрос какой-либо страницей по умолчанию, которая хранится в кеше, используя {{domxref("CacheStorage.match","match()")}}:</p> + + <pre class="brush: js notranslate">caches.match('./fallback.html');</pre> + </li> + <li> + <p>Вы можете получить больше информации о каждом запросе, используя для этого свойства объекта {{domxref("Request")}}, который можно получить как свойство объекта {{domxref("FetchEvent")}}:</p> + + <pre class="brush: js notranslate">event.request.url +event.request.method +event.request.headers +event.request.body</pre> + </li> +</ol> + +<h2 id="Восстановление_неудачных_запросов">Восстановление неудачных запросов</h2> + +<p>Итак, <code>caches.match(event.request)</code> отработает как нужно только в том случае, если в кеше сервис-воркера будет найдено соответствие запросу. Но что произойдет, если такого соответствия не будет найдено? Если мы не предоставим никакого механизма обработки такой ситуации, то промис выполнится со значением <code>undefined</code> и мы не получим никакого значения.</p> + +<p>К счастью, сервис-воркеры имеют структуру основанную на промисах, что делает тривиальной такую обработку и предоставляет большое количество способов успешно обработать запрос:</p> + +<pre class="brush: js notranslate">self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request).then((response) => { + return response || fetch(event.request); + }) + ); +});</pre> + +<p>Если промис будет отклонен, функция <code>catch()</code> вернет обычный сетевой запрос к внешнему ресурсу. Это значит, что, если сеть доступна, то ресурс просто загрузится с сервера.</p> + +<p>Если же мы были достаточно умны, то мы не стали бы просто возвращать сетевой запрос, а сохранили бы его результат в кеше, чтобы иметь возможность получить его в offline-режиме. В случае с нашим демо-приложением "Star Wars gallery", это означает, что, если в галерею будет добавлено еще одно изображение, то оно будет получено и сохранено в кеше:</p> + +<pre class="brush: js notranslate">self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request).then((resp) => { + return resp || fetch(event.request).then((response) => { + return caches.open('v1').then((cache) => { + cache.put(event.request, response.clone()); + return response; + }); + }); + }) + ); +});</pre> + +<p>Здесь мы возвращаем обычный сетевой запрос, который возвращен вызовом <code>fetch(event.request);</code> этот запрос также является промисом. Когда промис разрешится, мы получим кеш вызвав <code>caches.open('v1');</code> этот метод также возвращает промис. Когда разрешится уже второй промис, будет использован вызов <code>cache.put()</code>, чтобы поместить ресурс в кеш. Ресурс получен через <code>event.request</code>, а ответ — через клонирование <code>response.clone()</code>. Клон помещается в кеш, а оригинальный ответ передается браузеру, который передает его странице, которая запросила ресурс.</p> + +<p>Почему? Потому что потоки запроса и ответа могут быть прочитаны только единожды. Чтобы ответ был получен браузером и сохранен в кеше, нам нужно клонировать его. Так оригинальный объект отправится браузеру, а клон будет закеширован. Оба они будут прочитаны единожды.</p> + +<p>У нас все ещё остается единственная проблема - если на какой-либо запрос в кеше не будет найдено соответствие, и в этот момент сеть не доступна, то наш запрос завершится неудачно. Давайте реализуем запасной вариант по умолчанию, при котором пользователь, в описанном случае, будет получать хоть что-нибудь:</p> + +<pre class="brush: js notranslate">self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request).then((resp) => { + return resp || fetch(event.request).then((response) => { + let responseClone = response.clone(); + caches.open('v1').then((cache) => { + cache.put(event.request, responseClone); + }); + + return response; + }); + }).catch(() => { + return caches.match('./sw-test/gallery/myLittleVader.jpg'); + }) + ); +});</pre> + +<p>Здесь мы решили обрабатывать только картинки, потому что единственные запросы, которые могут не удасться — это загрузка новых картинок, так как все остальное было закешировано во время обработки события <code>install</code>, которое мы обсуждали ранее.</p> + +<h2 id="Обновление_вашего_сервис-воркера"><a id="Updating your service worker" name="Updating your service worker">Обновление вашего сервис-воркера</a></h2> + +<p>Если после того, как ваш сервис-воркер был установлен, стала доступна его новая версия, при обновлении или загрузке страницы она будет установлена в фоновом режиме, но не будет активирована. Она будет активирована, лишь когда не останется ни одной страницы, использующей старую версию сервис-воркера.</p> + +<p>Вы можете обновить обработчик события install в новой версии сервис-воркера, чтобы получить примерно такое (обратите внимание на номер новой версии):</p> + +<pre class="brush: js notranslate">self.addEventListener('install', (event) => { + event.waitUntil( + caches.open('v2').then((cache) => { + return cache.addAll([ + './sw-test/', + './sw-test/index.html', + './sw-test/style.css', + './sw-test/app.js', + './sw-test/image-list.js', + + … + + // подключение прочих ресурсов для новой версии... + ]); + }) + ); +});</pre> + +<p>В то время, как этот код работает, предыдущая версия также доступна. Новая версия устанавливается в фоновом режиме. Мы можем работать с версией кеша v2, в то время как версия v1 не будет разрушена.</p> + +<p>Когда ни одна страница не будет использовать текущую версию, новый воркер активируется и станет ответственным за обработку всех запросов.</p> + +<h3 id="Удаление_старого_кеша">Удаление старого кеша</h3> + +<p>Вы можете обрабатывать событие activate. Оно обычно используется в случае, если нужно выполнить такие действия, которые бы нарушили работу воркеров предыдущей версии, если они все еще работают со старым кешем. Также это событие полезно использовать для того, чтобы удалить ненужные данные, чтобы освободить место, занимаемое на диске, потому что каждый браузер имеет жесткие ограничения на размер хранилища кеша, которое доступно для использования сервис-воркерами.</p> + +<p>Promise, переданный в <code>waitUntil()</code>, заблокирует другие события до своего завершения, поэтому можно быть уверенным, что процесс очистки закончится раньше, чем выполнится первое событие <code>fetch</code> на основе нового кеша.</p> + +<pre class="brush: js notranslate">self.addEventListener('activate', (event) => { + var cacheKeeplist = ['v2']; + + event.waitUntil( + caches.keys().then((keyList) => { + return Promise.all(keyList.map((key) => { + if (cacheKeeplist.indexOf(key) === -1) { + return caches.delete(key); + } + })); + }) + ); +});</pre> + +<h2 id="Инструменты_разработчика">Инструменты разработчика</h2> + +<p>В браузере Chrome есть раздел <code>chrome://inspect/#service-workers</code>, где можно увидеть активность сервис-воркеров и их хранилища. Также есть раздел <code>chrome://serviceworker-internals</code>, в котором можно получить более детальную информацию, запустить, остановить и отладить процессы сервис-воркеров. В будущем ожидается поддержка эмуляции различного качества сетевых соединений и их отсутствия, что будет весьма кстати.</p> + +<p>Firefox также начал реализовывать полезные инструменты для разработки сервис-воркеров:</p> + +<ul> + <li><span style="line-height: 1.5;">Вы можете пройти в раздел </span><a style="line-height: 1.5;">about:debugging</a><span style="line-height: 1.5;">, чтобы посмотреть зарегистрированные сервис-воркеры и обновить или удалить их.</span></li> + <li>Во время тестирования вы можете снять ограничение HTTPS, поставив флажок "Enable Service Workers over HTTP (when toolbox is open)" в <a href="/ru/docs/Tools/Settings">настройках Firefox Developer Tools</a>.</li> + <li>Кнопка "Забыть", включаемая в настройках «Персонализация» в Firefox, позволяет удалить все сервис-воркеры и очистить их кеши.</li> +</ul> + +<div class="blockIndicator note"> +<p><strong>На заметку</strong>: Вы можете держать своё приложение на <code>http://localhost</code> (например, используя <code>me@localhost:/my/app$ <strong>python -m SimpleHTTPServer</strong></code>) для локальной разработки. См. <a href="https://www.w3.org/TR/service-workers/#security-considerations">Security considerations</a></p> +</div> + +<h2 id="Спецификации">Спецификации</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Спецификация</th> + <th scope="col">Статус</th> + <th scope="col">Коментарии</th> + </tr> + <tr> + <td>{{SpecName('Service Workers', '')}}</td> + <td>{{Spec2('Service Workers')}}</td> + <td>Начальное определение.</td> + </tr> + </tbody> +</table> + +<h2 id="Совместимость_с_браузерами">Совместимость с браузерами</h2> + +<div>{{CompatibilityTable}}</div> + +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Internet Explorer</th> + <th>Opera</th> + <th>Safari (WebKit)</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoDesktop("33.0") }}<sup>[1]</sup></td> + <td>{{CompatNo}}</td> + <td>24</td> + <td>{{CompatNo}}</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Chrome for Android</th> + <th>Firefox Mobile (Gecko)</th> + <th>Firefox OS</th> + <th>IE Phone</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatNo}}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{CompatNo}}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{CompatNo}}</td> + </tr> + </tbody> +</table> +</div> + +<p>[1] Service workers (and <a href="/en-US/docs/Web/API/Push_API">Push</a>) были отключены в <a href="https://www.mozilla.org/en-US/firefox/organizations/">расширенной версии поддержке Firefox 45</a> (ESR.)</p> + +<h2 id="Смотрите_также">Смотрите также</h2> + +<ul> + <li><a href="https://serviceworke.rs/">The Service Worker Cookbook</a></li> + <li><a href="https://jakearchibald.github.io/isserviceworkerready/">Готов ли ServiceWorker для использования?</a></li> + <li>Скачать <a href="https://mdn.mozillademos.org/files/12638/sw101.png">Service Workers 101 cheatsheet</a>.</li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li> + <li><a href="/en-US/docs/Web/Guide/Performance/Using_web_workers">Using web workers</a></li> +</ul> |