К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как выбрать директиву компиляции в 1С

Как выбрать директиву компиляции в 1С: НаКлиенте, НаСервере, НаСервереБезКонтекста. Критерии выбора, примеры кода, ошибки и проверка результата.

Директива компиляции в 1С определяет, где будет исполняться код процедуры или функции: на клиенте, на сервере или в обоих контекстах. От правильного выбора зависят производительность прикладного решения, объём сетевого трафика между клиентом и сервером и даже сама возможность выполнения кода — многие объекты (запросы, документы, регистры) недоступны в клиентском контексте тонкого и веб-клиента.

Ошибка в выборе директивы проявляется либо сразу — платформа выдаёт сообщение «Процедура или функция с указанным именем не определена» или «Переменная не определена (Запрос)», либо отложенно — форма начинает «тормозить» из-за десятков серверных вызовов при каждом действии пользователя. Поэтому выбирать директиву нужно осознанно, а не методом перебора до исчезновения ошибки.

В управляемых формах доступны директивы &НаКлиенте, &НаСервере, &НаСервереБезКонтекста, &НаКлиентеНаСервереБезКонтекста, а также &После, &Вместо, &ИзменениеИКонтроль для расширений. В модулях объектов, менеджеров и общих модулях директивы не указываются — контекст исполнения определяется настройками самого модуля.

Что означает каждая директива и где она исполняется

Прежде чем выбирать, нужно чётко понимать контексты исполнения. В клиент-серверной архитектуре 1С код формы физически разделён: часть выполняется на компьютере пользователя (тонкий, толстый или веб-клиент), часть — на сервере 1С:Предприятия, где находится доступ к базе данных.

Директива &НаКлиенте означает, что код исполняется на стороне клиента. Здесь доступны элементы формы, диалоги с пользователем, работа с файловой системой клиентского компьютера, OLE/COM-объекты (в толстом клиенте). Недоступны запросы, прямая работа с объектами базы (Документы.Реализация и т.п.), транзакции.

Директива &НаСервере исполняется на сервере в контексте формы: доступны реквизиты формы, её данные, преобразование через РеквизитФормыВЗначение(). Директива &НаСервереБезКонтекста тоже исполняется на сервере, но без передачи данных формы — только явно переданные параметры. Директива &НаКлиентеНаСервереБезКонтекста компилирует код в обоих контекстах, поэтому в ней можно использовать только пересечение возможностей — по сути, простые вычисления и работу со строками, датами, структурами.

Критерии выбора директивы: от чего отталкиваться

Первый и главный критерий — какие операции выполняет код. Если процедура обращается к базе данных (запросы, выборки, запись объектов, вызовы функций общих серверных модулей), она обязана быть серверной. Если код управляет видимостью элементов, открывает диалоги, работает с файлами на компьютере пользователя — он может быть только клиентским.

Второй критерий — нужен ли доступ к данным формы. Если серверному коду требуются реквизиты формы (например, прочитать табличную часть объекта, редактируемого в форме), используйте &НаСервере. Если достаточно передать два-три параметра (ссылку, дату, число) — выбирайте &НаСервереБезКонтекста: это дешевле, потому что платформа не сериализует и не пересылает всё состояние формы.

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

Маршрут решения

Как выбрать директиву

Код управляет элементами формы, показывает вопрос пользователю, работает с файламиИспользуйте &НаКлиенте
Нужен запрос к базе и доступ к реквизитам формы или её объектуИспользуйте &НаСервере
Нужен запрос к базе, достаточно передать параметры (ссылка, дата)Используйте &НаСервереБезКонтекста
Простая вспомогательная функция без обращения к даннымИспользуйте &НаКлиентеНаСервереБезКонтекста или общий модуль с флагами Клиент и Сервер
Код должен выполняться при записи объекта независимо от формыПеренесите логику в модуль объекта, директивы там не нужны

Типовые сценарии: какую директиву ставить

Обработчики событий формы

События вроде ПриИзменении поля ввода, НачалоВыбора, ОбработкаОповещения выполняются на клиенте — платформа сама предлагает &НаКлиенте при создании обработчика. Менять директиву такого обработчика на серверную нельзя: событие возникает в клиентском контексте.

События ПриСозданииНаСервере, ПриЧтенииНаСервере, ПередЗаписьюНаСервере по своим именам указывают контекст — они всегда &НаСервере. Это правильные места для первичного заполнения данных формы запросами и проверок перед записью.

Получение данных из базы по действию пользователя

Классический паттерн: пользователь выбрал номенклатуру, нужно подставить цену. Обработчик НоменклатураПриИзменении остаётся клиентским, а из него вызывается серверная функция:

&НаКлиенте

Процедура НоменклатураПриИзменении(Элемент)

Объект.Цена = ПолучитьЦену(Объект.Номенклатура, Объект.Дата);

КонецПроцедуры

&НаСервереБезКонтекста

Функция ПолучитьЦену(Номенклатура, Дата)

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ Цена ИЗ РегистрСведений.Цены.СрезПоследних(&Дата, Номенклатура = &Номенклатура)";

Запрос.УстановитьПараметр("Дата", Дата);

Запрос.УстановитьПараметр("Номенклатура", Номенклатура);

Выборка = Запрос.Выполнить().Выбрать();

Возврат ?(Выборка.Следующий(), Выборка.Цена, 0);

КонецФункции

Обратите внимание: функция объявлена без контекста, потому что ей достаточно двух параметров. Если бы потребовалось заполнить табличную часть редактируемого объекта, пришлось бы использовать &НаСервере и работать через РеквизитФормыВЗначение("Объект").

Вспомогательные функции без данных

Форматирование строки, расчёт суммы по переданной таблице значений, проверка корректности ИНН — такие функции не привязаны к контексту. Лучшее место для них — общий модуль с установленными флагами «Клиент (управляемое приложение)» и «Сервер». Внутри формы для таких функций подходит &НаКлиентеНаСервереБезКонтекста.

Производительность: почему «БезКонтекста» предпочтительнее

Каждый вызов процедуры &НаСервере заставляет платформу сериализовать состояние управляемой формы, передать его на сервер, восстановить там и после выполнения вернуть обратно. На больших формах с табличными частями в сотни строк это ощутимые затраты, особенно при работе через веб-клиент или медленный канал связи.

Вызов &НаСервереБезКонтекста передаёт только фактические параметры и возвращаемое значение. Поэтому стандарт разработки рекомендует по умолчанию использовать серверные процедуры без контекста, а контекстные применять только когда действительно нужны данные формы.

Сравнение серверных директив

&НаСервере

Доступ к реквизитам и данным формы, но передаёт всё состояние формы. Оправдана при работе с объектом формы целиком.

&НаСервереБезКонтекста

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

Общий серверный модуль

Логика вне формы, вызывается из любого места. Ограничение: данные формы нужно передавать параметрами; требует права на модуль.

Ещё одна техника экономии — кэширование на клиенте. Если обработчик запрашивает одни и те же данные (например, ставку НДС по организации), получите её один раз, сохраните в переменной модуля формы или структуре и переиспользуйте, вместо повторных серверных вызовов.

Проверка результата и типичные ошибки

После выбора директивы проверьте код синтаксическим контролем конфигуратора: «Конфигурация — Проверка конфигурации» с флагами проверки модулей для тонкого клиента, веб-клиента и сервера. Ошибки вида «Переменная не определена» в клиентском контексте — верный признак, что код обращается к серверным объектам и директива выбрана неверно.

Контрольные точки

Как проверить результат

Синтаксическая проверка модулей для всех режимов клиента
Норма

Ошибок компиляции нет ни для тонкого, ни для веб-клиента

Тревожный признак

Ошибки «Переменная не определена» или «Метод не обнаружен» в клиентском контексте

Поведение формы при работе пользователя
Норма

Один серверный вызов на осмысленное действие, форма откликается без пауз

Тревожный признак

Форма «замирает» при каждом изменении поля — избыточные серверные вызовы

Логика записи объекта
Норма

Проверки и пересчёты выполняются и при записи из формы, и программно

Тревожный признак

Логика срабатывает только из формы — она вынесена не туда, нужен модуль объекта

Частая ошибка — попытка вызвать из клиентской процедуры несуществующую серверную функцию без директивы, либо наоборот: обращение из серверного кода к элементам формы (Элементы.ТаблицаТоваров.Видимость). Элементы формы существуют только на клиенте; на сервере доступны лишь реквизиты и данные формы.

Типичные ошибки
01
Выполнение запроса в &НаКлиенте — код не скомпилируется для тонкого и веб-клиента, решение: вынести запрос в &НаСервереБезКонтекста
02
Серверный вызов в цикле по строкам таблицы — десятки обращений к серверу, решение: передать таблицу одним параметром и обработать на сервере за один вызов
03
Использование &НаСервере ради одной ссылки — передача всего состояния формы без необходимости, решение: &НаСервереБезКонтекста с параметром-ссылкой
04
Дублирование бизнес-логики в форме вместо модуля объекта — при программной записи документа проверки не сработают
05
Обращение к Элементы формы на сервере — ошибка исполнения, элементы доступны только в &НаКлиенте

Ограничения и когда директивы не помогут

Директивы работают только в модулях управляемых форм. В общих модулях контекст задаётся флагами свойств модуля («Сервер», «Клиент», «Внешнее соединение»), а директивы внутри них не применяются. В модулях объектов и менеджеров код всегда серверный.

Учитывайте, что асинхронная модель платформы влияет на клиентский код: работа с файлами и многие диалоги требуют обработчиков оповещений (НачатьПомещениеФайла, ПоказатьВопрос), и это не решается сменой директивы — меняется структура самого кода.

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

FAQ: частые вопросы о директивах компиляции

Можно ли вызвать клиентскую процедуру из серверной?

Нет, прямой вызов невозможен — сервер не знает о клиентском контексте. Серверный код может вернуть значение клиентской процедуре, которая его вызвала, и уже на клиенте выполнить дальнейшие действия. Для уведомления других открытых форм используется Оповестить() на клиенте после завершения серверного вызова.

Чем отличается &НаСервереБезКонтекста от общего серверного модуля?

По стоимости вызова они сопоставимы — оба не передают состояние формы. Разница в области видимости: функция в форме доступна только этой форме, а общий модуль переиспользуется из любых объектов конфигурации. Если логика нужна более чем в одной форме, правильнее вынести её в общий модуль.

Почему обработчик ПриИзменении нельзя сделать серверным?

Событие возникает в клиентском контексте при действии пользователя, и платформа требует клиентский обработчик. Серверную логику реализуют вызовом серверной процедуры из этого клиентского обработчика — это штатный и единственно корректный паттерн.

Что делать, если функция нужна и на клиенте, и на сервере, но использует недоступные на клиенте объекты?

Директива &НаКлиентеНаСервереБезКонтекста требует, чтобы код компилировался в обоих контекстах, поэтому запросы и работу с объектами базы в ней использовать нельзя. Разделите функцию: общую часть оставьте универсальной, а обращение к данным вынесите в отдельную серверную функцию.

Влияет ли директива на права доступа?

Сама директива права не меняет, но серверный код может выполняться в привилегированном режиме (УстановитьПривилегированныйРежим или привилегированный общий модуль), обходя ограничения RLS и прав на объекты. Поэтому серверные процедуры, возвращающие данные на клиент, нужно проектировать с учётом того, что клиент потенциально недоверенный.

Как найти избыточные серверные вызовы в готовой форме?

Включите технологический журнал на сервере или используйте замер производительности в конфигураторе (Отладка — Замер производительности). Частые вызовы серверных процедур формы из клиентских обработчиков будут видны как повторяющиеся события; оптимизируйте их объединением в один вызов или кэшированием результатов.

Итог

Что важно запомнить

Директиву выбирают по содержимому кода: интерфейс — &НаКлиенте, данные формы на сервере — &НаСервере, запросы по параметрам — &НаСервереБезКонтекста, универсальные функции — &НаКлиентеНаСервереБезКонтекста или общий модуль. Минимизируйте серверные вызовы, а критичную бизнес-логику храните в модуле объекта, а не в форме.