В процессе доработки конфигураций 1С, особенно на обычных формах (например, в "Комплексной автоматизации 1.1"), часто возникает задача добавить на форму множество элементов для удобства пользователей. Со временем количество таких доработок растет, и форма начинает открываться все медленнее. В этой статье разберем ситуацию, когда открытие карточки контрагента замедлилось в два раза из-за многочисленных процедур, управляющих видимостью элементов, и рассмотрим эффективные способы оптимизации.
Давайте проанализируем ситуацию, с которой столкнулся автор. При открытии формы элемента в процедуре ПриОткрытии() выполняется цикл, который для каждого из 40 сервисов вызывает процедуры, настраивающие видимость связанных с ним полей, надписей и флажков. Именно этот код, по результатам замера производительности, и является причиной "тормозов".
Рассмотрим фрагмент кода, который приводит к замедлению:
1. Основной цикл в процедуре ПриОткрытии
Этот код итерируется по таблице сервисов и динамически выполняет процедуры для каждого из них.
// ... кусок из ПриОткрытии
ЗаполнитьТаблицуСервисов;
Для Каждого Сервис Из тз_Сервисы Цикл
// ... пропущено для краткости
// У сервисов "ПФотч", "Бухфон", "СопровождениеWebСервисов" нет реквизита "СопровождаетКонкурент"
Если НЕ (Сервис.Значение = "ПФотч" ИЛИ Сервис.Значение = "Бухфон_" ИЛИ Сервис.Значение = "СопровождениеWebСервисов_" ИЛИ Сервис.Значение = "ВиженСофт_") Тогда
Попытка
Выполнить(Сервис.Значение + "СопровождаетКонкурентПриИзменении(Неопределено)");
Исключение;
КонецПопытки;
КонецЕсли;
// ... другие вызовы
Цикл;
2. Пример вызываемой процедуры
Для каждого сервиса выполняется объемная процедура, которая изменяет свойства видимости для множества элементов.
Процедура Диадок2_СопровождаетКонкурентПриИзменении(Элемент)
//если программу сопровождает конкурент становится видимым поле Конкурент,
//а не нужные для заполнения поля исчезают.
Если Диадок2_СопровождаетКонкурент = Истина Тогда
ЭлементыФормы.РамкаГруппы20.ЦветРамки = ЦветаСтиля.ЦветОсобогоТекста;
ЭлементыФормы.НадписьДиадок2_Конкурент.Видимость = истина;
ЭлементыФормы.Диадок2_Конкурент.Видимость = истина;
ЭлементыФормы.Диадок2_УслугаПредоставлена.Видимость = ложь;
ЭлементыФормы.Диадок2_ДатаОконДействСертиф.Видимость = ложь;
ЭлементыФормы.НадписьДиадок2_ДатаОконДействСертиф.Видимость = ложь;
// ... и еще много-много строк изменения видимости
Иначе
ЭлементыФормы.РамкаГруппы20.ЦветРамки = ЦветаСтиля.ТекстИнформационнойНадписи;
ЭлементыФормы.Диадок2_Конкурент.Видимость = ложь;
ЭлементыФормы.НадписьДиадок2_Конкурент.Видимость = ложь;
ЭлементыФормы.Диадок2_УслугаПредоставлена.Видимость = истина;
// ... и снова много строк
КонецЕсли;
КонецПроцедуры
Основные причины низкой производительности здесь:
ЭлементыФормы.ИмяЭлемента.Видимость = Ложь — это отдельная операция. Когда таких строк десятки, а процедура вызывается в цикле 40 раз, общее число операций становится огромным, что сильно нагружает механизм отрисовки формы.Выполнить(). Динамическое выполнение кода через процедуру Выполнить() — это мощный, но медленный механизм. Платформе требуется дополнительное время на синтаксический анализ и компиляцию передаваемой строки. В циклах это является классическим антипаттерном.Если Диадок2_СопровождаетКонкурент = Истина Тогда обращается к реквизиту объекта. Если данные этого объекта еще не были полностью считаны с сервера, каждое такое обращение внутри цикла может инициировать неявный запрос к базе данных, что катастрофически сказывается на производительности в клиент-серверном режиме.Теперь давайте разберем, как можно оптимизировать этот код.
Это самый простой и часто самый эффективный способ. Вместо того чтобы управлять видимостью каждого отдельного поля, надписи и флажка, их следует сгруппировать.
Как это сделать:
ГруппаФормы или Панель.Пример "до" и "после":
До оптимизации:
ЭлементыФормы.НадписьДиадок2_Конкурент.Видимость = истина;
ЭлементыФормы.Диадок2_Конкурент.Видимость = истина;
ЭлементыФормы.Диадок2_УслугаПредоставлена.Видимость = ложь;
ЭлементыФормы.Диадок2_ДатаОконДействСертиф.Видимость = ложь;
// и т.д.
После оптимизации:
Предположим, мы создали группу ГруппаДиадок2_Конкурент и ГруппаДиадок2_Основная.
Если Диадок2_СопровождаетКонкурент = Истина Тогда
ЭлементыФормы.ГруппаДиадок2_Конкурент.Видимость = Истина;
ЭлементыФормы.ГруппаДиадок2_Основная.Видимость = Ложь;
Иначе
ЭлементыФормы.ГруппаДиадок2_Конкурент.Видимость = Ложь;
ЭлементыФормы.ГруппаДиадок2_Основная.Видимость = Истина;
КонецЕсли;
В результате вместо десятков команд на изменение свойств элементов мы выполняем всего две. Это радикально сокращает время на перерисовку формы.
Как мы уже выяснили, обращения к реквизитам объекта внутри цикла могут вызывать неявные запросы к базе данных. Чтобы этого избежать, необходимо получить все нужные данные для всех 40 сервисов одним большим запросом до начала цикла.
Порядок действий:
СопровождаетКонкурент, Отключен и т.д.) для текущего контрагента. Скорее всего, эти данные хранятся в регистрах сведений или табличных частях.ТаблицуЗначений или Структуру.Такой подход гарантирует, что при настройке видимости элементов не будет никаких "сюрпризов" в виде незапланированных обращений к серверу. Вы полностью контролируете процесс получения данных.
Использование Выполнить() в цикле — плохая практика. Код становится нечитаемым, его сложно отлаживать и он медленно работает. Вместо этого следует использовать прямые вызовы процедур.
Вместо динамического формирования имени процедуры можно создать одну общую процедуру, которая будет принимать параметры, например, префикс имени элементов и значения флагов.
Пример рефакторинга:
// Вместо цикла с Выполнить()
Для Каждого Сервис Из тз_Сервисы Цикл
// Получаем значения флагов из заранее подготовленной структуры/таблицы
ЗначениеСопровождаетКонкурент = ДанныеСервисов[Сервис.Значение].СопровождаетКонкурент;
ЗначениеОтключен = ДанныеСервисов[Сервис.Значение].Отключен;
// Вызываем единую процедуру
НастроитьВидимостьГруппыСервиса(Сервис.Значение, ЗначениеСопровождаетКонкурент, ЗначениеОтключен);
Цикл;
// Новая универсальная процедура
Процедура НастроитьВидимостьГруппыСервиса(Префикс, СопровождаетКонкурент, Отключен)
// Здесь логика, которая использует Префикс для обращения к элементам
// Например, через коллекцию ЭлементыФормы по индексу
ЭлементыФормы[Префикс + "_Конкурент"].Видимость = СопровождаетКонкурент;
ЭлементыФормы["Надпись" + Префикс + "_Конкурент"].Видимость = СопровождаетКонкурент;
// и т.д.
КонецПроцедуры
Такой код работает быстрее, он более структурирован и его гораздо легче поддерживать в будущем.
Не забывайте, что главный инструмент для поиска узких мест — это "Замер производительности". Именно он помог автору темы локализовать проблему.
Совет: при анализе замера производительности попробуйте отключать флажок "Для вызова процедур и функций включать время выполнения". Это позволит увидеть, сколько времени тратится на сам вызов, а сколько — на выполнение кода внутри вызываемой процедуры. Это может дать дополнительную информацию для анализа.
В заключение, отметим, что оптимизация — это комплексная задача. Начните с группировки элементов на форме, затем оптимизируйте получение данных с сервера и приведите код в порядок, избавившись от медленных конструкций. Эти шаги помогут значительно ускорить открытие даже самых "тяжелых" и доработанных форм.