От автора: несколько лет назад я писала о том, как мы можем использовать CSS для стилизации битых изображений. Техника основана на том факте, что любые стили псевдо-элементов ::before или ::after для элемента будут применяться только в том случае, если изображение не загружается. Таким образом, мы можем стилизовать эти элементы, и они будут отображаться, только если изображение было повреждено.
Обработка изображений, сделанная с помощью такого подхода, имеет свои плюсы и минусы. Одним из ограничений является поддержка браузерами, так как этот метод не работает в некоторых основных браузерах, таких как Safari.
Недавно, когда мне пришлось плотно работать с Service Worker, мне пришло в голову, что мы можем использовать их для обработки битых изображений другим способом. Так как Service Worker может определить, удалось ли получить файл изображения, в противном случае мы можем, например, предоставить браузеру другое изображение.
Перехват запросов на битые изображения
В событии Service Worker fetch мы можем определить, когда в запросе, выполненном браузером, что-то пошло не так, будь то из-за того, что пользователь был оффлайн или ответ на запрос был неверным.
self.addEventListener('fetch', (e) => { e.respondWith( fetch(e.request) .then((response) => { if (response.ok) return response; // Пользователь онлайн, но ответ не в порядке }) .catch((err) => { // Пользователь, вероятно, оффлайн }) ) });
В любом из этих сценариев мы можем проверить, имел ли место неудачный запрос на изображение, и сделать в ответ все, что нам нравится.
function isImage(fetchRequest) { return fetchRequest.method === "GET" && fetchRequest.destination === "image"; } self.addEventListener('fetch', (e) => { e.respondWith( fetch(e.request) .then((response) => { if (response.ok) return response; // Пользователь онлайн, но ответ не в порядке if (isImage(e.request)) { // делаем что-то } }) .catch((err) => { // Пользователь, вероятно, оффлайн if (isImage(e.request)) { // делаем что-то } }) ) });
Обслуживание изображения «битого изображения»
Одним из способов обработки запросов на изображения, которые не разрешаются, является отправка на его место изображения-заполнителя. Например, у нас может быть изображение, подобное приведенному ниже, чтобы показать пользователю, что изображение повреждено.
Мы можем осуществить это, отвечая на запрос fetch новым запросом на файл заполнителя, /broken.png.
self.addEventListener('fetch', (e) => { e.respondWith( fetch(e.request) .then((response) => { if (response.ok) return response; // Пользователь онлайн, но ответ не в порядке if (isImage(e.request)) { // Извлекаем вместо этого заполнитель битого изображения return fetch("/broken.png"); } }) .catch((err) => { // Пользователь, вероятно, оффлайн if (isImage(e.request)) { // делаем что-то } }) // конец fetch ) });
Это будет работать, когда пользователь находится в сети, но если мы хотим, чтобы это также работало в автономном режиме, нам нужно будет кэшировать изображение заполнителя. Обычно это делается на этапе жизненного цикла service worker install.
self.addEventListener('install', (e) => { self.skipWaiting(); e.waitUntil( caches.open("precache").then((cache) => { // Добавляем /broken.png в "precache" cache.add("/broken.png"); }) ); });
Когда изображение помещено в кэш, мы можем ответить на запрос fetch ответом из кеша, вместо того, чтобы выполнять новый запрос к сети.
self.addEventListener('fetch', (e) => { e.respondWith( fetch(e.request) .then((response) => { if (response.ok) return response; // Пользователь онлайн, но ответ не в порядке if (isImage(e.request)) { // Получаем из кэша изображение-заполнитель return caches.match("/broken.png"); } }) .catch((err) => { // Пользователь, вероятно, оффлайн if (isImage(e.request)) { // Получаем из кэша изображение-заполнитель return caches.match("/broken.png"); } }) ) });
Я создал GitHub Gist с полной реализацией на случай, если вы захотите использовать его в своих собственных проектах.
Источник: https://bitsofco.de/
Редакция: Команда webformyself.