Как оптимизировать работу управляемой формы и ускорить обновление данных в строках при использовании РеквизитФормыВЗначение

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на платформе 1С 8.3 программисты часто сталкиваются с ситуацией, когда обработка события в строке табличной части (например, ПриИзменении номенклатуры) начинает работать медленно. Основная причина кроется в избыточных вызовах сервера и использовании тяжелых механизмов трансформации данных. Разберем на конкретном примере, как превратить «тяжелый» код в эффективное решение.

Анализ исходной проблемы: почему форма «тормозит»

Рассмотрим типовую ситуацию. Пользователь меняет товар в строке, и нам нужно заполнить цену, остаток, единицу измерения и пересчитать сумму. В исходном варианте кода часто допускаются следующие ошибки:

  1. Множественные серверные вызовы: на каждое действие (получить цену, получить остаток, получить реквизит) создается отдельный вызов &НаСервере. Каждый такой переход — это сетевой трафик и накладные расходы на инициализацию контекста.
  2. Использование РеквизитФормыВЗначение: этот метод полностью сериализует весь объект (например, весь документ со всеми его строками) из данных формы в прикладной объект. Если в документе 500 строк, система будет обрабатывать их все, даже если вы меняете только одну.
  3. Контекстные вызовы: использование &НаСервере вместо &НаСервереБезКонтекста заставляет платформу передавать всё состояние формы (все реквизиты, элементы управления и данные) туда и обратно.

Проанализируем классический «тяжелый» код:


&НаСервере
Процедура ПолучитьОстатки(ДанныеОбъекта)
    // ... выполнение запроса ...
    ЭлементТаблицы = РеквизитФормыВЗначение("Объект"); // Тяжелая операция!
    ЭлементТаблицы.РасчитатьОстаток(ОстатокТовара);
    ЗначениеВРеквизитФормы(ЭлементТаблицы,"Объект"); // Еще более тяжелая операция!
КонецПроцедуры

Решение №1: Объединение серверных вызовов

Первое правило оптимизации управляемых форм — один клиентский обработчик = один серверный вызов. Вместо того чтобы по очереди запрашивать цену, а затем остатки, мы должны собрать все необходимые параметры на клиенте, передать их на сервер за один раз, получить заполненную структуру данных и применить её к строке.

Рассмотрим, как подготовить данные на клиенте:


&НаКлиенте
Процедура ТоварыТоварПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
    
    // Подготавливаем параметры для сервера
    ПараметрыЗапроса = Новый Структура;
    ПараметрыЗапроса.Вставить("Товар", ТекущаяСтрока.Товар);
    ПараметрыЗапроса.Вставить("Дата", Объект.Дата);
    ПараметрыЗапроса.Вставить("ВидЦен", Объект.ВидЦен);
    
    // Один вызов сервера вместо пяти
    Результат = ПолучитьДанныеТовараНаСервере(ПараметрыЗапроса);
    
    // Применяем данные к текущей строке на клиенте
    ЗаполнитьЗначениеСвойств(ТекущаяСтрока, Результат);
    ТекущаяСтрока.Сумма = ТекущаяСтрока.Количество * ТекущаяСтрока.Цена;
КонецПроцедуры

Решение №2: Отказ от контекста и РеквизитФормыВЗначение

Выясним, зачем нам вообще РеквизитФормыВЗначение. Обычно его используют, чтобы вызвать экспортный метод из модуля объекта. Однако в 90% случаев логику этого метода можно реализовать прямо в процедуре формы или в общем модуле, работая с конкретной строкой. Это избавляет систему от необходимости гонять весь «Объект» через сериализацию.

Наилучшим вариантом будет использование &НаСервереБезКонтекста. В этом случае на сервер передаются только те данные, которые вы явно указали в параметрах. Это критически важно для работы через интернет или в облачных сервисах.

Разберем пример эффективной серверной функции:


&НаСервереБезКонтекста
Функция ПолучитьДанныеТовараНаСервере(Параметры)
    Результат = Новый Структура("Цена, ЕдИзм, Остаток", 0, Неопределено, 0);
    
    // Выполняем один запрос для получения всех данных сразу
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	Цены.Цена КАК Цена,
    |	Товары.ЕдИзм КАК ЕдИзм,
    |	ЕСТЬNULL(Запасы.КоличествоОстаток, 0) КАК Остаток
    |ИЗ
    |	Справочник.Номенклатура КАК Товары
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Товар И ВидЦены = &ВидЦен) КАК Цены
    |		ПО (ИСТИНА)
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Номенклатура = &Товар) КАК Запасы
    |		ПО (ИСТИНА)
    |ГДЕ
    |	Товары.Ссылка = &Товар";
    
    Запрос.УстановитьПараметр("Товар", Параметры.Товар);
    Запрос.УстановитьПараметр("Период", Параметры.Дата);
    Запрос.УстановитьПараметр("ВидЦен", Параметры.ВидЦен);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        ЗаполнитьЗначениеСвойств(Результат, Выборка);
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

Решение №3: Работа с идентификаторами строк

Проанализируем ситуацию, когда нам всё же нужен контекст формы (например, если расчет зависит от многих других реквизитов формы, которые неудобно собирать в структуру). В этом случае вместо перебора всей таблицы или использования РеквизитФормыВЗначение, следует использовать метод НайтиПоИдентификатору().

Рассмотрим, как правильно передать строку на сервер:

  1. На клиенте получаем идентификатор строки через ТекущиеДанные.ПолучитьИдентификатор().
  2. Передаем этот числовой идентификатор в серверную процедуру.
  3. На сервере находим конкретную строку в Объект.Товары по этому ID.

Пример реализации:


&НаКлиенте
Процедура ТоварыТоварПриИзменении(Элемент)
    ID = Элементы.Товары.ТекущиеДанные.ПолучитьИдентификатор();
    ОбработатьСтрокуНаСервере(ID);
КонецПроцедуры

&НаСервере
Процедура ОбработатьСтрокуНаСервере(ID)
    // Находим строку в данных формы (контекст объекта доступен)
    СтрокаТаблицы = Объект.Товары.НайтиПоИдентификатору(ID);
    
    // Работаем напрямую со строкой
    Если СтрокаТаблицы <> Неопределено Тогда
        СтрокаТаблицы.Остаток = ПолучитьАктуальныйОстаток(СтрокаТаблицы.Товар);
    КонецЕсли;
    // Изменения автоматически отобразятся на клиенте по завершении вызова
КонецПроцедуры

Важные рекомендации по оптимизации

Проанализировав вышеописанные методы, можно выделить ключевые правила «хорошего тона» при работе с управляемыми формами:

1. Объединяйте запросы к базе данных. Как показано в примере с ЛЕВОЕ СОЕДИНЕНИЕ, гораздо быстрее получить цену и остаток одним запросом к СУБД, чем двумя отдельными обращениями. Это снижает нагрузку на SQL-сервер и ускоряет работу кода.

2. Избегайте лишних обращений через точку. Помните, что код Стр.Товар.ЕдИзм на сервере — это скрытый запрос к базе данных. Если вам нужно несколько реквизитов номенклатуры, лучше один раз выбрать их запросом в структуру.

3. Кэшируйте данные. Если вы знаете, что пользователь будет часто выбирать одни и те же товары, можно использовать временное хранилище или повторное использование значений в общих модулях для хранения редко меняющихся данных (например, единиц измерения).

4. Используйте «ленивые» вычисления. Если получение остатков — очень долгая операция (например, расчет по многим складам), рассмотрите возможность вызова этой процедуры через ПодключитьОбработчикОжидания. Тогда форма не «зависнет» сразу после выбора товара, а данные подтянутся через долю секунды, когда интерфейс уже разблокирован.

Таким образом, отказ от РеквизитФормыВЗначение в пользу передачи данных через легкие структуры и минимизация контекстных серверных вызовов позволяют ускорить работу формы в несколько раз, обеспечивая комфортную работу пользователей даже в нагруженных системах.

← На главную