В этой статье мы опишем простое Progressive Web Application, которое можно будет установить на смартфон. Я не буду описывать создание по шагам, а лишь объясню общий результат. Пример самого приложения можно увидеть здесь.
Итак, чтобы наше PWA можно было установить на устройство, должны выполняться следующие правила:
- Должен быть указан файл манифеста в блоке HEAD страницы.
- В файле манифеста должны быть указаны как минимум: name, short_name, display, description, иконка.
- Должен быть описан Service Worker, который обрабатывает событие install и кэширует все ресурсы приложения.
- Приложение должно заинтересовать пользователя: пользователь должен провести на странице браузера с приложением какое-то время, потыкать экранные элементы и т. д.
При выполнении этих условий придёт событие 'beforeinstallprompt' в объект window.
HTML-страница:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!DOCTYPE html> <html> <head> <title>PWA installable</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="style.css"> <script type="text/javascript" src="script.js" defer></script> <link rel="manifest" href="manifest.json"> </head> <body> <div> <a href="https://urvanov.ru">https://urvanov.ru</a> <button class="add-button">Установить</button> </div> <p> Простой пример приложения PWA, которое можно установить на свой телефон. Откройте эту страницу браузером со своего смартфона, подождите немного. Должна появиться кнопка установки. После установки приложение можно будет запускать со смартфона даже при отсутствии интернета. Оно будет показывать эту же страничку, но уже без кнопки "Установить", правда. </p> </body> </html> |
Файл манифеста:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "background_color": "cyan", "description": "Простое приложение PWA, которое можно установить на свой смартфон", "display" : "standalone", "icons": [ { "src": "application.png", "sizes": "192x192", "type": "image/png" } ], "name": "PWA installable", "short_name": "PWA installable", "start_url": "https://urvanov.ru/projects/html-examples/pwa-installable/index.html" } |
В icons можно описать несколько иконок, а может, даже нужно, адаптировав их под разные разрешения. Эта иконка будет отображаться в качестве значка установленного приложения. Остальные поля, думаю, понятны по названию.
Файл с описаниями стилей (здесь нам он не особо важен, для самого PWA здесь ничего не нужно):
1 2 3 |
body { background-color : cyan; } |
Файл со скриптами, делающими доступной кнопку установки и отображающими диалоговое окно с предложением установить:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// https://urvanov.ru // Register service worker to control making site work offline if ('serviceWorker' in navigator) { navigator.serviceWorker .register('/projects/html-examples/pwa-installable/sw.js') .then((registration) => { console.log('Service worker registered:', registration); }); } // Code to handle install prompt on desktop let deferredPrompt; const addBtn = document.querySelector('.add-button'); addBtn.style.display = 'none'; window.addEventListener('beforeinstallprompt', (e) => { // Prevent Chrome 67 and earlier from automatically showing the prompt e.preventDefault(); // Stash the event so it can be triggered later. deferredPrompt = e; // Update UI to notify the user they can add to home screen addBtn.style.display = 'block'; addBtn.addEventListener('click', () => { // hide our user interface that shows our A2HS button addBtn.style.display = 'none'; // Show the prompt deferredPrompt.prompt(); // Wait for the user to respond to the prompt deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } deferredPrompt = null; }); }); }); |
Файл с Service Worker-ом, обрабатывающим события install и fetch. В install мы просто кэшируем страницы, а в fetch достаём их новые копии с сервера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
self.addEventListener('install', (e) => { e.waitUntil( caches.open('pwa-installable').then((cache) => cache.addAll([ '/projects/html-examples/pwa-installable/', '/projects/html-examples/pwa-installable/index.html', '/projects/html-examples/pwa-installable/script.js', '/projects/html-examples/pwa-installable/style.css', ])), ); }); self.addEventListener('fetch', (e) => { console.log(e.request.url); e.respondWith( caches.match(e.request).then((response) => response || fetch(e.request)), ); }); |