Web-приложения всё больше "затачиваются" под мобильные устройства, а service worker'ы являются фундаментом прогрессивных web-приложений (PWA). При первом ознакомлении с данной технологией может сложиться впечатление, что основной задачей service worker'ов является кэширование контента. И это так. Задача service worker'ов обеспечение функционирования web-приложения в условиях нестабильного или вообще отсутствующего подключения к Сети, что достигается при помощи кэширования данных.
Под катом пара мыслей о том, к каким последствиям для web-приложений привело появление возможности кэшировать данные посредством service worker'ов.
Вот классическая трёхуровневая архитектура web-приложения:
Добавление на клиенте service worker'а и инструментов для сохранения
данных (Cache API и IndexedDB) превращают трёхуровневую архитектуру
в пятиуровневую:
По сути, при отсутствии соединения с Сетью прогрессивное
web-приложение должно работать на клиенте в классическом
трёхуровневом режиме:
а когда появляется соединение с Сетью переходить на пятиуровневую:
В web-разработке популярной является стратегия mobile first. Для PWA есть похожая стратегия offline first. Её суть в том, что приложение изначально разрабатывается для условий автономной работы (классическая трёхуровневая архитектура на клиенте), а затем расширяется до пятиуровневой.
Таким образом, самой первой задачей service worker'а является кэширование данных в объёме, достаточном для автономного функционирования приложения. Следующей организация очереди запросов для обмена данными конкретного пользователя с сервером. Затем мониторинг состояния соединения с сервером (online/offline) и обработка очереди запросов.
Итого, обеспечения функционирования приложения в offline-режиме это не только кэширование, а ещё и (в какой-то мере) дублирование серверной бизнес-логики обработки данных, плюс управление очередью запросов.
Трафик между браузером клиента и сервером можно разделить на две части:
Для первого типа трафика (статика) браузер предоставляет Cache API простое хранилище "запрос" "ответ"
Для хранения данных (API) IndexedDB (NoSQL хранилище структурированных данных в формате JSON).
В панели инструментов Chrome'а хранилище находится в
Application / Cache / Cache Storage / <имя
кэша>
, содержимое выглядит примерно так:
Возможности хранилища позволяют, при достаточном воображении, перенести CDN на клиентский уровень, превращая каждый браузер в своего рода однопользовательскую точку распространения контента.
В панели инструментов Chrome'а хранилища объектов находятся в
Application / Storage / IndexedDB / <имя базы> /
<имя хранилища объектов>
, содержимое выглядит примерно
так:
База предоставляет транзакционный доступ к хранилищам для выполнения CRUD-операций, индексацию данных по ключевым полям, версионирование структуры базы. Запросы к IndexedDB выполняются в асинхронном режиме. Объём хранимых данных зависит от многих факторов, но вполне может достигать гигабайтов.
В общем, функционала вполне достаточно для реализации в рамках браузера приложения в классической трёхуровневой архитектуре.
При имплементации клиентской бизнес-логики в service worker'е он получается достаточно сложным функциональным блоком. В одном файле его код без средств сборки (типа webpack'а) не разместить. Для загрузки скриптов в область видимости service worker'а существует метод WorkerGlobalScope.importScripts(). Но его особенность в том, что он синхронный. Для service worker'а нет возможности динамической загрузки его компонентов:
import('/modules/my-module.js') .then((module) => { // Do something with the module. });
Все скрипты, которые могут понадобиться service worker'у, должны загружаться при его регистрации. Что и понятно service worker должен обеспечивать работоспособность web-приложения в offline-режиме, а для этого он сначала должен обеспечить свою собственную работоспособность.
Добавление service worker'а (и IndexedDB) даёт возможность создавать браузерные приложения (PWA в режиме offline) по классической трёхуровневой схеме (интерфейс логика данные). Online-режим в PWA добавляет к трёхуровневой схеме ещё и "межсерверные" взаимодействия: Client Logic/Data Server Logic/Data. Бизнес-логика web-приложения распадается на две части: для отдельного пользователя и для совокупности всех пользователей, во многом совпадающие, но имеющие различия (например, ACL имеет смысл встраивать только в серверную сторону).
Синхронная загрузка скриптов в service worker'е ограничивает возможности разработчиков в выборе инструментария для реализации бизнес-логики в самом service worker'е (например, слабо поддерживается ES6 import), поэтому есть смысл оставлять за service worker'ами только лишь функции кэширования статики, а всю клиентскую бизнес-логику выводить в Main Thread (включая обработку данных и очередей запросов).
Поэтому схема архитектуры прогрессивного web-приложения, на мой
взгляд, лучше выглядит в таком виде:
Service worker обрабатывает и кэширует только статику. Клиентская логика отрабатывает в основном потоке браузерного приложения, взаимодействует с хранилищем данных и сама контролирует переход из online в offline и обратно (т.е. обмен данными с серверной частью приложения).
Возможно, кому-то изложенное выше покажется несколько запутанным, но до написания этой статьи моё понимание роли service worker'ов в прогрессивных web-приложениях было ещё запутаннее. Буду признателен за комментарии, ещё больше проясняющие задачи service worker'ов и способы их использования.
Добрый день, меня зовут Андрей Солдатов, мы в команде Россельхозбанка разрабатываем новые интересные проекты, связанные с сельским хозяйством. В качестве фронтального решения для некоторых из них мы решили использовать интересное open source решение Vue Storefront. В этой статье вы можете ознакомиться с ключевыми возможностями и особенностями этого решения. Статья является переводом статьи из официального блога Vue Storefront, оригинал доступен по ссылке.
Vue Storefront - это достаточно сложное решение с обширными возможностями. Изучение их всех может занять некоторое время. В этой статье я за несколько минут постараюсь объяснить вам его ключевые понятия, чтобы показать все необходимое для начала работы с Vue Storefront.
Vue Storefront это backend-независимое PWA (веб)приложение для электронной коммерции, написанное на Vue.js. То, что он использует headless-архитектуру, позволяет Vue Storefront работать с любой eCommerce платформой, он может выступать в качестве PWA для Magento, Shopify, BigCommerce, WooCommerce и других.
Это очень популярный open source проект с сильным и развивающимся сообществом.
Ключевые особенности Vue Storefront:
Кроссплатформенность
Фокус на производительность
Принцип mobile-first
Передовые технологии
Нет ограничений в стилистике тем и кастомизаций
Открытый исходный код с лицензией MIT
Сообщество опытных разработчиков
Серверный рендеринг из коробки (для SEO)
Offline режим
Тут вы найдете сайт Vue Storefront, а здесь GitHub репозиторий.
Vue Storefront является backend-независимым благодаря vue-storefront-api и отдельным API-коннекторам для eCommerce backend-платформ. Формат данных в vue-storefront-api всегда одинаков для любой платформы. Это означает, что независимо от того, какую eCommerce платформу вы используете, ваш веб-интерфейс остается без каких-либо изменений.
Это отличная стратегия, так как вы легко сможете переходить с одной платформы на другую (или с одной версии на другую, например, Magento 1 -> 2), не трогая ваш frontend.
Коннектор API работает в два этапа:
data pump (на рисунке mage2nosql) извлекает статические данные (каталог, заказы и т.д.) из вашей eCommerce платформы во Vue Storefront ElasticSearch и изменяет формат на тот, который используется vue-storefront-api. После получения данных вы можете отобразить ваш каталог продуктов во Vue Storefront. После загрузки данных в ElasticSearch они будут синхронизироваться с изменениями на стороне backend-платформы и обновлять контент.
worker pool это синхронизация так называемых динамических вызовов (пользовательских сессий, правил корзины и т.д.), которые не могут быть сохранены в базе данных и должны вызываться vue-storefront-api напрямую с backend платформы.
Управляя этими двумя этапами интеграции, Vue Storefront может работать с вашей backend платформой.
Некоторые из популярных backend платформ уже имеют готовые интеграции (Magento 2, Magento 1, CoreShop, BigCommerce, WooCommerce), но вы можете легко создать свою собственную с помощью шаблона.
Синие элементы на рисунке отвечают за автономный кеш, о них будет рассказано позже.
При работе с Vue Storefront необходимо ознакомиться с тремя концепциями:
Vue Storefront Core (/core(http://personeltest.ru/aways/github.com/DivanteLtd/vue-storefront/tree/master/core)) является связующим звеном для всех его функций, которые позволяют Vue Storefront работать. Он содержит все необходимые для работы приложения: SSR, билды, встроенные библиотеки и хелперы. Не следует трогать эту папку при создании собственных решений, для того, чтобы продолжать получать обновления.
Vue Storefront Modules (/core/modules и /src/modules) - это eCommerce модули. Каждый модуль представляет собой одну функцию (например, корзина, wish list, каталог, сторонние интеграции). Вы можете добавлять / удалять / редактировать эти модули по своему усмотрению и использовать только те функции, которые вам нужны. Они же используются и для сторонних расширений.
Vue Storefront Themes (src/themes) - это реализация вашего сайта. В темах вы можете использовать и расширять всю логику из зарегистрированных модулей/ядра и добавлять свою HTML-разметку и стили. Vue Storefront из коробки предоставляет полностью надстраиваемую тему по умолчанию.
Подводя итог: ваш сайт это в основном Vue Storefront Themes, в котором используются функции, предоставляемые Vue Storefront Modules. Vue Storefront Core склеивает это все воедино.
Понимание этих трех аспектов позволит вам работать с Vue Storefront и создавать собственные сайты на нем.
Полезные материалы: структура проекта Vue Storefront.
Есть три варианта:
Это все, что нужно чтобы VS заработал с нашим демо-бэкендом.Вы можете подключить свой frontend к нашей демо backend-платформе (лучший вариант, чтобы попробовать Vue Storefront).
Вы можете настроить frontend с вашим собственным vue-storefront-api и базой данных, выгруженной из демо-версии.
Вы можете настроить frontend с помощью vue-storefront-api, подключенного к вашему eCommerce backend.
Чтобы сделать что-то из этих вариантов, просто введите "yarn installer" в корневом каталоге проекта и ответьте на вопросы в консоли. После завершения установки для запуска проекта введите команду "yarn dev" (по умолчанию на 3000 порту). Независимо от того, что вы выберете, вы сможете изменить настройки в файле конфигурации.
Большая часть конфигурации Vue Storefront (например, активная тема, адреса внутреннего интерфейса API, настройки магазинов и т.д.) выполняется через файл конфигурации, который можно найти в папке "/config". Файл default.json содержит все настройки по умолчанию.
Для вашей реализации вы должны создать файл local.json и включить поля из default.json, которые вы хотите переопределить. Эти два файла будут объединены с приоритетом local.json в процессе сборки. Если вы используете инсталлятор для установки своего инстанса Vue Storefront, он сам сгенерирует корректные файлы конфигурации.
При создании тем в Vue Storefront в большинстве случаев все, что вам нужно это создать собственную HTML и CSS разметку. Вся необходимая бизнес-логика находится в ядре с его основными модулями и может быть легко внедрена в любой из компонентов темы.
Бизнес-логика из основных компонент может быть легко внедрена в любые темы с использованием Vue.js миксинМеханизм внедрения основной бизнес-логики в темы очень прост. Мы используем Vue.js миксины для сохранения возможности обновления основной бизнес-логики. Таким образом, предполагая, что у нас есть, например, ядро Microcart с такой бизнес-логикой (слева на рисунке), мы можем легко внедрить его в любой из наших компонентов темы (справа), просто импортировав его и добавив в качестве mixins: [Microcart]. Это все, что вам нужно, чтобы использовать основной бизнес-логику в вашей теме. При таком подходе мы можем легко обновлять все основные компоненты, не ломая сайт.
Самый простой способ создать свою тему - это создать копию темы по умолчанию, изменить её имя в package.json, выбрать активную тему в config/local.json и запустить yarn, чтобы сделать Lerna линкование (которое используются для monorepos).
Vue Storefront продолжает работать, даже когда пользователь не в сети. Нам удалось это сделать с помощью активного использования кеша браузера.
Для статик ресурсов (только для prod) мы используем плагин sw-precache (конфигурацию можно найти в /core/build/webpack.prod.sw.config.js). Они кэшируются в Service Worker и могут быть проверены на вкладке браузера Application.
Для кэша каталога и данных мы используем IndexedDB и Local Storage. Мы также предварительно выбираем товары из посещенных категорий, поэтому, как только вы посетите одну из них, все ее продукты будут доступны вам в автономном режиме. Механизм автономного кэширования находится в папке /core/lin./storage.
Мы используем некоторые из закэшированных данных, даже когда пользователь находится онлайн, это нужно для мгновенного отображения контента. Вот почему Vue Storefront такой быстрый.
Можете мне не верить, но это все, что вам нужно знать, для начала работы с Vue Storefront! После изучения основ просто посмотрите нашу документацию и посетите сообщество, чтобы глубже погрузиться в проект.
Также вы можете посмотреть видео с 4го Vue Storefront хакатона с ознакомительным обучением.
В комментах к моей предыдущей статье о service worker'ах была высказана мысль, что PWA на десктопах - вещь малополезная. Примерно полгода назад я разбирался с тем, как прикрутить PWA Vue Storefront к магазинам на платформе Magento и мне понравилось, как шустро крутилось в моём компьютере это приложение по сравнению с оригинальным web-интерфейсом. Мой персональный опыт показывал, что PWA на десктопах имеет неплохую перспективу, но дальнейшее углубление в тему показало, что коллега @sumanai со своим отрицанием PWA на десктопах был прав.
Какая основная "фишка" прогрессивных web-приложений?
Способность работать в режиме offline.
Эта способность прежде всего актуальна для мобильных устройств (смартфоны и планшеты), и, в какой-то мере, для ноутбуков. Десктопы же практически всегда находятся в зоне стабильно работающего интернета.
По способу ввода ("сенсорный экран" против "клавиатура + мышь") и отображения информации (размер дисплея в дюймах) устройства можно разделить на две больших группы:
смартфоны и планшеты
ноутбуки и десктопы
Современные браузеры предлагают разнообразный API для web-приложений, большая часть которого работает для обеих групп устройств. Тем не менее вот эти три интерфейса актуальны именно для смартфонов/планшетов:
Для "осознанного" кэширования запросов (например, на уровне service worker'ов) современные браузеры предоставляют Cache API, но основным хранилищем данных при работе в режиме offline является IndexedDB. Серверная база данных общего пользования (MySQL, Postgres, Oracle, MongoDB, ...) в этот момент заменяется базой данных персонального пользования (IndexedDB).
В online-режиме самым очевидным поведением является как можно быстрее передать данные в центральное хранилище на сервере или получить из него наиболее актуальные данные. Для условий стабильного интернет-соединения IndexedDB - это пятое колесо в телеге. Для offline режима - жизненная необходимость, позволяющая кэшировать получение данных от пользователя до момента установления соединения с сервером.
В моей статье "Влияние service worker'ов на web-приложения" я пришёл вот к такой схеме работы PWA:
При общении браузера с сервером возникают два потока информации:
статика: это код (HTML/CSS/JS) и медиа-файлы (в основном изображения), которые могут кэшироваться service worker'ом;
API: это пользовательские данные, которые так или иначе должны мигрировать между серверным хранилищем (DB) и персональным хранилищем (IndexedDB);
PWA - это прежде всего альтернатива native apps для мобильных устройств. Native apps также можно рассматривать с позиции, что статика (код + медиа) сворачивается в один пакет и распространяется через App Store или Google Play, а данные передаются между приложением и сервером через API (с учётом offline/online состояния подключения). И при этом никто не ожидает, что поисковики должны индексировать API-запросы native apps. От web-приложений же наоборот ждут, что любой адрес (страница) на сервере должен иметь возможность быть собранным на стороне сервера, в том числе и для индексации поисковиками.
PWA - это, в первую очередь, альтернатива native apps для мобильных устройств.
PWA должны обеспечивать работу в режиме offline, что сильно усложняет разработку клиентской части, но является бесполезным функционалом для десктопов с их стабильным online-режимом работы сети.
Особенности взаимодействия пользователя со смартфоном/планшетом (ввод-вывод, гео-позиционирование) достаточно сильно отличаются от особенностей взаимодействия пользователя с десктопом/ноутбуком. Создание универсального решения ("амфибии") добавляет сложности по отношению к двум решениям - отдельно для смартфонов/планшетов и отдельно для ноутбуков/десктопов. Причём не столько с точки зрения кодирования, сколько с точки зрения UX.
PWA хорошо подходят для мобильных пользователей, но очень плохо подходят для поисковых роботов, что бы там себе ни думал по этому поводу Google.
Привет, Хабр!
Меня зовут Святослав. Я работаю в компании ДомКлик и отвечаю за развитие сервисов оформления ипотеки. В начале года мы взяли курс на внедрение философии Progressive Web Application (PWA) в наших клиентских приложениях.
Одним из важных аспектов PWA является использование технологии Service Worker API, выполняющей роль прокси между браузером и сервером. Это позволяет поддерживать работу приложения в оффлайн-режиме, управлять кэшированием сетевых данных с их фоновой синхронизацией, а также работать с push-уведомлениями.
Однако эта технология не так проста, как кажется на первый взгляд. Для её эффективного использования придётся пройти тернистый путь из квестов, связанных с особенностями жизненного цикла воркеров, неполной поддержкой браузерами, проблемами с политикой кэширования и попутными побочными эффектами (устаревшее содержимое кэша браузера или, к примеру, сломанные ссылки).
Workbox это разработанный в Google набор инструментов, предоставляющих высокоуровневый API для работы с такими браузерными технологиями, как Service Worker API и Cache Storage API. Инструментарий состоит из набора изолированных модулей, которые помогут вам сделать полноценное PWA-приложение.
Входящие в состав Workbox модули.Вы можете работать с сервис-воркерами нативно, однако использование Workbox даст вам значительные преимущества. Давайте рассмотрим их.
Зачастую сложно сформировать единую политику кэширования для всех запрашиваемых ресурсов. К примеру, можно себе позволить кэшировать шрифты на долгий срок и не нагружать сеть запросами, но мы не можем кэшировать запросы, отвечающие за предоставление актуальных данных, влияющих на бизнес-процесс (например, ипотечную ставку или срок кредитования). Здесь нам на помощь приходит Workbox, который предлагает тонкую настройку стратегий кэширования, вплоть до каждого запроса. Разберём особенности работы каждой из них.
При получении запроса сервис-воркер перенаправляет его в сеть. Кэш не используется.
Сервис-воркер формирует ответ на запрос только из кэша. Сеть не используется. Эта стратегия будет полезна, если у вас используется предварительное кэширование.
В самом начале происходит попытка получить данные из сети. Если получен корректный ответ, то сервис-воркер возвращает его браузеру, перед этим сохраняя полученные данные в кэш. Если же запрос, направленный в сеть, завершился ошибкой, то для ответа будут использоваться данные из кэша.
Согласно этой стратегии, система сперва пытается получить данные из кэша. Если в хранилище ничего не найдено, то запрос уходит в сеть. При получении успешного ответа кэш обновляется, после чего ответ возвращается браузеру.
Эта стратегия предполагает использование кэшированного ответа на запрос, если он доступен, но при этом уходит фоновый запрос на обновление кэша из сети. Это наиболее безопасная для пользователя стратегия, потому что она предполагает регулярное обновление данных кэша. Однако у неё есть недостаток: возникает нагрузка на сеть из-за фоновых запросов за обновленными данными.
С помощью плагинов можно настроить каждую стратегию посредством дополнительных параметров. Например, добавить имя сегмента для Cache Storage, выставить сроки протухания данных, настроить статусы ответов, которые нужно кэшировать.
В приведенном ниже примере определяются правила для кэширования изображений. При успешном статусе ответа файлы будут сохраняться в сегмент хранилища с названием "assets". Максимальное число хранимых записей 60, срок актуальности данных 30 дней.
import {registerRoute} from 'workbox-routing';import {CacheFirst} from 'workbox-strategies';import {CacheableResponsePlugin} from 'workbox-cacheable-response';import {ExpirationPlugin} from 'workbox-expiration';registerRoute( ({request}) => request.destination === 'image', new CacheFirst({ cacheName: 'assets', plugins: [ new CacheableResponsePlugin({ statuses: [0, 200] }), new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days }) ] }));
Некоторые HTTP-запросы включают в себя заголовок
Range:
, который говорит серверу о том, чтобы тот
вернул часть запрашиваемого ресурса. Такие запросы используются в
случае потоковых аудио- и видеоданных для реализации чанковой
загрузки информации, что является более выигрышной альтернативой по
сравнению с одинарным запросом на весь объём данных.
Workbox содержит модуль workbox-range-requests, который реализует всю необходимую логику для поддержки чанковой загрузки с участием кэша.
Простой пример работы с этим модулем:
import {registerRoute} from 'workbox-routing';import {CacheFirst} from 'workbox-strategies';import {RangeRequestsPlugin} from 'workbox-range-requests';registerRoute( ({url}) => url.pathname.endsWith('.mp4'), new CacheFirst({ plugins: [ new RangeRequestsPlugin(), ] }););
Отладка работы сервис-воркера является неотъемлемой частью процесса разработки PWA-приложений. У Workbox есть отладочный режим, который позволяет выводить в консоль браузера подробную информацию о работе вашего сервис-воркера. Опираясь на журналы, разработчик сможет намного быстрее добраться до корня возникшей проблемы.
Workbox разрабатывается с учётом кроссбраузерной работы. Если браузер не поддерживает определённую технологию, будет использоваться альтернативная реализация.
К примеру:
Модуль оповещения об обновлении кэшированных данных (workbox-broadcast-cache-update) использует под капотом Broadcast Channel API. А если браузер его не поддерживает, то переключается на механизм postMessage.
Модуль фоновой синхронизации данных (workbox-background-sync) использует Background Sync API. При отсутствии браузерной поддержки модуль попытается повторить запрос из очереди событий во время следующего запуска сервис-воркера.
При работе в оффлайн-режиме важно понимать, как пользователи взаимодействуют с вашим приложением. Но для работы сервисов аналитики (например, Google Analytics) важно наличие интернет-соединения для отправки отчетов на сервер. При отсутствии соединения данные о пользователе будут потеряны, и итоговый аналитический отчет окажется некорректен.
Модуль Workbox Google Analytics создан для решения этой проблемы. При оффлайн-работе он отлавливает неудачные запросы и сохраняет их в локальную базу данных браузера IndexedDB. А при возобновлении интернет-соединения запросы повторно отправляются на серверы Google Analytics.
Простой пример подключения этого модуля:
import * as googleAnalytics from 'workbox-google-analytics';googleAnalytics.initialize();
Workbox предлагает следующие варианты использования:
Давайте рассмотрим один из способов использования Workbox вместе со сборщиком статических модулей Webpack. Для начала нам нужно установить плагин, который поставляется в виде npm-пакета:
npm install workbox-webpack-plugin --save-dev
Далее нужно подключить плагин в конфигурационном файле
webpack.config.js
, чтобы webpack автоматически
сгенерировал воркер-файл на этапе сборки приложения.
Возьмем за основу пример из официальной документации. По умолчанию Workbox добавляет в предварительный кэш все файлы, которые участвуют в webpack-сборке. Но было бы ошибкой кэшировать все изображения вашего приложения, когда их число измеряется сотнями, а общий размер мегабайтами (нужно помнить, что браузер имеет квоту на хранение данных в Cache Storage). Вместо этого выставим такие параметры, чтобы сервис-воркер кэшировал изображения только тогда, когда приложение обращается за их загрузкой. А также установим лимит в 10 записей.
// Inside of webpack.config.js:const WorkboxPlugin = require('workbox-webpack-plugin');module.exports = { // Other webpack config... plugins: [ // Other plugins... new WorkboxPlugin.GenerateSW({ // Do not precache images exclude: [/\.(?:png|jpg|jpeg|svg)$/], // Define runtime caching rules. runtimeCaching: [{ // Match any request that ends with .png, .jpg, .jpeg or .svg. urlPattern: /\.(?:png|jpg|jpeg|svg)$/, // Apply a cache-first strategy. handler: 'CacheFirst', options: { // Use a custom cache name. cacheName: 'images', // Only cache 10 images. expiration: { maxEntries: 10, } } }] }) ]};
На выходе мы получим сгенерированный файл sw.js
с
определенными правилами кэширования сетевых данных.
Workbox делает работу с сервис-воркерами более комфортной. Этот инструмент позволяет декларативно определить правила кэширования ресурсов приложения, взяв низкоуровневую работу на себя. К тому же Workbox уже интегрирован с такими инструментами разработки, как react-create-app, vue-cli, preact-cli, next.js, что говорит о его признании со стороны сообщества разработчиков.
Слышали шутку о том, что если установил ServiceWorker - пора менять домен? Сейчас я расскажу, в чём её смысл и что делать, если вы всё-таки решили, что вам необходим PWA.
В инструкциях по типу этой или этой ServiceWorker'у и работе с ним почти не уделяется внимания. И, я уверен, подобные статьи - первое, что вы прочтёте перед использованием. Но в момент, когда после подобных статей ваш свежеиспечённый PWA наконец-то появился на продакшене и у юзеров появилась возможность добавить ещё одну иконку на свой рабочий стол, знайте: вами пройдена точка невозврата.
С вашего позволения, я не буду останавливаться на описании
Service Worker (далее SW) и том, как он работает. На Хабре уже есть хорошая статья об
этом. Даже не важно, какой SW конкретно у вас. Может, вы
используете create-react-app, а значит за SW
у вас отвечает библиотека Workbox. Возможно, вы
реализовывали SW сами, с какой-то мудрённой стратегией кэширования.
Стек на самом деле не важен. В той же документации CRA говорится,
что всё, что вам нужно - это поменять одну строчку и получить все
прелести app-like поведения. Вы написали .register()
и
ожидаете результат. И вы его получите.
В следующий раз, когда недовольный клиент попросит вас поменять цвет этой оранжевой кнопки или решить наконец тот баг со слетающим фокусом, вы обнаружите себя в удивительной ситуации. Хотфикс в репозитории, контейнер собрался и nginx точно раздаёт последнюю версию, но клиент почему-то всё ещё недоволен. Ах да, мы же теперь PWA.
Обновите, пожалуйста, страницу. Как не помогает? А если CTRL+R ?
Итак, что же делать, когда судорожное обновление страницы не помогает и клиент всё ещё видит издевательски оранжевую кнопку?
Важно помнить, что SW пытается вести себя как десктопное приложение.
Давайте вспомним, как себя обновляет десктопное приложение. Оно скачивает свежий инсталлятор, удаляет старую версию и устанавливается заново. Только после этого пользователь получает новую версию приложения.
Схожим образом действует и браузер при обновлении SW.
Всего у SW три статуса: installing, waiting и active. Active - это ваш текущий, работающий SW. Стадии installing и waiting SW проходит на пути к active. На стадии installing SW нужно время, чтобы установиться. На стадии waiting ему нужна причина, чтобы заменить текущий SW (обычно это закрытие всех вкладок приложения). Вот в этом поведении и весь подвох.
Браузер получает новый SW, но юзер увидит изменения только тогда, когда приложение - все вкладки сайта будут закрыты. SW даже установит эту новую версию, но не активирует её. Пока все вкладки со старой версией не будут закрыты. Неважно, сколько раз вы обновляли страницу. Даже если это одна страница, вам всё равно нужно её закрыть.
Дело в том, что браузер начинает загружать обновлённую страницу до того, как старая "умрёт". И когда вы перезагружаете страницу, для SW существует аж две вкладки: старая, обречённая на смерть, и новая, которая ещё запускается. Пока не будут закрыты все, SW не обновится. Это нужно для того, чтобы вы не получали разные версии приложения во вкладках браузера.
Я намеренно пропускаю глубокий разбор механизма обновления SW с его озвученными выше статусами installing, waiting и active. Больше об этом написано тут - рекомендую ознакомиться. Мы уже понимаем механизм и вообще мы здесь, чтобы решить проблему.
Что же, вы можете делать рассылки клиентам с просьбой перезагрузить страницу со сбросом кэша, а можете воспользоваться следующими вариантами решения.
Самый простой (и опасный) способ - это просто
пропустить ожидание в установке SW. В скоупе вашего SW есть
прекрасная функция skipWaiting()
, которая сделает это
для вас. При её вызове новый SW после своей становки сразу убивает
старый. Вам лишь надо дождаться "перезапуска" приложения.
Но будьте осторожны: данный подход несёт опасность, если у вашего
пользователя открыты другие вкладки с приложением. Вам может
показаться, что слепо вызывать skipWaiting()
более чем
достаточно, но это приводит к багам на вашем продакшене, которые
потом сложно понять и воспроизвести.
Это слегка лучше, чем прошлый подход. В
navigator.serviceWorker
происходит
эвентcontrollerchange
,когда новый SW получает
контроль над текущей страницей. Это происходит сразу после
прохождения этапа installing.
Теперь можно вызвать skipWaiting()
во время установки,
отловить эвент и заставить вкладку обновиться. Это будет выглядеть
вот так:
navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload());
В таком случае SW устанавливается и сразу перезагружает все
страницы, переводя их на новую версию.
Но, с точки зрения пользовательского опыта, это не очень хороший
подход. Может, ваш пользователь заполняет сложную форму или вносит
данные своей карточки, а тут установилось ваше обновление и
перезагружает ему страницу посреди дела. Нехорошо.
Суть этого метода в том, что мы ждём, пока SW установится, а потом показываем пользователю какое-то сообщение - модальное окно или алерт с предложением перезагрузить страницу и перейти на новую версию.
Мы всё ещё перезагружаем страницу на срабатывании
controllerchange
, как и в предыдущем способе, но
теперь пользователь знает о том, что это
произойдёт и может этого избежать.
Для того, чтобы отследить новый SW, нам понадобится объект
ServiceWorkerRegistration
. Раньше мы просто вызывыли
.register()
и не знали, что этот метод возвращает
промис с объектом регистрации. В этом API регистрации есть
несколько интересных возможностей. Например, можно вызвать
update()
, чтобы обновить SW вручную. Обычно он делает
это сам после регистрации, но вдруг вы хотите проверять наличие
обновлений чаще.
Ссылку на текущий (active) SW можно получить через
navigator.serviceWorker.controller
из поля active в
регистрации. Таким же образом можно достучаться до ожидающего
(waiting) или устанавливающегося (installing)
SW.
Любому SW можно отправить сообщение через
postMessage()
, если вы работали с iframe и передавали
сообщения между окнами, вам знаком этот API. Внутри кода SW мы
можем слушать это событие. Вы можете добавить следующий код в ваш
SW.
addEventListener('message', ev => { if (ev.data === 'skipWaiting') return skipWaiting();});
Если вы используете Workbox или CRA, то примерно этот код там уже есть.
Дальше нам нужно отследить появление ожидающего SW. На мой
взгляд лучше не реагировать каждый раз на SW со статусом
installing, как это пишут в некоторых руководствах, а дождаться
когда объект регистрации SW вернёт true в поле
waiting
. Это замедляет обновление, но не триггерит
ваше модальное окно когда SW устанавливается в первый раз.
После того, как мы дождались ожидающий SW, вызываем модальное
окно, в котором пользователь может подтвердить обновление. При
подтверждении мы вызываем skipWaiting()
и насильно
перезагружаем страницу, как описано выше. При отказе обновление
будет отложено. Выглядеть код в моём случае будет так :
// вызов модального окнаconst askUserToUpdate = reg => { return Modal.confirm({ onOk: async () => { // вешаем обработчик изменения состояния navigator.serviceWorker.addEventListener('controllerchange', () => { window.location.reload(); }); // пропускаем ожидание if (reg && reg.waiting) { reg.waiting.postMessage({ type: 'SKIP_WAITING' }); } }, onCancel: () => { Modal.destroyAll(); }, icon: null, title: 'Хорошие новости!
Какие характеристики должны быть у web-приложения, чтобы соответствовать критерию "прогрессивное"? Понятно, что, как и обычные web-приложения, прогрессивные строятся на базе "большой тройки" web-технологий - HTML/CSS/JS. Но что именно делает web-приложения прогрессивными?
В данной публикации я попробую найти минимальный набор характеристик, которым на данный момент должно соответствовать web-приложение, чтобы называться "прогрессивным", а также отвечу на вопрос, почему в августе этого года некоторые "прогрессивные" приложения перестанут быть таковыми.
Я возьму пустой HTML-файл и буду постепенно добавлять к нему артефакты, превращающие его в PWA, пока мой смартфон не решит установить это приложение, а Chrome не перестанет выкидывать предупреждения. Chrome выбран по причине того, что основным "движителем" технологии PWA на данный момент является Google.
Первое, что нужно настроить на сервере - это шифрование трафика. Тут всё делается так же, как и для обычных web-приложений. Лично я использую Apache httpd, а сертификаты для шифрования генерирую через Let's Encrypt.
Оболочка приложения - это
минимальный набор файлов, позволяющий приложению запускаться в
offline-режиме. Я попробую весь необходимый код (HTML/CSS/JS) по
максимуму уместить в одном файле - index.html
.
<!DOCTYPE html><html lang="en"><head> <title>PWA</title> <style> BODY { background-color: #FB9902; color: #342309; font-size: xx-large; margin: 0; } DIV { align-content: center; display: grid; height: 100vh; justify-content: center; } </style></head><body><div>App Shell</div></body></html>
Манифест является непосредственным маркером того, что данная страница является частью прогрессивного web-приложения. Подключается манифест в заголовке HTML-страницы:
<link rel="manifest" href="demo.pwa.json">
Специальных правил для наименования файла с манифестом я не
видел, тип содержимого маркируется через значение
manifest
атрибута rel
.
Пустой манифест генерирует такие сообщения об ошибках в Chrome:
Вот минимальное содержимое, которое не вызывает появления в Chrome предупреждений:
{ "start_url": "./", "name": "PWA", "display": "standalone", "icons": [ { "src": "./icon.png", "sizes": "144x144", "type": "image/png" } ]}
Как видно из предупреждений Chrome'а, иконка должна быть размера 144x144 минимум и в формате PNG, SVG или WebP. Примерно такую я и сделал:
В общем случае может быть множество иконок различных размеров.
После добавления минимального манифеста и иконки в Chrome остаётся только одно предупреждение - относительно service worker'а.
Service worker подключается через скрипты в оболочке приложения
(index.html
):
<script> if ("serviceWorker" in navigator) { self.addEventListener("load", async () => { const container = navigator.serviceWorker; if (container.controller === null) { const reg = await container.register("sw.js"); } }); }</script>
Минимальное содержимое файла sw.js
:
'use strict';
вызывает вот такое предупреждение в Chrome: Page does not
work offline
Stackoverflow сообщает, что предупреждение
связано с отсутствием обработчика на событие fetch
.
Добавляем пустой обработчик:
'use strict';function hndlEventFetch(evt) {}self.addEventListener('fetch', hndlEventFetch);
Теперь предупреждение изменилось на:
Site cannot be installed: Page does not work offline. Starting in Chrome 93, the installability criteria is changing, and this site will not be installable. See https://goo.gle/improved-pwa-offline-detection for more information.
Моя текущая версия Chrome: Version 89.0.4389.72 (Official
Build) (64-bit)
Тем не менее, смартфон предлагает установить приложение при заходе на страницу:
То есть, на данный момент service worker может быть номинальным, но в ближайшем будущем (в августе 2021-го) этого будет недостаточно.
Чтобы наше приложение оставалось PWA и после августа 2021-го, нам нужно добавить кэширование файлов, входящих в оболочку приложения. Вот полный код service worker'а:
'use strict';const CACHE_STATIC = 'static-cache-v1';function hndlEventInstall(evt) { /** * @returns {Promise<void>} */ async function cacheStaticFiles() { const files = [ './', './demo.pwa.json', './icon.png', './index.html', './sw.js', ]; const cacheStat = await caches.open(CACHE_STATIC); await Promise.all( files.map(function (url) { return cacheStat.add(url).catch(function (reason) { console.log(`'${url}' failed: ${String(reason)}`); }); }) ); } // wait until all static files will be cached evt.waitUntil(cacheStaticFiles());}function hndlEventFetch(evt) { async function getFromCache() { const cache = await self.caches.open(CACHE_STATIC); const cachedResponse = await cache.match(evt.request); if (cachedResponse) { return cachedResponse; } // wait until resource will be fetched from server and stored in cache const resp = await fetch(evt.request); await cache.put(evt.request, resp.clone()); return resp; } evt.respondWith(getFromCache());}self.addEventListener('install', hndlEventInstall);self.addEventListener('fetch', hndlEventFetch);
С таким service worker'ом наше приложение будет считаться прогрессивным и в Chrome 93+. Вот какие файлы легли в кэш:
Ошибка, которая высветилась в консоли - отсутствие файла
favicon.ico
:
GET https://bwl.local.teqfw.com/favicon.ico 404
Но это уже особенности работы браузера, а не PWA.
Чтобы web-приложение считалось прогрессивным (в том числе и после августа 2021-го) оно должно удовлетворять следующим условиям:
Использовать шифрование (HTTPS).
Оболочка приложения (загружает манифест приложения).
Манифест (загружает иконку и service worker).
Иконка.
Service worker.
Кэширование файлов оболочки приложения.
При соблюдении этих условий PWA устанавливается на смартфоне:
На написание этой статьи меня натолкнуло сообщение в Chrome о том, что с версии 93 web-приложения без кэширования оболочки приложения более не будут считаться прогрессивными и им будет отказано в установке. Я вышел на PWA через такое замечательное приложение, как Vue Storefront. Разработчики провели гигантскую работу и привязали к Magento 2 фронт, созданный с использованием современных технологий (положа руку на сердце, оригинальный фронт Magento 2 очень сильно отстал от современных тенденций web-разработки).
Когда я разбирался с тем, как устроен Vue Storefront, я обратил внимание, что приложение написано из расчёта, что соединение с интернетом будет всегда. Что и понятно, e-коммерция без интернета перестаёт быть таковой. И хотя браузеры предоставляют определённые возможности для того, чтобы PWA были максимально похожи на нативные мобильные приложения, современные PWA не спешат их использовать. Всё-таки, они во-первых - web, а прогрессивные - лишь во-вторых. Можно написать web-приложение, можно написать serverless приложение (за исключением App Shell, манифеста и service worker'а, разумеется - их всё равно придётся тянуть с сервера). Но написать приложение, которое бы работало как web-приложение при наличии интернет-соединения, и работало в автономном режиме в его отсутствие - в разы сложнее каждого из этих вариантов.
Именно поэтому разработчики Vue Storefront не стали заморачиваться с автономной функциональностью, ограничившись кэшированием статики и некоторых данных. Именно поэтому для Vue Storefront вылетает сообщение о том, что в Chrome 93+ их приложение более не будет считаться прогрессивным (демо, откройте консоль). Кстати, для собственной PWA-разработки самой Magento ситуация аналогичная.
Другими словами Google ужесточает критерии PWA, сдвигая фокус от web'а в сторону мобильных платформ. Конечно, можно и дальше называть обычные web-приложения прогрессивными, даже если они на 100% предполагают использование на десктопах и в условиях стабильного интернет-соединения. Но общая тенденция говорит о том, что место для PWA - мобильные устройства:
In December 2020, Firefox for desktop abandoned implementation of PWAs (specifically, removed the prototype "site-specific browser" configuration that had been available as an experimental feature). A Firefox architect noted: "The signal I hope we are sending is that PWA support is not coming to desktop Firefox anytime soon." Mozilla still plans to support PWAs on Android.
Разработчики Firefox не собираются поддерживать PWA в десктопной версии своего браузера.
Нельзя сказать, что PWA являются чисто Google'овской технологией:
всё-таки поддержка PWA-технологии различными браузерами на различных платформах достаточно объемлющая. Но дрейф в сторону "мобилизации" технологии я игнорировать не могу.
На мой взгляд PWA - это "нишевое" решение, для мобильных устройств. Да, оно сделано с использованием web-технологий (HTML/CSS/JS) и крутится внутри браузера, но к разработке PWA нужно подходить скорее с точки зрения нативных мобильных приложений, чем с точки зрения web-приложений (сайтов или SPA).
Другими словами, если вам нужно именно web-приложение, то вам не нужно PWA. А вот если вам нужно разработать именно мобильное приложение, то PWA может быть приемлемым вариантом.
Среди отзывов, полученных мной после этого доклада, занявшего второе место по полезности по результатам опроса после конференции, был такой: Краткий пересказ документации Workbox. Что ж, в какой-то степени это действительно так: я рассказал пошаговую историю превращения обычного веб-приложения в прогрессивное с помощью этой замечательной библиотеки. Буду очень рад, если доклад и конспект вдохновят вас и помогут начать ваше собственное путешествие в мир сервис-воркеров и PWA.
Дмитрий Злыгин из DINS расскажет, как на фронтенде выполнять несколько задач параллельно и какие инструменты для этого нужны. Максим Сальников из Google поделится возможностями API из семейства сервис-воркеров, позволяющих продлить жизнь приложения.
Участие бесплатное по предварительной регистрации. Программа и подробная информация о спикерах под катом.
19:00-19:40 Параллелизм в вебе что это и зачем? (Дмитрий Злыгин, DINS)
Часто современные веб-приложения требуют интенсивной обработки данных. Это и обработка видео, звука, графики и какие-то расчёты. Пробежимся по тому, как на фронте можно выполнять несколько задач параллельно, что из инструментов нам доступно и что со всем этим можно делать. Обсудим кооперативную многозадачность внутри основного потока JS, и истинную многозадачность через web workers и worklet. Затронем проблему многопоточной обработки.
Доклад обзорный и будет полезен как фронтендерам с опытом, так и бэкендерам для понимания возможностей современного веба.
Дмитрий Злыгин более 5 лет разрабатывает исключительно фронтенд. Работает с JavaScript с переменным успехом третье десятилетие, повидал и Netscape Navigator, и IE 4.0. С появлением Vue.js забросил бэкенд и перешел в стан фронтендеров, но со временем осознал и все прелести React. Сейчас работает над веб-клиентом для видеоконференций в компании DINS.
19:40-19:50 PiterJS: новости из жизни сообщества
19:50-20:40 Фоновые сервисы в браузерах есть ли жизнь после закрытия вкладки? (Максим Сальников, Google)
Пользователь закрывает вкладку в браузере, и ваше замечательное фронтенд-приложение испаряется. А что если можно было бы оставить какие-то его части еще немного поработать во благо улучшения UX? Отреагировать на какие-то события, завершить начатое общение с сетью в общем, исполнить немного кода при закрытой вкладке и даже браузере. Максим расскажет о разных интересных возможностях API из семейства сервис-воркеров, позволяющих продлить жизнь приложения, чтобы всегда иметь под рукой свежие данные, не бояться проблем с сетью, уметь показывать уведомления все ради отличного пользовательского опыта. В процессе доклада мы откроем браузер и протестируем работу Background Sync API вживую.
Максим Сальников создает веб-приложения с конца прошлого века и накопил солидный опыт работы со многими аспектами веб-платформы, которым он активно делится, выступая и проводя мастер-классы на конференциях для разработчиков по всему миру (и сам организует конференции и митапы). В настоящее время он активно исследует новые возможности веба и идею прогрессивных веб-приложений (PWA) в частности.
Участие бесплатное, нужна предварительная регистрация. В день митапа мы пришлем ссылку на трансляцию на указанный при регистрации имейл.
Записи предыдущих митапов можно посмотреть на нашем YouTube-канале.
DINS IT EVENING это место встречи и обмена знаниями технических специалистов по направлениям Java, DevOps, QA и JS. Несколько раз в месяц мы организуем встречи, чтобы обсудить с коллегами из разных компаний интересные кейсы и темы.
В данной статье, мы расмотрим как "прикрутить", и настроить PWA в Angular приложении.
PWA (Progressive Web App) это веб-технология, трансформирующая
сайт в приложение. При открытии, приложение запускается в обертке
браузера, из за чего PWA позволяет использовать приложение на любой
платформе, использующей браузер, соответствующий стандартам.
Внутри себя, PWA использует Service worker, который взаимодействует
с браузером, для обеспечения доступа к некоторым встроенным
функциям. У Service worker'a есть доступ к Cache Storage для web
ресурсов, и IndexDB для данных. Благодаря Service worker'у возможна
реализация кешерования что позволяет PWA приложению работать, в
режиме "офлайн".
Концентрироваться на приложении не будем, для примера просто
создадим дефолтное, через Angular CLI, командой ng new
app
.
В результате после запуска командой ng serve
, имеем
стартовое Angular приложение на 4200 порте.
Собственно установка предельно проста и делается опять же через
Angular CLI. Необходимо всего лишь ввести команду ng add
@angular/pwa
. После успешного завершения, у нас добавятся
новые файлы в проекте и обновится несколько существующих.
Краткое описание добавившихся файлов в проект:
ngsw-config.json
- конфиг отвечающий за создание
ngsw-worker.js (serviceworker.js). Содержит базовую структуру для
нашего Service worker'а.
manifest.webmanifest
- файл манифеста, определяет
как приложение PWA будет выглядеть при открытии. Позволяет
сконфигурировать такие параметры как значки, различные бекграунды,
название и т.п.
Заглянув в файл src/index.html
, мы увидим что наш
манифест прописался в head'е, html файла нашего приложения:
И в angular.json
:
Также автоматически сгенерировались иконки приложения в
src/assets/icons/
под различные разрешения, которые
можно в дальшейшем заменить.
В app.module.ts
добавился модуль Service worker для
регистрации файла ngsw-worker.js
(данный файл
генерируется при билде приложения) для прод окружения.
Теперь можем сбилдить наше приложения и запустить его. Но так
как PWA работает только с https и localhost и ng serve
не работает с Service worker'ами, нам необходим отдельный
HTTP-сервер чтобы локально проверить ваш проект. Для этого нужно
создать сборку и разместить ее отдельно, используя http-сервер. Для
этого установим npm пакет http-server
командой
npm i -g http-server
.
После этого находясь в корневой папке нашего приложения,
создадим продакшен билд командой ng build --prod
.
По окончанию билда в папке dist появится наш собранный билд
проекта. Так же в нем будет сгенерированный файл нашего Service
worker'а ngsw-worker.js
.
Теперь находясь в папке dist/app
запустим наше
приложение на 8080 порте, командой http-server -p
8080
.
Наше приложение успешно запустилось на 8080 порту и при открытии, в адресной строке браузера появляется кнопка для установки PWA версии нашего приложения.
Так же в dev tools'е, в разделе Application, мы можем отслеживать статус нашего Service worker'а
В этой статье мы рассмотрели базовую настройку и запуск
приложения с технологией PWA, за экраном осталось описание
возможностей работы Service worker'ов в "офлайн" режиме, и
кеширования а также конфигурирование файла
ngsw-config.json
, о котором можно почитать в
официальной документации для Angular https://angular.io/guide/service-worker-config.
Когда речь заходит о разработке сразу для Android и iOS, начинаются холивары и гадания на кофейной гуще. Что перспективнее, Flutter или Kotlin Multiplatform? За этими технологиями будущее, или завтра их обе забудут?
Уверенно делать прогнозы по принципу я так вижу занятие весёлое, но бессмысленное. Как подойти конструктивнее? Как известно, кто забывает об истории, обречён на её повторение. Поэтому я решил вспомнить, какими были предыдущие решения Android+iOS, и посмотреть, что с ними стало. Тогда вместо голых спекуляций будут суровые факты. И эти факты из прошлого полезны для понимания будущего: они показывают, где разложены грабли.
Я не использовал лично большинство старых технологий, но благодаря общению со спикерами нашей конференции Mobius узнал многое об их опыте, и текст во многом основан на этом. Если вы работали с чем-то из перечисленного, смело дополняйте в комментариях, будет интересно и полезно.
В 2007-м, представляя разработчикам первый iPhone, Стив Джобс объяснял, что нативные приложения не нужны: В iPhone есть полный движок Safari. Так что можете писать потрясающие Web 2.0 и Ajax-приложения, которые выглядят и действуют на iPhone именно как приложения.
Android на тот момент ещё даже не был анонсирован. Но получается, что исторически первым единым решением для iOS и Android стал веб.
Вот только разработчики тогда не разделили энтузиазма Джобса (неудивительно, учитывая тогдашнее состояние мобильного интернета). И годом позже всё-таки появился App Store для нативных приложений. А его комиссия в 30% стала новым денежным потоком для компании. В итоге её позиция сменилась на противоположную: теперь Apple считает, что правильный подход это натив (и предпочитает не вспоминать, что там её лидер говорил в 2007-м, Океания всегда воевала с Остазией).
Однако идея веб-приложений не исчезла, а продолжила развиваться. И в 2015-м новое поколение таких приложений назвали Progressive Web Apps. Они могут хранить данные локально, работать в офлайне, а ещё их можно установить на домашний экран смартфона. Чем это тогда не настоящие мобильные приложения? Что ещё для счастья надо?
Ну, например, для счастья нужны push-уведомления. По состоянию на 2021-й iOS не поддерживает их у PWA, и это мощнейший стопор для распространения подхода. Получается, компания, которая первой хвалила веб-приложения, позже сама поставила главное препятствие на их пути. На change.org есть даже петиция, обращённая к Apple: мы всё понимаем, вы дорожите своими 30%, но эта ситуация должна измениться.
Что в итоге: подход жив, но не стал общепринятым во многом из-за ограничений Apple. В будущем что-то может измениться, стоит следить.
Это решение тоже связано с вебом, но подход другой так называемый гибридный. Тут предложили использовать знакомую троицу HTML/CSS/JS, но результат не публиковать в виде сайта, а упаковать в контейнер нативного приложения. В таком случае не сталкиваешься с ограничениями веба и можешь реализовать те же push-уведомления.
PhoneGap появился в 2009-м благодаря компании Nitobi, а в 2011-м Adobe купила её и создала также опенсорсный вариант Apache Cordova. У проекта модульная архитектура, позволяющая подключать плагины. И в сочетании с опенсорсностью это означает, что если Cordova не умеет чего-то нужного (например, взаимодействовать с акселерометром смартфона), сообщество может научить. Вроде как прекрасно, да?
На практике что-то не очень. В своё время некоторая популярность у проекта была, те же плагины появлялись, но масштабного захвата рынка не произошло. А затем всё и вовсе пошло на спад.
Почему так? Среди главных причин называют UX, уступающий нативным приложениям. Всё хуже и с производительностью, и с плавностью. Но основное отличие даже не измерить числами: пользователю попросту заметна разница между гибридным приложением и нативным, они производят разное впечатление. Для людей через WebView ощущения не те.
А с годами сказалось ещё и развитие веба. Чем лучше становились веб-приложения и чем быстрее становился мобильный интернет, тем меньше преимуществ можно было получить от гибридного подхода.
Интересно, что авторы проекта сами предвидели такое развитие событий и ещё в 2012-м написали, что итоговая цель PhoneGap прекратить своё существование. И недавно эта цель была достигнута: в 2020-м Adobe заявили о прекращении разработки, ссылаясь на то, что нишу закрыли PWA.
Что в итоге: разработка прекращена.
Проект Qt помогал людям заниматься кроссплатформенной разработкой ещё с 90-х, когда речь шла о десктопных ОС, а не мобильных. При этом он завязан на C++, который для Android и iOS не совсем чужой: даже нативные разработчики на этих платформах могут обращаться к плюсам. Так что, казалось бы, поддержка iOS/Android со стороны Qt просто напрашивалась.
Но поначалу дело осложнялось тем, что в 2008-м проект купила Nokia. Компания тогда делала ставку на Symbian и не горела желанием помогать конкурентам. В 2010-м возможностью запускать Qt-приложения на Android занимались энтузиасты, и на Хабре об этом писали:
В 2011-м Nokia отказалась от Symbian в пользу Windows Phone, а часть проекта Qt продала компании Digia. И тогда началась работа над поддержкой одновременно Windows 8, Android и iOS. Ну вот теперь-то счастье? Спустя 10 лет ясно, что тоже нет.
Вакансии по мобильной разработке на Qt сейчас единичные. Хабрапосты о ней появляются очень редко и не свидетельствуют об успехе: в одном причиной использования Qt стала ОС Аврора (экс-Sailfish), в другом попросту у меня уже был большой опыт с ним.
Что помешало? Я встречал жалобы на то, что недостаточно было использовать сам Qt и писать на С++/QML. Потому что средствами Qt многое было не реализовать, и приходилось-таки иметь дело с конкретной платформой (например, на Android работать с Java, увязывая это с плюсовой частью через JNI). Всё это очень неудобно и подрывает исходную идею бодренько запилим два приложения по цене одного.
При этом здесь пользователь тоже ощущает не-нативность. А к IDE Qt Creator есть нарекания, рантайм Qt увеличивает размер приложения, бесплатный вариант Qt подходит не всегда и может понадобиться недешёвый коммерческий. Кроме того, мне лично кажется, что ещё сказался язык. Кроссплатформенной разработке желательно быть такой, чтобы нативным разработчикам было попроще перекатиться туда, а с языком C++ слово попроще не ассоциируется.
И хотя случаи использования Qt встречаются до сих пор, это скорее исключения из правил, у которых могут быть какие-то особые исходные условия: например, хотим перетащить на телефоны с десктопа уже имеющуюся кодовую базу на C++.
Что в итоге: крайне нишевое решение, которое не умерло полностью, но используется очень узкой прослойкой.
Мигель де Икаса ещё в проекте Mono занимался тем, что притаскивал .NET на несвойственные ему платформы (начав с Linux). А когда в 2011-м он вместе со всей командой Mono попал под сокращение, основал новую компанию Xamarin, собрал прежних коллег там, и сосредоточился на мобильных платформах: мол, давайте писать мобильные приложения для них на C#, вот инструмент для этого.
Надо понимать контекст того времени. Годом ранее компания Microsoft выпустила Windows Phone и стремилась стать третьим большим игроком на мобильном рынке. И даже большая Windows лезла на мобильный рынок: готовилась к выходу Windows 8, оптимизированная для планшетов.
В таком контексте идея писать под все смартфоны на языке от Microsoft выглядит логично. Ведь если для кроссплатформы применяется родной язык одной из главных платформ, то он уже знаком части мобильных разработчиков, и они могут переиспользовать что-то из существующих кодовых баз. В общем, будет проще наводить мосты.
Но теперь задним числом мы знаем, что мобильные амбиции Microsoft заглохли. И это оставило Xamarin в странном положении: на рынке две живых платформы, а тут предлагают писать для обеих на языке умершей третьей. Ни айосеров, ни андроидоводов такое сильно не привлекает.
Ещё порой вызывали недовольство и то, что после появления новых фич в Android/iOS приходилось ждать их поддержки в Xamarin, и стоимость лицензии, и общее состояние экосистемы (документация, поддержка, библиотеки).
А тем временем компания Microsoft возлюбила опенсорс и сторонние платформы вроде Linux, так что идеи де Икасы оказались ей близки, и в итоге она купила Xamarin. Теперь его наработки вошли в .NET 5, и в прошлом году представили .NET MAUI (Multi-platform App UI) развитие тулкита Xamarin.Forms. В общем, не забросили купленное.
Что в итоге: пока всё остаётся в странноватом статусе, когда и масштабного взлёта не получилось, и полностью не прекращено.
Наконец, самое свежее. В 2013-м в Facebook выпустил React, ставший очень успешным во фронтенде, а в 2015-м за ним последовал React Native, приносящий подход в мобильную разработку.
Писать предлагается на JavaScript, поэтому кому-то может показаться, что это реинкарнация PhoneGap и его HTML-подхода, но нет. Когда-то Facebook действительно полагался для мобильных устройств на HTML, но ещё в 2012-м Цукерберг назвал это ошибкой. И у React Native идея не в том, чтобы с помощью HTML/CSS городить что хочешь, а в том, чтобы с помощью JSX использовать нативные элементы UI так что пользователь ощущал себя прямо как с нативным приложением.
Насколько это получилось? Шероховатости и срезанные углы существуют с ними сталкиваются и разработчики, и пользователи. Для кого-то они оказываются критичными: особенно нашумел пост Airbnb, где после React Native решили вернуться к нативной разработке. Но какие-то другие компании (вроде самой Facebook) эти ограничения не остановили, использование в индустрии есть.
Поскольку нативной отполированности не достичь, это решение часто рекомендуют для случаев, где она и не требуется. Если пользователь залипает в приложении часами (как в соцсетях), то любая неаккуратная мелочь будет бросаться ему в глаза но вот если юзкейс приложения такой, что его включают на минуту в неделю, то эти мелочи просто никто не заметит.
Если зайти на HeadHunter и сравнить число вакансий по React Native с нативной разработкой, то их немного. Но вот если сравнивать с Qt/Xamarin/PhoneGap то вакансий окажется больше, чем у них всех, вместе взятых, это наиболее успешный вариант.
Что в итоге: React Native не стал так успешен, как его фронтендовый старший брат, но определённую нишу занял.
Обычно в холиварах занимают крайние позиции: эта крутая штука всех победит или все эти глупости скоро отомрут. А у меня по итогам всех вариантов позиция получается сдержанной:
Победит и умрёт не единственные варианты. Есть ситуации, когда технология занимает небольшой сегмент рынка и не претендует на мировое господство, но приносит кому-то пользу.
Кроссплатформа это всегда компромисс. Выдержать планку качества, заданную нативом, не получалось нигде. Везде что-то вытарчивает и пользователи могут заметить разницу, и разработчикам приходится возиться со сложностями. И далеко не для всего кроссплатформа годится одинаково хорошо: если бизнес-логика между платформами шарится хорошо, то с UI всё сложнее.
Но качество ниже нативного не означает, что кроссплатформа плоха и не нужна. Кому-то на этот компромисс вполне стоит идти (например, стартапу, где вся жизнь один сплошной компромисс). В каждом проекте надо взвешивать свои за и против, и результаты в разных случаях будут различаться.
И всё это приводит меня к такому выводу про Flutter: вместо обсуждений выиграет он или проиграет надо обсуждать в каких конкретно юзкейсах он выигрывает. По-моему, он хорош для определённой ниши, поэтому вряд ли угрожает будущему всей нативной разработки, но закрепиться в этой нише вполне может. Вопрос в том, каковы её размеры и попадает ли в неё ваш проект.
А вот про Kotlin Multiplatform сделать выводы по прошлому у меня не получилось, потому что у него нетипичная ситуация, отличающаяся от предшественников. Во-первых, идея кроссплатформа годится не для всего тут заложена прямо в фундамент: а мы и не предлагаем объединять всё, реализуйте общую бизнес-логику, а остальное на Android и iOS делайте раздельно. А во-вторых, тут играет на руку родной для Android язык: он уже знаком половине мобильных разработчиков, и такого в кроссплатформе раньше не возникало. Так что опыт предыдущих технологий тут непоказателен остаётся смотреть, что покажет время.
На последнем Mobius было сразу несколько кроссплатформенных докладов (три про Flutter, один про Kotlin Multiplatform) мы уже выложили все видеозаписи конференции на YouTube, так что можете сами их посмотреть. А мы тем временем вовсю работаем над следующим Mobius: он пройдёт 13-16 апреля в онлайне, и там без освещения кроссплатформы тоже не обойдётся. Так что, если вас заинтересовал этот пост, то вам будет интересно и там.
Но AMP новый! <amp-img> делает гораздо больше, чем !
Привет, босс, давайте перепишем наш веб-сайт, чтобы он загружался быстрее!
Отвали!
Но исследования показывают, что каждая секунда загрузки
Я сказал, отвали!
Привет, босс, давайте перепишем наш сайт с помощью AMP. Это новая технология от Google ..."
Брось все! Вот возьми $$$
Это также может улучшить
Мне все равно. Начни это СЕЙЧАС!
Ниже речь пойдет о технологиях работы с голосом в вебе, таких как распознавание и синтез речи. В статье не будет примеров кода или сложного технического описания, моя цель показать вам возможности этих технологий и уровень их зрелости на примере простого приложения, которое было разработано в рамках изучения данной темы. Ознакомиться с кодом этого приложения вы можете на GitHub: https://github.com/mosharov/speech-api-example
Многие согласятся со мной, что адрес на картах и длинные
поисковые запросы в телефоне проще вводить голосом. Часто на наши
запросы отвечает голосовой помощник. Эти технологии используют
многие мобильные приложения, но часто ли вы встречаете подобное на
сайтах?
Выгода для пользователей мобильных девайсов очевидна, но не только
для них. Web Speech API повышает доступность веб-приложения для
людей с ограниченными возможностями и детей. В некоторых случаях
технология может быть полезна также пользователям десктопов,
например, в веб-чатах.
Попробуйте открыть ссылку на пример работы Web Speech API в вашем Google Chrome (о причинах, по которым я рекомендую именно этот браузер, расскажу позже). Страница, которую вы открыли, разделена на две части:
Ниже вы увидите, как страница преобразует вашу речь в текст.
Проверили? Отлично, переходим к мыслям о том, где это все можно применить в наших веб-приложениях.
Внедрить поддержку Speech API можно по всему приложению, но будут ли этим пользоваться? Для распознавания речи, например, хороший вариант поля для ввода большого количества текста.
В Северстали, несмотря на большое количество сервисов, мы смогли выделить лишь несколько из них, где распознавание речи было бы действительно полезно:
К сожалению, мы не нашли вариантов применения генерации речи, поэтому я буду очень благодарен за ваши идеи в комментариях =)
Выше, давая ссылку на страницу с примером, я рекомендовал открывать ее в Google Chrome. Дело в том, что данный пример отлично работает в Chrome на десктопах и сносно в мобильной версии, но другие браузеры не отличаются хорошей поддержкой этого API. Если генерация речи работает в основных браузерах, то поддержка распознавания речи крайне ограничена.
Подробнее о поддержке браузерами: https://caniuse.com/?search=speech
Так как демо сделано на коленке, оно не будет идеально работать (а может быть, и вообще не будет работать) с теми браузерами, которые поддерживают Web Speech API лишь частично или требуют для этого написания дополнительного кода. Попробовав запустить его в Firefox, Edge или Safari, вы поймете, что технология очень сырая, и использовать её как есть не самая хорошая идея.
Окей, мы решили внедрять технологию, но столкнулись с проблемами
ее поддержки, как быть?
Для начала стоить понять, будете ли вы использовать Web Speech API
в тех браузерах, где это работает, или полностью откажетесь от
него. Для себя мы решили, что внедрять API, которое имеет слабую
поддержку и может измениться не лучшая идея, поэтому начали поиски
альтернатив.
Pocketsphinx и Tensorflow. Если вы хотите, чтобы распознавание речи работало в ваших PWA (Progressive Web Application) приложениях оффлайн это отличный выбор, но могут возникнуть проблемы с распознаванием некоторых языков.
Облачные сервисы. API для работы с речью есть у многих облачных сервисов. При выборе этого варианта мы получаем прекрасное распознавание речи и абонентскую плату впридачу =)
Собственный API. Самый сложный и долгий путь, который требует дополнительных знаний и железа для развертывания быстрого и качественного сервиса.
Что и когда использовать решать вам, я бы рекомендовал начать с
облачных сервисов для быстрого старта ваших прекрасных
веб-приложений. Также, если вы захотите поиграть с Web Speech API,
вы можете использовать мой код в любых целях.
Спасибо.
Ссылки:
Web Speech API:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API
Демо приложение:
https://github.com/mosharov/speech-api-example