Как из сайта сделать PWA приложение

Создаем своё 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. Кстати последнему прошу Вас уделить особое внимание.

Как из сайта сделать PWA приложение
Ваш сайт должен иметь адаптивную верстку. Проверить Ваш сайт на адаптивность рекомендую этим сервисом search.google.com/test/mobile-friendly.
Если с выше перечисленным у Вас всё в порядке, то давайте незамедлительно приступим к созданию своего собственного Progressive Web Application.

Приступаем к созданию PWA.

Но перед этим давайте замерим исходные показатели метрик браузерным плагином “LightHouse”

Как из сайта сделать PWA приложение

Подключите HTTPS протокол используя для этого платный или бесплатный сертификат SSL. Убедитесь, что на сайте отсутствует смешанный контент.
Создаем в графическом редакторе иконку, которая будет отображаться у нас в будущем как ярлык мобильного приложения на экране телефона. Берем размеры 512х512 (этот размер выбран не случайно, чтобы в будущем LightHouse не ругался) формат PNG. Рекомендуется сделать иконку красивой настолько, чтобы ее хотелось облизать. Далее создаем дубликаты этой иконки но уже в более меньших размерах специально для устройств на Android/iOS.
Добавляем в <head> строчки:

<link rel=»apple-touch-icon» sizes=»57×57″ href=»/ico/apple-icon-57×57.png»>
<link rel=»apple-touch-icon» sizes=»60×60″ href=»/ico/apple-icon-60×60.png»>
<link rel=»apple-touch-icon» sizes=»72×72″ href=»/ico/apple-icon-72×72.png»>
<link rel=»apple-touch-icon» sizes=»76×76″ href=»/ico/apple-icon-76×76.png»>
<link rel=»apple-touch-icon» sizes=»114×114″ href=»/ico/apple-icon-114×114.png»>
<link rel=»apple-touch-icon» sizes=»120×120″ href=»/ico/apple-icon-120×120.png»>
<link rel=»apple-touch-icon» sizes=»144×144″ href=»/ico/apple-icon-144×144.png»>
<link rel=»apple-touch-icon» sizes=»152×152″ href=»/ico/apple-icon-152×152.png»>
<link rel=»apple-touch-icon» sizes=»180×180″ href=»/ico/apple-icon-180×180.png»>
<link rel=»icon» type=»image/png» sizes=»192×192″ href=»/ico/android-icon-192×192.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-36×36.png»,
«sizes»: «36×36»,
«type»: «image/png»,
«density»: «0.75»
},
{
«src»: «/ico/android-icon-48×48.png»,
«sizes»: «48×48»,
«type»: «image/png»,
«density»: «1.0»
},
{
«src»: «/ico/android-icon-72×72.png»,
«sizes»: «72×72»,
«type»: «image/png»,
«density»: «1.5»
},
{
«src»: «/ico/android-icon-96×96.png»,
«sizes»: «96×96»,
«type»: «image/png»,
«density»: «2.0»
},
{
«src»: «/ico/android-icon-144×144.png»,
«sizes»: «144×144»,
«type»: «image/png»,
«density»: «3.0»
},
{
«src»: «/ico/android-icon-192×192.png»,
«sizes»: «192×192»,
«type»: «image/png»,
«density»: «4.0»
},
{
«src»: «/ico/android-icon-512×512.png»,
«sizes»: «512×512»,
«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-144×144.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:{redirectUrl:s},requireInteraction:!0})}return e}r=t.notification.title,n=t.notification.message,o=t.notification.icon,a=t.notification.tag,s=t.notification.url;return self.registration.showNotification(r,{body:n,icon:o,tag:a,data:{redirectUrl:s},requireInteraction:!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” и мы увидим, теперь некоторые показатели нашего сайта стали зелененькие

Модернизируем сайт в PWA

В заключении хочу сказать, что наличие зеленых индикаторов в количестве 5 штук будет являться одним из критериев качества сайта. Как минимум Google будет обращать на это внимание и учитывать как фактор ранжирования. По этому если Вы до сих пор не привели свои сайты в порядок, то учтите — Ваши клиенты скоро уйдут ко мне за услугами SEO

Роман Бондарь

Автор блога, SEO-специалист. Продвигаю сайты с 2011 года. Бригадир студии копирайтинга "Text-Hunter.ru" из 14 авторов. Практик эффективного крауд-маркетинга и нестандартного линкбилдинга. Ведущий эксперт поисковой оптимизации в компании IMarketing (Казахстан).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *


Срок проверки reCAPTCHA истек. Перезагрузите страницу.