Создаем своё PWA [Progressive Web Application] = прогрессивное веб-приложение.
Предлагаю Вам рассмотреть создание прогрессивного веб-приложения на примере одного из моих лэндингов evroremont.kz. Я постараюсь написать солюшен максимально развернуто и понятно, при этом Я не буду уделять внимание для разъяснений технологий, которые будут употребляться (например HTTPS, JSON и т.д.) ибо не о них речь.
Что такое PWA? Прогрессивное веб-приложение (PWA) – это тот же самый web-сайт, но только максимально адаптирован под мобильные девайсы, по сути ведет себя так же как привычные для нас мобильные приложения. Его тоже можно добавить на главный экран телефона и получать PUSH-уведомления (если конечно Вы дали на это согласие как пользователь), работать в автономном режиме, когда у Вас частично или полностью отсутствует интернет.
Что необходимо для создания своего PWA?- Для начала нужно просто иметь сайт.
- Далее необходимо провести хотя бы базовую техническую оптимизацию сайта, чтобы он быстро загружался в браузере пользователя. В помощь для отслеживания метрик загрузки рекомендую использовать три моих самых любимых сервиса: webpagespeed.org, developers.google.com/speed/pagespeed/insights/, testmysite.withgoogle.com/intl/ru-ru. Кстати последнему прошу Вас уделить особое внимание.
- Ваш сайт должен иметь адаптивную верстку. Проверить Ваш сайт на адаптивность рекомендую этим сервисом search.google.com/test/mobile-friendly.
Если с выше перечисленным у Вас всё в порядке, то давайте незамедлительно приступим к созданию своего собственного Progressive Web Application.
Приступаем к созданию PWA. Но перед этим давайте замерим исходные показатели метрик браузерным плагином “LightHouse”
- Подключите HTTPS протокол используя для этого платный или бесплатный сертификат SSL. Убедитесь, что на сайте отсутствует смешанный контент.
- Создаем в графическом редакторе иконку, которая будет отображаться у нас в будущем как ярлык мобильного приложения на экране телефона. Берем размеры 512х512 (этот размер выбран не случайно, чтобы в будущем LightHouse не ругался) формат PNG. Рекомендуется сделать иконку красивой настолько, чтобы ее хотелось облизать. Далее создаем дубликаты этой иконки но уже в более меньших размерах специально для устройств на Android/iOS.
Добавляем в
<head> строчки:
<link rel="apple-touch-icon" sizes="57x57" href="/ico/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/ico/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/ico/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/ico/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/ico/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/ico/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/ico/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/ico/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/ico/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/ico/android-icon-192x192.png">
3. Создаем файл manifest.json. Его мы поместим в корневой каталог сайта, содержание файла будет следующее:
{
"name": "Евроремонт квартир и офисов под ключ",
"short_name": "Евроремонт",
"lang": "ru",
"start_url": "https://evroremont.kz/",
"theme_color": "#000000",
"background_color": "#000000",
"display": "standalone",
"icons": [
{
"src": "/ico/android-icon-36x36.png",
"sizes": "36x36",
"type": "image/png",
"density": "0.75"
},
{
"src": "/ico/android-icon-48x48.png",
"sizes": "48x48",
"type": "image/png",
"density": "1.0"
},
{
"src": "/ico/android-icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"density": "1.5"
},
{
"src": "/ico/android-icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"density": "2.0"
},
{
"src": "/ico/android-icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"density": "3.0"
},
{
"src": "/ico/android-icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"density": "4.0"
},
{
"src": "/ico/android-icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"density": "5.0"
}
]
}
Файл manifest.json является своего рода описанием нашего приложения, наличие которого просто необходимо. Кстати, как Вы видите все получившиеся иконки я положил в предварительно созданный каталог
/ico/.
Добавляем в
<head> строки:
<link rel="manifest" href="manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/ico/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<link rel="alternate" hreflang="ru-kz" href="https://evroremont.kz/" />
В этих строках мы подключаем непосредственно к документу сам файл манифеста, задаем цвет фона при загрузке, дефолтную иконку, и язык целевой аудитории.
4. Далее создаем не менее важный файл manup.js или качаем его отсюда github.com/boyofgreen/manUp.js/ уже готовый и добавляем в корневой каталог. При этом не забываем добавить в <head> строку:
<script src="manup.js"></script>
Этот файл не менее важен чем манифест, но отвечает за некоторые триггеры AJAX скриптов.
5. Теперь нужно создать скрипт pwabuilder-sw.js, который позволит нашему веб-приложению работать в автономном режиме, когда на телефоне отсутствует интернет. Для этого мы создаем файл в корневом каталоге со следующим содержимым:
self.addEventListener('install', function(event) {
var indexPage = new Request('index.html');
event.waitUntil(
fetch(indexPage).then(function(response) {
return caches.open('pwabuilder-offline').then(function(cache) {
console.log('[PWA Builder] Cached index page during Install'+ response.url);
return cache.put(indexPage, response);
});
}));
});
self.addEventListener('fetch', function(event) {
var updateCache = function(request){
return caches.open('pwabuilder-offline').then(function (cache) {
return fetch(request).then(function (response) {
console.log('[PWA Builder] add page to offline'+response.url)
return cache.put(request, response);
});
});
};
event.waitUntil(updateCache(event.request));
event.respondWith(
fetch(event.request).catch(function(error) {
console.log( '[PWA Builder] Network request Failed. Serving content from cache: ' + error );
return caches.open('pwabuilder-offline').then(function (cache) {
return cache.match(event.request).then(function (matching) {
var report = !matching || matching.status == 404?Promise.reject('no-match'): matching;
return report
});
});
})
);
})
P.S: Даже после того когда кэш браузера в телефоне был очищен, приложение всё равно успешно открывалось и работало. Конечно если на сайте подключены внешние скрипты (например iframe ютуба) то их функционал работать не будет.
- Далее нужно синхронизировать данный скрипт со своей страницей, для этого Я поместил в <head> следующий скрипт:
<script>
if (navigator.serviceWorker.controller) {
console.log('[PWA Builder] active service worker found, no need to register')
} else {
navigator.serviceWorker.register('pwabuilder-sw.js', {
scope: './'
}).then(function(reg) {
console.log('Service worker has been registered for scope:'+ reg.scope);
});
}
</script>
- Теперь пора нам настроить PUSH-уведомления. Для этого я воспользовался первым попавшимся сервисом, который бесплатно предлагает эту услугу (например: SendPulse). Сначала я добавил скрипт в <head>:
<script charset="UTF-8" src="//cdn.sendpulse.com/js/push/6add0b973d2190c6ee7065f686369fc9_1.js" async></script>
Далее создал файл
sp-push-manifest.json с содержимым:
{
"display": "standalone",
"gcm_sender_id": "300013155679",
"gcm_user_visible_only": true
}
Следующий созданный мной файл
sp-push-worker.js с содержимым:
"use strict";var sServerApi="https://pushdata.sendpulse.com:4434",gcmServer="https://android.googleapis.com/gcm/send/",sFirefoxServer="https://updates.push.services.mozilla.com/push/",sFirefoxServer2="https://updates.push.services.mozilla.com/wpush/v1/";function endpointWorkaround(t){return~t.indexOf(gcmServer)?t.split(gcmServer)[1]:~t.indexOf(sFirefoxServer)?t.split(sFirefoxServer)[1]:~t.indexOf(sFirefoxServer2)?t.split(sFirefoxServer2)[1]:t}self.addEventListener("install",function(t){t.waitUntil(self.skipWaiting())}),self.addEventListener("push",function(t){var c=!1;t.data&&(c=t.data.json()),t.waitUntil(self.registration.pushManager.getSubscription(
).then(function(t){var e=null;if(e=endpointWorkaround(e="subscriptionId"in t?t.subscriptionId:t.endpoint),c){var i=Base64.decode(c.message.title),r=Base64.decode(c.message.message),n=c.message.
icon,o=c.message.tag,a=Base64.decode(c.message.url),s={action:"statisctic",subscriptionId:e,statisctic_action:"delivered",task_id:c.message.task_id,user_id:c.message.user_id};return fetch(sServerApi,{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}).then(function(t){var e={body:r,icon:n,tag:o,data:{redirectUrl:a},requireInteraction:!0};return void 0!==c.message.buttons&&(e.actions=c.message.buttons,e.data.buttons=c.message.buttons),void 0!==c.message.image&&(e.image=c.message.image),self.registration.showNotification(i,e)})}return fetch(sServerApi+"/message/"+e).then(function(t){if(200!==t.status)throw new Error;return t.json().then(function(t){if(t.error||!t.notification)throw new Error;if(void 0!==t.notifications){for(var e=!1,i=0;i<t.notifications.length;i++){var r=t.notifications[i].title,n=t.notifications[i].message,o=t.notifications[i].icon,a=t.notifications[i].tag,s=t.notifications[i].url;e=self.registration.showNotification(r,{body:n,icon:o,tag:a+i,data:{r
edirectUrl:s},requireInteraction:!0})}return e}r=t.notification.title,n=t.notification.message,o=t.notification.icon,a=t.noti
fication.tag,s=t.notification.url;return self.registration.showNotification(r,{body:n,icon:o,tag:a,data:{redirectUrl:s},r
equireInteraction:!0})})})}))}),self.addEventListener("notificationclick",function(t){var r=t.notification.data.redirectUrl;if(void 0!==t.action){var e=t.action;if(void 0!==t.notification.data.buttons)for(var i=0;i<t.notification.data.buttons.length;i++)t.notification.data.buttons[i].action==e&&(r=t.notification.data.buttons[i].url)}t.notification.close(),t.waitUntil(clients.matchAll({type:"window"}).then(function(t){for(var e=0;e<t.length;e++){var i=t[e];if("/"==i.url&&"focus"in i)return i.focus()}if(clients.openWindow)return clients.openWindow(r)}))});var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(t){var e,i,r,n,o,a,s,c="",d=0;for(t=Base64._utf8_encode(t);d<t.length;)n=(e=t.charCodeAt(d++))>>2,o=(3&e)<<4|(i=t.charCodeAt(d++))>>4,a=(15&i)<<2|(r=t.charCodeAt(d++))>>6,s=63&r,isNaN(i)?a=s=64:isNaN(r)&&(s=64),c=c+this._keyStr.charAt(n)+this._keyStr.charAt(o)+this._keyStr.charAt
(a)+this._keyStr.charAt(s);return c},decode:function(t){var e,i,r,n,o,a,s="",c=0;for(t=t.replace(/[^A-Za-z0-9\+\/\=]/g,"");c<t.length;)e=this._keyStr.indexOf(t.charAt(c++))<<2|(n=this._keyStr.indexOf(t.charAt(c++)))>>4,i=(15&n)<<4|(o=this._keyStr.indexOf(t.charAt(c++)))>>2,r=(3&o)<<6|(a=this._keyStr.indexOf(t.charAt(c++))),s+=String.fromCharCode(e),64!=o&&(s+=String.fromCharCode(i)),64!=a&&(s+=String.fromCharCode(r));return s=Base64._utf8_decode(s)},_utf8_encode:function(t){t=t.replace(/\r\n/g,"\n");for(var e="",i=0;i<t.length;i++){var r=t.charCodeAt(i);r<128?e+=String.fromCharCode(r):(127<r&&r<2048?e+=String.fromCharCode(r>>6|192):(e+=String.fromCharCode(r>>12|224),e+=String.fromCharCode(r>>6&63|128)),e+=String.fromCharCode(63&r|128))}return e},_utf8_decode:function(t){for(var e="",i=0,r=0,n=0,o=0;i<t.length;)(r=t.charCodeAt(i))<128?(e+=String.fromCharCode(r),i++):191<r&&r<224?(n=t.charCodeAt(i+1),e+=String.fromCharCode((31&r)<<6|63&n),i+=2):(n=t.charCodeAt(i+1),o=t.charCodeAt(i+2),e+=String.fromCharCode((15&r)<<12|(63&n)<<6|63&o),i+=3);return e}};
Создание этих двух файлов было необходимо, один для идентификации сайта системой sendpulse, другой для корректной работы PUSH у пользователей использующие браузеры на движке Хромиум.
- Теперь Вы можете зайти на свой сайт через смартфон и добавить его к себе на экран на ряду с другими значками Ваших мобильных приложений, лично у меня на Андройде Яндекс-Браузера это делалось так: Настройки -> Добавить сайт на экран.
- Профит! Теперь у нас красивая иконка нашего прогрессивного веб-приложения на экране при клике на которую мы попадаем на наш сайт (даже если отсутствует интернет). Проверить результаты своей работы можно все тем же встроенным плагином для браузера “LightHouse” и мы увидим, теперь некоторые показатели нашего сайта стали зелененькие J
В заключении хочу сказать, что наличие зеленых индикаторов в количестве 5 штук будет являться одним из критериев качества сайта. Как минимум Google будет обращать на это внимание и учитывать как фактор ранжирования. По этому если Вы до сих пор не привели свои сайты в порядок, то учтите - Ваши клиенты скоро уйдут ко мне за услугами SEO :D
На этом у меня всё! Желаю удачных Вам экспериментов!
Если статья была интересна, не забывайте говорить "спасибо" в мою карму, ибо Вам воздастся!При поддержке:
Eternalhost.net - первый хостинг с единоразовой оплатой.
Обсудить на форуме.