Область применения: управляемое приложение, мобильное приложение, обычное приложение.
1.1. Разработку управляемого приложения необходимо вести с контролем количества вызовов серверных процедур и функций из клиентского кода (серверных вызовов), а в некоторых случаях – также объема передаваемых данных между клиентом и сервером (трафика).
Общее количество серверных вызовов складывается из
В общем виде следует руководствоваться тем, что на каждое действие пользователя в клиентском коде конфигурации не должно выполнять дополнительных вызовов на сервер. Любые исключения из этого правила требуют дополнительного обоснования.
Типовым исключением является неявный серверный вызов при первом обращении к клиентскому общему модулю. Например, вызов ТорговыеПредложенияКлиент.ОбновитьПредложения приведет к вызову сервера, если общий модуль ТорговыеПредложенияКлиент еще не был скомпилирован.
1.2. Для режима с низкой скоростью соединения и мобильного клиента следует оптимизировать не только количество вызовов, но и объем передаваемых данных между клиентом и сервером (трафик). Отладку клиент-серверного взаимодействия в этом режиме работы рекомендуется проводить в режиме имитации задержек серверных вызовов.
Ниже рассмотрены типовые действия пользователя и даны рекомендации по организации клиент-серверного взаимодействия.
2.1. В простейшем случае, код конфигурации, выполняемый при запуске клиентского приложения, не должен приводить к обращениям на сервер. Речь идет о коде обработчиков ПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы модуля приложения. В тех случаях, когда все же в них необходимо получать данные с сервера:
В случае если параметры для запуска клиентского приложения требуется запрашивать с сервера из разных мест клиентского кода, следует разместить такую функцию в общем серверном модуле с повторным использованием возвращаемых значений. При первом вызове этой функции происходит одно обращение к серверу, после чего полученное значение автоматически кешируется платформой на клиенте для всех повторных вызовов этой функции.
Пример:
// Фрагмент общего серверного модуля ПараметрыПовтИсп с повторным использованием возвращаемых значений
Функция ПараметрыРаботыКлиента()
Параметры = Новый Структура();
Параметры.Вставить("ИнформационнаяБазаФайловая", ОбщегоНазначения.ИнформационнаяБазаФайловая());
// Инициализация других параметров, необходимых на клиенте при запуске приложения
// Параметры.Вставить(имя параметра, значение параметра);
Возврат Параметры;
КонецФункции
Пример клиентского кода обработчика ПриНачалеРаботыСистемы, использующего функцию ПараметрыРаботыКлиента:
ИнформационнаяБазаФайловая = ПараметрыПовтИсп.ПараметрыРаботыКлиента().ИнформационнаяБазаФайловая;
Если ИнформационнаяБазаФайловая Тогда
// обработка этого случая в клиентском коде
//…
2.2. При использовании в конфигурации Библиотеки стандартных подсистем (БСП) следует использовать предусмотренный в ней программный интерфейс:
Подробнее см. комментарии к указанным процедурам и функциям, а также примеры реализации в демонстрационной базе БСП.
См. также: Использование значений, влияющих на поведение клиентского приложения
Периодические серверные вызовы
3.1. В некоторых случаях требуется периодически передавать данные с клиента на сервер или выполнять периодические проверки состояния сервера, например:
Для этого следует объединять вызовы в один и делать его период по возможности не чаще одного раза в минуту. Кроме того, рекомендуется использовать систему взаимодействия для отправки оповещений клиентским сеансам с сервера вместо периодических вызовов с клиента для проверки состояния сервера.
3.2. При использовании в конфигурации Библиотеки стандартных подсистем (БСП) следует использовать предусмотренный в ней программный интерфейс.
3.2.1. Для длительных операций реализовать ожидание результата с помощью процедуры ОжидатьЗавершение общего модуля ДлительныеОперацииКлиент.
3.2.2. Для проверки состояния сервера, которое необходимо только для отдельно открытой формы без использования длительных операций, допустимо делать бесконтекстные (быстрые) серверные вызовы, однако следует выбирать как можно больший период вызова в рамках решаемой задачи. Например, обновление состояния в форме обмена данными о ходе работы регламентного задания.
Для форм на начальной странице (то есть открытых условно постоянно) не рекомендуется делать периодические серверные вызовы, а вместо этого рекомендуется сделать кнопку Обновить. Если же периодические вызовы необходимы, то делать их отключенными по умолчанию, а их период делать не чаще одного раза в минуту. Например, в форме Текущие дела.
3.2.3. Для периодической проверки состояния сервера и отправки оповещений клиентским сеансам:
3.2.4. Для периодической отправки данных с клиента на сервер:
Подробнее см. комментарии к указанным процедурам, а также примеры реализации в демонстрационной базе БСП.
Открытие управляемой формы
4.1. В случае если открытие формы выполняется из кода, следует открывать форму за один вызов с помощью метода глобального контекста ОткрытьФорму (при использовании версии платформы 1С:Предприятие 8.2 и более ранних версий - также ОткрытьФормуМодально). Для передачи параметров в форму следует использовать параметр этих методов Параметры.
4.2. При открытии формы не допускается выполнять обращений к серверу из кода модуля формы в обработчиках клиентских событий формы, таких как ПриОткрытии и ПриПовторномОткрытии. При необходимости обращения из них к серверным данным, следует предварительно размещать эти данные в реквизитах формы в ПриСозданииНаСервере.
Например, неправильно запускать фоновое задание с длительным расчетом в обработчике ПриОткрытии. Правильно, запускать его в ПриСозданииНаСервере, а стартовать проверку его готовности в ПриОткрытии. Подробнее см.: Длительные операции на сервере.
Другой пример, неправильно:
НастройкаПроксиСервера = СерверныйМодуль.НастройкаПроксиСервера();
ОткрытьФорму("ОбщаяФорма.ПараметрыПроксиСервера", Новый Структура("Настройка", НастройкаПроксиСервера));
правильно:
ОткрытьФорму("ОбщаяФорма.ПараметрыПроксиСервера");
при этом получение значения константы выполнять в обработчике ПриСозданииНаСервере формы ПараметрыПроксиСервера.
Также при открытии формы не следует вызывать серверные функции с повторным использованием возвращаемых значений, т.к. их результат кешируется только после первого вызова, а, следовательно, при первом открытии формы возникнет избыточное обращение на сервер.
4.3. Не следует при открытии формы подключать несколько различных обработчиков ожидания (с помощью метода ПодключитьОбработчикОжидания), которые проверяют готовность запущенных фоновых заданий и обновляют данные формы, например, выводят на форму различные результаты контроля или дополнительную информацию, подготовка которой занимает длительное время. Несколько обработчиков ожидания приводят к избыточным вызовам сервера.
Правильно объединять различные обработчики завершения фоновых заданий в один обработчик, который за один серверный вызов проверяет готовность и забирает результат с сервера. В общем случае, целесообразно также объединять фоновые задания в одно для экономии ресурсов сервера на их запуск. Если обработчик подключается периодически, то следует выбирать интервал срабатывания обработчика согласно рекомендациям в статье Длительные операции на сервере.
При использовании в конфигурации Библиотеки стандартных подсистем необходимо запускать длительные операции с помощью программного интерфейса в общих модулях ДлительныеОперации и ДлительныеОперацииКлиент. В нем уже предусмотрено объединение различных обработчиков завершения фоновых заданий в один с оптимальным интервалом срабатывания для экономии серверных вызовов.
5.1. Выполнение локальной команды формы должно приводить не более чем к одному вызову сервера.
Если команда выполняет только клиентские операции (приводит к открытию новой формы, устанавливает отбор в списке, меняет стиль оформление и пр.), то все необходимые данные для ее выполнение должны быть заранее переданы на клиент. Рекомендуется заранее готовить эти данные в обработчике события формы ПриСозданииНаСервере и размещать их в реквизитах формы.
Пример:
При выборе товаров из списка номенклатуры требуется запретить для пользователя выбор групп номенклатуры: при попытке выбрать группу программа должна выдавать останавливающее сообщение. Для проверки, является ли выбранный элемент номенклатуры группой, неправильно вызывать отдельную серверную функцию. Следует добавить в таблицу значений, которая связана с полем списка на форме, реквизит ЭтоГруппа и заполнять его в обработчике события формы ПриСозданииНаСервере. Тогда проверка на клиенте выполняется без дополнительного серверного вызова и имеет вид:
ТекущаяСтрока = Элемент.ТекущиеДанные;
Если ТекущаяСтрока.ЭтоГруппа Тогда
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = НСтр("ru = 'Выбор группы запрещен.'");
Сообщение.Сообщить();
Возврат;
КонецЕсли;
Если команда связана с выполнением бизнес-логики, которую возможно отработать только на сервере, то вся она должна выполняться за один серверный вызов.
5.2. В общем случае, при выборе из справочника допускается выполнять только один серверный вызов из кода, к которому приводит вызов метода глобального контекста ОткрытьФорму (или ОткрытьФормуМодально).
6.3. В случае если после выбора из справочника необходимо выполнить бизнес-логику, которую возможно отработать только на сервере, то допустимо выполнять ее за один дополнительный серверный вызов.
6.1. При выполнении глобальной команды допускается выполнять только один серверный вызов из кода. В случае если команда открывает форму, то этот вызов должен выполняться при вызове метода глобального контекста ОткрытьФорму (или ОткрытьФормуМодально).
7.1. При выполнении команды формирования отчета не допускается выполнять дополнительных серверных вызовов из кода конфигурации.
В частности, при открытии формы отчета не допускается выполнять обращений к серверу из кода модуля формы в обработчиках клиентских событий формы, таких как ПриОткрытии и ПриПовторномОткрытии.
Например, неправильно выполнять формирование отчета, использующего систему компоновки данных, из обработчика формы ПриОткрытии:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ВывестиОтчет();
КонецПроцедуры
&НаСервере
Процедура ВывестиОтчет()
// код по формированию отчета…
КонецПроцедуры
правильно:
8.1. Особенность клиент-серверного взаимодействия при выполнении подбора элементов состоит в необходимости передавать список выбранных элементов между формой объекта и формой подбора. При этом объем передаваемых данных может быть достаточно большим.
В этом случае не рекомендуется передавать потенциально большой массив данных в качестве параметра формы подбора. Потенциально большой массив данных, хранимый в форме подбора в параметре типа ДанныеФормыКоллекция, может оказаться на клиенте не полным за счет оптимизации работы управляемой формы. Как результат – для передачи параметра будет выполнено дополнительное "дочитывание" данных формы с сервера.
8.2. В целях оптимизации передачи данных между формой объекта и формой подбора рекомендуется использовать временное хранилище, чтение и запись которого должна выполняться на сервере.
Проиллюстрируем методику использования формы подбора на примере подбора элементов справочников Товары в табличную часть Товары документа РасходТовара. (Из демонстрационной конфигурации по платформе 1С:Предприятие).
Открытие формы подбора из клиентского кода должно приводить не более чем к двум обращениям на сервер. С этой целью локальная команда открытия формы подбора в модуле формы документа РасходТовара помещает список товаров из табличной части во временное хранилище (первый вызов) и открывает форму подбора (второй вызов), передавая адрес временного хранилища:
// в форме документа
&НаКлиенте
Процедура ОбработчикКомандыПодбора()
АдресТоваровВХранилище = ПоместитьТоварыВХранилище();
ПараметрыПодбора = Новый Структура();
ПараметрыПодбора.Вставить("АдресТоваровДокумента", АдресТоваровВХранилище);
ПараметрыПодбора.Вставить("ВидЦен ", Объект.ВидЦен);
ПараметрыПодбора.Вставить("Склад ", Объект.Склад);
ФормаПодбора = ОткрытьФорму("Документ.РасходТовара.Форма.ФормаПодбора", ПараметрыПодбора, Элементы.Товары );
КонецПроцедуры
// Функция помещает список товаров во временное хранилище и возвращает адрес
&НаСервере
Функция ПоместитьТоварыВХранилище()
Возврат ПоместитьВоВременноеХранилище(Объект.Товары.Выгрузить(,"Товар,Цена,Количество"), УникальныйИдентификатор);
КонецФункции
Форма подбора получает список выбранных товаров из временного хранилища в обработчике ПриСозданииНаСервере:
// в форме подбора
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) Экспорт
// Сохраним адрес временного хранилища, чтобы поместить в него обратно подобранные товары
АдресТоваровДокумента= Параметры.АдресТоваровДокумента;
Объект.Товары.Загрузить(ПолучитьИзВременногоХранилища(АдресТовара));
КонецПроцедуры
Закрытие формы подбора должно приводить не более чем к двум обращениям на сервер. При закрытии форма подбора помещает список выбранных товаров во временное хранилище (первый вызов):
&НаКлиенте
Процедура ОКВыполнить()
АдресТовараВоВременномХранилище = ЗаписатьПодборВХранилище();
ОповеститьОВыборе(АдресТовараВоВременномХранилище);
КонецПроцедуры
&НаСервере
Функция ЗаписатьПодборВХранилище()
Возврат ПоместитьВоВременноеХранилище(Товары.Выгрузить(), АдресТоваровДокумента);
КонецФункции
Затем форма документа восстанавливает список товаров из временного хранилища (второй вызов на сервер):
// Обработка выбора таблицы формы Товары
&НаКлиенте
Процедура ПриОбработкеПодобранныхТоваров(Элемент, АдресТоваровВХранилище, СтандартнаяОбработка) Экспорт
Если АдресТоваровВХранилище = Неопределено Тогда
Возврат;
КонецЕсли;
ПолучитьТоварыИзХранилища(АдресТоваровВХранилище);
КонецПроцедуры
&НаСервере
Процедура ПолучитьТоварыИзХранилища(АдресТоваровВХранилище)
ПодобранныеТовары = ПолучитьИзВременногоХранилища(АдресТоваровВХранилище);
Объект.Товары.Загрузить(ПодобранныеТовары);
УдалитьИзВременногоХранилища(АдресТоваровДокумента); // очищается временное хранилище для минимизации расхода оперативной памяти
КонецПроцедуры
8.3. При помещении данных во временное хранилище следует выбрать один из двух вариантов:
В противном случае при многократном повторении действия в форме, например, при многократном подборе товаров, это приводит к излишнему расходу оперативной памяти.
Рассмотрим пример предварительной инициализации временного хранилища для переиспользования:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
АдресТоваров = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор); // Инициализируется реквизит формы
КонецПроцедуры
&НаСервере
Функция ТоварыВоВременномХранилище()
Возврат ПоместитьВоВременноеХранилище(Товары.Выгрузить(), АдресТоваров);
КонецФункции
При переиспользовании временного хранилища не требуется удалять значение из временного хранилища:
// в форме документа
&НаСервере
Процедура ПолучитьТоварыИзХранилища(АдресТоваровВХранилище)
ПодобранныеТовары = ПолучитьИзВременногоХранилища(АдресТоваровВХранилище);
Объект.Товары.Загрузить(ПодобранныеТовары);
КонецПроцедуры
9. Вместо дополнительного вызова сервера в клиентских обработчиках ПередЗаписью и ПослеЗаписи модуля формы следует размещать серверную логику в аналогичных серверных обработчиках ПередЗаписьюНаСервере, ПриЗаписиНаСервере, ПослеЗаписиНаСервере, и передавать данные на клиент через параметр обработчиков ПараметрыЗаписи или реквизиты формы.
Например, неправильно:
&НаКлиенте
Процедура ПослеЗаписи
УправлениеФормой();
КонецПроцедуры
&НаСервере
Процедура УправлениеФормой()
…
правильно: вызывать процедуру УправлениеФормой в ПослеЗаписиНаСервере с передачей и проверкой параметра о последующем закрытии формы: если форма будет закрыта, управлять ей не имеет смысла.
10. Особого внимания требуют клиентские методы и свойства объектов платформы, которые приводят или могут приводить к серверному вызову:
Следует избегать многократного вызова таких методов и свойств объектов платформы из клиентского кода. Вместо этого их необходимо объединять в один серверный вызов: использовать одно временное хранилище или переносить логику в серверную процедуру, которая однократно вызывается с клиента.
Также не следует вызывать серверные функции с повторным использованием возвращаемых значений, т.к. их результат кешируется только после первого вызова, а следовательно, при нем возникнет избыточное обращение на сервер. Вместо этого следует применять пункт 4.2 для вызовов из модуля формы, либо п. 2.1 если вызов происходит не из формы.
11. Не следует передавать с клиента на сервер больше данных, чем нужно для выполнения задачи. Например, если для выполнения процедуры на сервере требуется только одно значение реквизита, то не следует делать вызов контекстным.
Например, неправильно:
&НаКлиенте
Процедура ПриАктивизацииСтрокиСпискаКонтрагентов()
ОбновитьПанельКонтактнойИнформацииСервер();
КонецПроцедуры
&НаСервере
Процедура ОбновитьПанельКонтактнойИнформацииСервер()
КонтактнаяИнформацияПанель.ОбновитьДанныеПанели(ЭтотОбъект, ТекущийКонтрагент);
КонецПроцедуры
правильно:
&НаКлиенте
Процедура ПриАктивизацииСтрокиСпискаКонтрагентов()
ОбновитьПанельКонтактнойИнформации();
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьПанельКонтактнойИнформации()
ДанныеПанели = ДанныеПанелиКонтактнойИнформации(ТекущийКонтрагент);
КонтактнаяИнформацияПанельКлиент.ЗаполнитьДанныеПанелиКИ(ЭтотОбъект, ДанныеПанели);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ДанныеПанелиКонтактнойИнформации(Контрагент)
Возврат КонтактнаяИнформацияПанель.ДанныеПанелиКонтактнойИнформации(Контрагент);
КонецФункции
12. Не рекомендуется возвращать значения параметров обратно с сервера на клиент, а вместо этого рекомендуется делать функцию, которая вернет требуемые данные. Все параметры в процедурах и функциях, которые вызываются с клиента на сервер рекомендуется указывать с префиксом Знач (тогда значение не будет возвращаться с клиента на сервер целиком, включая содержимое сложных типов – Структура, Массив и другие).
Это экспортные серверные процедуры, размещенные в общих модулях с признаком Вызов сервера, процедуры в модулях форм и команд с директивами компиляции &НаСервере, &НаСервереБезКонтекста или без директив (неявное определение &НаСервере).
Например:
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
НовоеОбсуждение = НовоеОбсуждение(ПараметрКоманды);
…
КонецПроцедуры
Неправильно:
&НаСервере
Функция НовоеОбсуждение(ПользовательСсылка)
…
Возврат ПолучитьНавигационнуюСсылку(Обсуждение.Идентификатор);
КонецПроцедуры
Правильно:
&НаСервере
Функция НовоеОбсуждение(Знач ПользовательСсылка)
…
Возврат ПолучитьНавигационнуюСсылку(Обсуждение.Идентификатор);
КонецПроцедуры