Разработка и Конфигурация · 12 мин
Как выбрать директиву компиляции в 1С
Как выбрать директиву компиляции в 1С: НаКлиенте, НаСервере, НаСервереБезКонтекста. Критерии выбора, примеры кода, ошибки и проверка результата.
Директива компиляции в 1С определяет, где будет исполняться код процедуры или функции: на клиенте, на сервере или в обоих контекстах. От правильного выбора зависят производительность прикладного решения, объём сетевого трафика между клиентом и сервером и даже сама возможность выполнения кода — многие объекты (запросы, документы, регистры) недоступны в клиентском контексте тонкого и веб-клиента.
Ошибка в выборе директивы проявляется либо сразу — платформа выдаёт сообщение «Процедура или функция с указанным именем не определена» или «Переменная не определена (Запрос)», либо отложенно — форма начинает «тормозить» из-за десятков серверных вызовов при каждом действии пользователя. Поэтому выбирать директиву нужно осознанно, а не методом перебора до исчезновения ошибки.
В управляемых формах доступны директивы &НаКлиенте, &НаСервере, &НаСервереБезКонтекста, &НаКлиентеНаСервереБезКонтекста, а также &После, &Вместо, &ИзменениеИКонтроль для расширений. В модулях объектов, менеджеров и общих модулях директивы не указываются — контекст исполнения определяется настройками самого модуля.
Что означает каждая директива и где она исполняется
Прежде чем выбирать, нужно чётко понимать контексты исполнения. В клиент-серверной архитектуре 1С код формы физически разделён: часть выполняется на компьютере пользователя (тонкий, толстый или веб-клиент), часть — на сервере 1С:Предприятия, где находится доступ к базе данных.
Директива &НаКлиенте означает, что код исполняется на стороне клиента. Здесь доступны элементы формы, диалоги с пользователем, работа с файловой системой клиентского компьютера, OLE/COM-объекты (в толстом клиенте). Недоступны запросы, прямая работа с объектами базы (Документы.Реализация и т.п.), транзакции.
Директива &НаСервере исполняется на сервере в контексте формы: доступны реквизиты формы, её данные, преобразование через РеквизитФормыВЗначение(). Директива &НаСервереБезКонтекста тоже исполняется на сервере, но без передачи данных формы — только явно переданные параметры. Директива &НаКлиентеНаСервереБезКонтекста компилирует код в обоих контекстах, поэтому в ней можно использовать только пересечение возможностей — по сути, простые вычисления и работу со строками, датами, структурами.
Критерии выбора директивы: от чего отталкиваться
Первый и главный критерий — какие операции выполняет код. Если процедура обращается к базе данных (запросы, выборки, запись объектов, вызовы функций общих серверных модулей), она обязана быть серверной. Если код управляет видимостью элементов, открывает диалоги, работает с файлами на компьютере пользователя — он может быть только клиентским.
Второй критерий — нужен ли доступ к данным формы. Если серверному коду требуются реквизиты формы (например, прочитать табличную часть объекта, редактируемого в форме), используйте &НаСервере. Если достаточно передать два-три параметра (ссылку, дату, число) — выбирайте &НаСервереБезКонтекста: это дешевле, потому что платформа не сериализует и не пересылает всё состояние формы.
Третий критерий — частота вызова. Код, который дёргается на каждое изменение поля ввода, не должен при каждом срабатывании идти на сервер. По возможности реализуйте проверки на клиенте, а серверные вызовы группируйте: один вызов, возвращающий структуру с несколькими значениями, вместо пяти отдельных функций.
Как выбрать директиву
Типовые сценарии: какую директиву ставить
Обработчики событий формы
События вроде ПриИзменении поля ввода, НачалоВыбора, ОбработкаОповещения выполняются на клиенте — платформа сама предлагает &НаКлиенте при создании обработчика. Менять директиву такого обработчика на серверную нельзя: событие возникает в клиентском контексте.
События ПриСозданииНаСервере, ПриЧтенииНаСервере, ПередЗаписьюНаСервере по своим именам указывают контекст — они всегда &НаСервере. Это правильные места для первичного заполнения данных формы запросами и проверок перед записью.
Получение данных из базы по действию пользователя
Классический паттерн: пользователь выбрал номенклатуру, нужно подставить цену. Обработчик НоменклатураПриИзменении остаётся клиентским, а из него вызывается серверная функция:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
Объект.Цена = ПолучитьЦену(Объект.Номенклатура, Объект.Дата);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьЦену(Номенклатура, Дата)
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Цена ИЗ РегистрСведений.Цены.СрезПоследних(&Дата, Номенклатура = &Номенклатура)";
Запрос.УстановитьПараметр("Дата", Дата);
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Возврат ?(Выборка.Следующий(), Выборка.Цена, 0);
КонецФункции
Обратите внимание: функция объявлена без контекста, потому что ей достаточно двух параметров. Если бы потребовалось заполнить табличную часть редактируемого объекта, пришлось бы использовать &НаСервере и работать через РеквизитФормыВЗначение("Объект").
Вспомогательные функции без данных
Форматирование строки, расчёт суммы по переданной таблице значений, проверка корректности ИНН — такие функции не привязаны к контексту. Лучшее место для них — общий модуль с установленными флагами «Клиент (управляемое приложение)» и «Сервер». Внутри формы для таких функций подходит &НаКлиентеНаСервереБезКонтекста.
Производительность: почему «БезКонтекста» предпочтительнее
Каждый вызов процедуры &НаСервере заставляет платформу сериализовать состояние управляемой формы, передать его на сервер, восстановить там и после выполнения вернуть обратно. На больших формах с табличными частями в сотни строк это ощутимые затраты, особенно при работе через веб-клиент или медленный канал связи.
Вызов &НаСервереБезКонтекста передаёт только фактические параметры и возвращаемое значение. Поэтому стандарт разработки рекомендует по умолчанию использовать серверные процедуры без контекста, а контекстные применять только когда действительно нужны данные формы.
Сравнение серверных директив
Доступ к реквизитам и данным формы, но передаёт всё состояние формы. Оправдана при работе с объектом формы целиком.
Передаёт только параметры, быстрее и дешевле. Ограничение: нет доступа к реквизитам формы, всё нужное передаётся явно.
Логика вне формы, вызывается из любого места. Ограничение: данные формы нужно передавать параметрами; требует права на модуль.
Ещё одна техника экономии — кэширование на клиенте. Если обработчик запрашивает одни и те же данные (например, ставку НДС по организации), получите её один раз, сохраните в переменной модуля формы или структуре и переиспользуйте, вместо повторных серверных вызовов.
Проверка результата и типичные ошибки
После выбора директивы проверьте код синтаксическим контролем конфигуратора: «Конфигурация — Проверка конфигурации» с флагами проверки модулей для тонкого клиента, веб-клиента и сервера. Ошибки вида «Переменная не определена» в клиентском контексте — верный признак, что код обращается к серверным объектам и директива выбрана неверно.
Как проверить результат
Ошибок компиляции нет ни для тонкого, ни для веб-клиента
Ошибки «Переменная не определена» или «Метод не обнаружен» в клиентском контексте
Один серверный вызов на осмысленное действие, форма откликается без пауз
Форма «замирает» при каждом изменении поля — избыточные серверные вызовы
Проверки и пересчёты выполняются и при записи из формы, и программно
Логика срабатывает только из формы — она вынесена не туда, нужен модуль объекта
Частая ошибка — попытка вызвать из клиентской процедуры несуществующую серверную функцию без директивы, либо наоборот: обращение из серверного кода к элементам формы (Элементы.ТаблицаТоваров.Видимость). Элементы формы существуют только на клиенте; на сервере доступны лишь реквизиты и данные формы.
Ограничения и когда директивы не помогут
Директивы работают только в модулях управляемых форм. В общих модулях контекст задаётся флагами свойств модуля («Сервер», «Клиент», «Внешнее соединение»), а директивы внутри них не применяются. В модулях объектов и менеджеров код всегда серверный.
Учитывайте, что асинхронная модель платформы влияет на клиентский код: работа с файлами и многие диалоги требуют обработчиков оповещений (НачатьПомещениеФайла, ПоказатьВопрос), и это не решается сменой директивы — меняется структура самого кода.
Также помните про безопасность: серверные общие модули с выгрузкой данных должны иметь флаг «Привилегированный» только при реальной необходимости, а клиент не должен получать от сервера больше данных, чем нужно для отображения.
FAQ: частые вопросы о директивах компиляции
Можно ли вызвать клиентскую процедуру из серверной?
Нет, прямой вызов невозможен — сервер не знает о клиентском контексте. Серверный код может вернуть значение клиентской процедуре, которая его вызвала, и уже на клиенте выполнить дальнейшие действия. Для уведомления других открытых форм используется Оповестить() на клиенте после завершения серверного вызова.
Чем отличается &НаСервереБезКонтекста от общего серверного модуля?
По стоимости вызова они сопоставимы — оба не передают состояние формы. Разница в области видимости: функция в форме доступна только этой форме, а общий модуль переиспользуется из любых объектов конфигурации. Если логика нужна более чем в одной форме, правильнее вынести её в общий модуль.
Почему обработчик ПриИзменении нельзя сделать серверным?
Событие возникает в клиентском контексте при действии пользователя, и платформа требует клиентский обработчик. Серверную логику реализуют вызовом серверной процедуры из этого клиентского обработчика — это штатный и единственно корректный паттерн.
Что делать, если функция нужна и на клиенте, и на сервере, но использует недоступные на клиенте объекты?
Директива &НаКлиентеНаСервереБезКонтекста требует, чтобы код компилировался в обоих контекстах, поэтому запросы и работу с объектами базы в ней использовать нельзя. Разделите функцию: общую часть оставьте универсальной, а обращение к данным вынесите в отдельную серверную функцию.
Влияет ли директива на права доступа?
Сама директива права не меняет, но серверный код может выполняться в привилегированном режиме (УстановитьПривилегированныйРежим или привилегированный общий модуль), обходя ограничения RLS и прав на объекты. Поэтому серверные процедуры, возвращающие данные на клиент, нужно проектировать с учётом того, что клиент потенциально недоверенный.
Как найти избыточные серверные вызовы в готовой форме?
Включите технологический журнал на сервере или используйте замер производительности в конфигураторе (Отладка — Замер производительности). Частые вызовы серверных процедур формы из клиентских обработчиков будут видны как повторяющиеся события; оптимизируйте их объединением в один вызов или кэшированием результатов.
Что важно запомнить
Директиву выбирают по содержимому кода: интерфейс — &НаКлиенте, данные формы на сервере — &НаСервере, запросы по параметрам — &НаСервереБезКонтекста, универсальные функции — &НаКлиентеНаСервереБезКонтекста или общий модуль. Минимизируйте серверные вызовы, а критичную бизнес-логику храните в модуле объекта, а не в форме.