Технология PWA в 1С - для чего и как использовать
23 октября 2020

Технология PWA в 1С - для чего и как использовать

Главный принцип нашей работы - постоянное развитие. Мы верим, что годные результаты может показать только тот, кто В платформе 1С версии 8.3.18 появилась поддержка технологии PWA (Progressive Web Apps - прогрессивные веб-приложения). В описании изменений объявили возможность вывода пиктограммы запуска приложения веб-клиента на рабочий стол и запуска веб-клиента в отдельном окне. На странице описания архитектуры платформы упоминается, что данная технология “позволяет создавать веб-приложения, которые выглядят как нативные приложения и работают почти так же быстро, как нативные приложения”.

И тут я задался вопросом. Неужели веб-клиент в новой платформе сможет работать так же быстро, как и тонкий клиент?

Сама технология PWA была создана в далёком 2007 году, но широкую известность получила только в 2015. На сегодняшний день PWA приложения умеют:

  • отправлять push-уведомления;
  • работать в оффлайн-режиме и активно использовать кэширование;
  • устанавливать ярлык на рабочий стол или стартовое меню.

Как мы видим, никакой магии в PWA нет. Единственное, за счёт чего PWA может увеличить скорость работы веб-клиента, - это агрессивное кэширование. Я решил попробовать проверить эту теорию.

Для начала надо разобраться, как устроена архитектура прогрессивного веб-приложения. В документации MDN web docs указано, что веб-приложение может называться PWA в том случае, если оно соответствует таким критериям:

  • работает по протоколу HTTPS
  • содержит файл манифеста
  • подключает один или несколько сервис-воркеров

По поводу первого первого пункта у меня вопросов нет. Если адрес публикации информационной базы начинается с https:// - критерий пройден. Манифест представляет собой файл формата JSON с набором атрибутов, описывающих свойства устанавливаемого приложения, такие как наименование, пиктограммы, версия и многие другие. Для указания ссылки на манифест используется конструкция вида

<link rel="manifest" href="/manifest.json">

в коде html страницы.

Если открыть код страницы приложения веб-клиента, то можно найти ссылку на манифест.


					<link id="id-manifest" rel="manifest" href="manifest.json?sysver=8.3.18.1128" crossorigin="use-credentials"/>
				

Сам манифест в платформе 8.3.18 имеет вид:

{
					"name": "Бухгалтерия предприятия, редакция 3.0",
					"short_name": "Бухгалтерия предприятия, редакция 3.0",
					"icons": [
					{
						"src": "e1csys/mngsrv/_PWA_L16.png",
						"sizes": "16x16",
						"type": "image/png"
					},
					...
					{
						"src": "e1csys/mngsrv/_PWA_L512.png",
						"sizes": "512x512",
						"type": "image/png"
					}
					],
					"start_url": "./",
					"display": "standalone",
					"background_color": "#FFFFFF",
					"theme_color": "#FFFFFF"
				}
			

Как мы видим, в нем указано наименование приложения, набор иконок с разрешением от 16 до 512 пикселей, стартовый адрес, режим запуска и цвета фона и темы.

Концепция последнего критерия может показаться немного непонятной, так как связана непосредственно с особенностями разработки веб-приложений и не имеет аналогов в экосистеме 1С. Сервис-воркер - это JavaScript-файл, который может контролировать веб-страницу/сайт, с которым он ассоциируется, перехватывать и модифицировать запросы навигации и ресурсов, очень гибко кешировать ресурсы, для того чтобы предоставить полный контроль над тем, как приложение ведет себя в определенных ситуациях (например, когда сеть не доступна).

Чтобы просмотреть зарегистрированные сервис-воркеры в Chrome, я иду в “Инструменты разработчика” на вкладке “Application - Service Workers”. Веб-клиент платформы 8.3.18 регистрирует единственный сервис воркер scripts/mod_sw_sw.js:

use strict';
				(this || self).g = {
				"goog.editor.defines.USE_CONTENTEDITABLE_IN_FIREFOX_3": !0
			};
			var a;
			a = self;
			a.addEventListener("install", function() {
			a.skipWaiting()
		});
		self.addEventListener("acti  vate", function() {
		a.clients.claim()
	});
	self.addEventListener("fetch", function() {});

В данном сервис-воркере все обработчики не выполняют никаких действий, которые могут повысить производительность веб-клиента. Таким образом, никакого увеличения производительности веб-клиента за счёт применения технологии PWA платформа 1С 8.3.18 не несет. Возможности модификации сервис-воркера средствами платформы не предусмотрено. Единственное, на что можно повлиять, - это манифест, который меняется через правку vrd файла или через параметры запуска.

Тогда я решил попробовать написать расширение, позволяющее использовать PWA на предыдущих версиях платформы. Наше расширение будет содержать в себе http-сервис для вывода компонентов PWA, таких как манифест, пиктограммы, сервис-воркер и страница установки PWA приложения. Также я добавлю ссылку на страницу установки в командный интерфейс. Я не могу модифицировать html-код веб-клиента, поэтому ссылку на манифест размещаю на отдельной странице установки PWA приложения. На этой же странице зарегистрирую сервис-воркер.

Готовлю 3 общих макета: pwa_144 с пиктограммой разрешением 144х144 пикселя (можно добавить и остальные разрешения, но это не обязательно), pwa_css с css-стилями страницы установки (я использовал готовый w3c.css) а также sw_js с сервис-воркером (в первой итерации я возьму за основу сервис-воркер из платформы 8.3.18). Далее создадаю http-сервис PWA_Installer с корневым адресом “pwa” и добавляю в него шаблоны url, обрабатывающие GET-запросы наших общих макетов:

Функция CSSGET(Запрос)
		Ответ = Новый HTTPСервисОтвет(200);
		ТекстCSS = ПолучитьОбщийМакет("pwa_css").ПолучитьТекст();
		Ответ.УстановитьТелоИзСтроки(ТекстCSS);
		Ответ.Заголовки.Вставить("content-type", "text/css");
		Возврат Ответ;
		КонецФункции

		Функция IconGET(Запрос)
		Ответ = Новый HTTPСервисОтвет(200);
		ДД = ПолучитьОбщийМакет("PWA_144");
		Ответ.УстановитьТелоИзДвоичныхДанных(ДД);
		Ответ.Заголовки.Вставить("content-type", "image/png");
		Возврат Ответ;
		КонецФункции

		Функция SWGET(Запрос)
			Ответ = Новый HTTPСервисОтвет(200);
		Ответ.Заголовки.Вставить("content-type", "application/javascript; charset=utf-8");

		АдресИБ = ОбщегоНазначения.АдресПубликацииИнформационнойБазыВИнтернете();
		СтруктураURI = ОбщегоНазначенияКлиентСервер.СтруктураURI(АдресИБ);

		ПутьНаСервере = СтруктураURI.ПутьНаСервере;
		Если НЕ СтрНачинаетсяС(ПутьНаСервере, "/") Тогда
		ПутьНаСервере = "/" + ПутьНаСервере;
		КонецЕсли;

		Ответ.Заголовки.Вставить("service-worker-allowed", ПутьНаСервере);
		Ответ.УстановитьТелоИзСтроки(ПолучитьОбщийМакет("sw_js").ПолучитьТекст());
		Возврат Ответ;
		КонецФункции
	

Для каждого общего макета устанавливается соответствующий заголовок content-type. Для пиктограммы - “image/png”, для css - “text/css”, для сервис-воркера - "application/javascript; charset=utf-8". Это позволяет браузеру корректно обработать полученные данные. Также для сервис-воркера устанавливается заголовок "service-worker-allowed", указывающий, для какого именно url разрешена работа данного воркера. Без этого заголовка сервис-воркер не будет работать на главной странице приложения, а только на странице http-сервиса.

Файл манифеста я буду формировать “на лету”:

Функция ManifestGET(Запрос)
		Ответ = Новый HTTPСервисОтвет(200);

		Структура = Новый Структура;
		Структура.Вставить("name", Метаданные.Синоним);
		Структура.Вставить("short_name", Метаданные.Имя);
		Структура.Вставить("start_url", "./../../"); // относительно hs/pwa/index.html
		Структура.Вставить("display", "standalone");
		Структура.Вставить("background_color", "#FFFFFF");
		Структура.Вставить("theme_color", "#FFFFFF");

		МассивИконок = Новый Массив;
		МассивИконок.Добавить(Новый Структура("src, sizes, type", "PWA_144.png", "144x144", "image/png"));
		Структура.Вставить("icons", МассивИконок);

		ЗаписьJSON = Новый ЗаписьJSON;
			ЗаписьJSON.УстановитьСтроку();
			
		ЗаписатьJSON(ЗаписьJSON, Структура);

		Ответ.Заголовки.Вставить("content-type", "application/json; charset=UTF-8");

		Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть());
			
		Возврат Ответ;
		КонецФункции
	

Страницу установки PWA приложения я хочу захардкодить:

Функция IndexGET(Запрос)
		Ответ = Новый HTTPСервисОтвет(200);
		Ответ.Заголовки.Вставить("content-type", "text/html; charset=utf-8");

		СтрокаШаблона = "<!DOCTYPE html>
		|<html>
		|<script>if('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js', {scope: '%SCOPE%'}); }</script>
		|<title>PWA</title>
		|<meta name=""viewport"" content=""width=device-width, initial-scale=1"">
		|<link rel=""stylesheet"" href=""w3c.css"">
		|<link rel=""manifest"" href=""manifest.json"" crossorigin=""use-credentials"">
		|<body>
		|<div class=""w3-container"">
		|  <h2>PWA</h2>
		|  <a href=""%АдресИБ%"">%АдресИБ%</a>
		|</div>
		|</body>
		|</html>";

		АдресИБ = ОбщегоНазначения.АдресПубликацииИнформационнойБазыВИнтернете();

		СтруктураURI = ОбщегоНазначенияКлиентСервер.СтруктураURI(АдресИБ);

		ПутьНаСервере = СтруктураURI.ПутьНаСервере;
		Если НЕ СтрНачинаетсяС(ПутьНаСервере, "/") Тогда
		ПутьНаСервере = "/" + ПутьНаСервере;
		КонецЕсли;

		СтрокаШаблона = СтрЗаменить(СтрокаШаблона, "%SCOPE%", ПутьНаСервере);
		СтрокаШаблона = СтрЗаменить(СтрокаШаблона, "%АдресИБ%", АдресИБ);

		Ответ.УстановитьТелоИзСтроки(СтрокаШаблона);

		Возврат Ответ;
		КонецФункции
	

При регистрации сервис-воркера я также указываю ему область видимости (SCOPE) равную странице приложения веб-клиента.

Последним штрихом добавляю ссылку на страницу установки, переопределив в расширении функцию “ИнтернетПоддержкаИСервисы_ПриСозданииНаСервере” общего модуля “ИнтеграцияПодсистемБИП”:

&После("ИнтернетПоддержкаИСервисы_ПриСозданииНаСервере")
		Процедура PWA_ИнтернетПоддержкаИСервисы_ПриСозданииНаСервере(Форма)

		АдресИБ = ОбщегоНазначения.АдресПубликацииИнформационнойБазыВИнтернете();

		Если НЕ ПустаяСтрока(АдресИБ) Тогда
		Декорация = Форма.Элементы.Добавить(
		"PWA",
		Тип("ДекорацияФормы"));
		Декорация.Заголовок = Новый ФорматированнаяСтрока(
		"Установить базу как прогрессивное веб приложение (progressive web app, PWA)",,,,
		АдресИБ + "/hs/pwa/index.html"
		);
		КонецЕсли;

		КонецПроцедуры
	

Применяю расширение, открываю в веб-клиенте “Интернет поддержку и сервисы” и нажимаю на ссылку “Установить как PWA…” или просто перехожу по адресу http-сервиса hs/pwa/index.html. На открывшейся странице вижу заветную кнопку “Установить”:

После установки ярлык приложения появится на рабочем столе, а само приложение откроется в отдельном окне.

Можно пойти немного дальше и добавить в сервис-воркер, например, поддержку оффлайн-режима. Конечно, я не буду делать полноценную работу без сети, но могу вывести культурное сообщение о временной недоступности сервиса. Добавляю еще один шаблон url в наш http-сервис:


		Функция OfflineGET(Запрос)
		Ответ = Новый HTTPСервисОтвет(200);
		Ответ.Заголовки.Вставить("content-type", "text/html; charset=utf-8");

		СтрокаШаблона = "<!DOCTYPE html>
		|<html>
		|<title>PWA</title>
		|<meta name=""viewport"" content=""width=device-width, initial-scale=1"">
		|<link rel=""stylesheet"" href=""w3c.css"">
		|<body>
		|<div class=""w3-container"">
		|  <h2>Сервис временно недоступен. Подробности по телефону +7-123-456-78-90</h2>
		|</div>
		|</body>
		|</html>";

		Ответ.УстановитьТелоИзСтроки(СтрокаШаблона);
		Возврат Ответ;
		КонецФункции
	

Также модифицирую код сервис-воркера:

'use strict';
		(this || self).g = {
		"goog.editor.defines.USE_CONTENTEDITABLE_IN_FIREFOX_3": !0
	};

	const ASSETS = [
	"offline.html",
	"w3c.css"
	];

	var a;
	a = self;

	let cache_name = "pwa_1c";

	a.addEventListener("install", event => {
	event.waitUntil(
	caches
	.open(cache_name)
	.then(cache => {
	return cache.addAll(ASSETS);
})
.catch(err => console.log(err))
);
});

self.addEventListener("activate", function() {
a.clients.claim()
});

self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request).catch(err =>
caches.open(cache_name).then(cache => cache.match("offline.html"))
)
);
});

При установке (событие install) сервис-воркер поместит в кэш страницу с сообщением о недоступности, а при ошибке получения данных из интернета он вернёт кэшированную страницу, и пользователи увидят сообщение “Сервис временно недоступен. Подробности по телефону +7-123-456-78-90”.

А тем кто дочитал - лови бонус! Я слепил и поделился) Пример расширения

Комментарии для сайта Cackle

Сервисы 1С для работы с маркетплейсами