При разработке на платформе 1С 8.3 программисты часто сталкиваются с ситуацией, когда обработка события в строке табличной части (например, ПриИзменении номенклатуры) начинает работать медленно. Основная причина кроется в избыточных вызовах сервера и использовании тяжелых механизмов трансформации данных. Разберем на конкретном примере, как превратить «тяжелый» код в эффективное решение.
Рассмотрим типовую ситуацию. Пользователь меняет товар в строке, и нам нужно заполнить цену, остаток, единицу измерения и пересчитать сумму. В исходном варианте кода часто допускаются следующие ошибки:
&НаСервере. Каждый такой переход — это сетевой трафик и накладные расходы на инициализацию контекста.&НаСервере вместо &НаСервереБезКонтекста заставляет платформу передавать всё состояние формы (все реквизиты, элементы управления и данные) туда и обратно.Проанализируем классический «тяжелый» код:
&НаСервере
Процедура ПолучитьОстатки(ДанныеОбъекта)
// ... выполнение запроса ...
ЭлементТаблицы = РеквизитФормыВЗначение("Объект"); // Тяжелая операция!
ЭлементТаблицы.РасчитатьОстаток(ОстатокТовара);
ЗначениеВРеквизитФормы(ЭлементТаблицы,"Объект"); // Еще более тяжелая операция!
КонецПроцедуры
Первое правило оптимизации управляемых форм — один клиентский обработчик = один серверный вызов. Вместо того чтобы по очереди запрашивать цену, а затем остатки, мы должны собрать все необходимые параметры на клиенте, передать их на сервер за один раз, получить заполненную структуру данных и применить её к строке.
Рассмотрим, как подготовить данные на клиенте:
&НаКлиенте
Процедура ТоварыТоварПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
// Подготавливаем параметры для сервера
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("Товар", ТекущаяСтрока.Товар);
ПараметрыЗапроса.Вставить("Дата", Объект.Дата);
ПараметрыЗапроса.Вставить("ВидЦен", Объект.ВидЦен);
// Один вызов сервера вместо пяти
Результат = ПолучитьДанныеТовараНаСервере(ПараметрыЗапроса);
// Применяем данные к текущей строке на клиенте
ЗаполнитьЗначениеСвойств(ТекущаяСтрока, Результат);
ТекущаяСтрока.Сумма = ТекущаяСтрока.Количество * ТекущаяСтрока.Цена;
КонецПроцедуры
Выясним, зачем нам вообще РеквизитФормыВЗначение. Обычно его используют, чтобы вызвать экспортный метод из модуля объекта. Однако в 90% случаев логику этого метода можно реализовать прямо в процедуре формы или в общем модуле, работая с конкретной строкой. Это избавляет систему от необходимости гонять весь «Объект» через сериализацию.
Наилучшим вариантом будет использование &НаСервереБезКонтекста. В этом случае на сервер передаются только те данные, которые вы явно указали в параметрах. Это критически важно для работы через интернет или в облачных сервисах.
Разберем пример эффективной серверной функции:
&НаСервереБезКонтекста
Функция ПолучитьДанныеТовараНаСервере(Параметры)
Результат = Новый Структура("Цена, ЕдИзм, Остаток", 0, Неопределено, 0);
// Выполняем один запрос для получения всех данных сразу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Цена КАК Цена,
| Товары.ЕдИзм КАК ЕдИзм,
| ЕСТЬNULL(Запасы.КоличествоОстаток, 0) КАК Остаток
|ИЗ
| Справочник.Номенклатура КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Товар И ВидЦены = &ВидЦен) КАК Цены
| ПО (ИСТИНА)
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Номенклатура = &Товар) КАК Запасы
| ПО (ИСТИНА)
|ГДЕ
| Товары.Ссылка = &Товар";
Запрос.УстановитьПараметр("Товар", Параметры.Товар);
Запрос.УстановитьПараметр("Период", Параметры.Дата);
Запрос.УстановитьПараметр("ВидЦен", Параметры.ВидЦен);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ЗаполнитьЗначениеСвойств(Результат, Выборка);
КонецЕсли;
Возврат Результат;
КонецФункции
Проанализируем ситуацию, когда нам всё же нужен контекст формы (например, если расчет зависит от многих других реквизитов формы, которые неудобно собирать в структуру). В этом случае вместо перебора всей таблицы или использования РеквизитФормыВЗначение, следует использовать метод НайтиПоИдентификатору().
Рассмотрим, как правильно передать строку на сервер:
ТекущиеДанные.ПолучитьИдентификатор().Объект.Товары по этому ID.Пример реализации:
&НаКлиенте
Процедура ТоварыТоварПриИзменении(Элемент)
ID = Элементы.Товары.ТекущиеДанные.ПолучитьИдентификатор();
ОбработатьСтрокуНаСервере(ID);
КонецПроцедуры
&НаСервере
Процедура ОбработатьСтрокуНаСервере(ID)
// Находим строку в данных формы (контекст объекта доступен)
СтрокаТаблицы = Объект.Товары.НайтиПоИдентификатору(ID);
// Работаем напрямую со строкой
Если СтрокаТаблицы <> Неопределено Тогда
СтрокаТаблицы.Остаток = ПолучитьАктуальныйОстаток(СтрокаТаблицы.Товар);
КонецЕсли;
// Изменения автоматически отобразятся на клиенте по завершении вызова
КонецПроцедуры
Проанализировав вышеописанные методы, можно выделить ключевые правила «хорошего тона» при работе с управляемыми формами:
1. Объединяйте запросы к базе данных. Как показано в примере с ЛЕВОЕ СОЕДИНЕНИЕ, гораздо быстрее получить цену и остаток одним запросом к СУБД, чем двумя отдельными обращениями. Это снижает нагрузку на SQL-сервер и ускоряет работу кода.
2. Избегайте лишних обращений через точку. Помните, что код Стр.Товар.ЕдИзм на сервере — это скрытый запрос к базе данных. Если вам нужно несколько реквизитов номенклатуры, лучше один раз выбрать их запросом в структуру.
3. Кэшируйте данные. Если вы знаете, что пользователь будет часто выбирать одни и те же товары, можно использовать временное хранилище или повторное использование значений в общих модулях для хранения редко меняющихся данных (например, единиц измерения).
4. Используйте «ленивые» вычисления. Если получение остатков — очень долгая операция (например, расчет по многим складам), рассмотрите возможность вызова этой процедуры через ПодключитьОбработчикОжидания. Тогда форма не «зависнет» сразу после выбора товара, а данные подтянутся через долю секунды, когда интерфейс уже разблокирован.
Таким образом, отказ от РеквизитФормыВЗначение в пользу передачи данных через легкие структуры и минимизация контекстных серверных вызовов позволяют ускорить работу формы в несколько раз, обеспечивая комфортную работу пользователей даже в нагруженных системах.