Директивы компиляции 1С — это пометки &НаКлиенте, &НаСервере, &НаСервереБезКонтекста и &НаКлиентеНаСервереБезКонтекста перед процедурой или функцией. Они говорят платформе, в каком контексте метод будет скомпилирован и где выполнится. Инструкции препроцессора — конструкции с символом # (#Если ... Тогда, #Область, #Вставка) — работают раньше: они вырезают или оставляют куски текста модуля до компиляции в зависимости от среды выполнения.

Коротко: директива управляет методом целиком и решает, «куда отправить» выполнение; препроцессор управляет текстом и решает, «будет ли этот код вообще существовать» в данной среде. Путаница между ними даёт ошибки, которые проявляются только в одном клиенте или только при вызове с сервера. Ниже — как это устроено и где чаще всего ошибаются.

Директивы компиляции 1С: какие бывают

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

ДирективаГде выполняетсяДоступ к данным формыДоступ к базе
&НаКлиентеНа клиентеДа, к данным на клиентеНет, только через вызов сервера
&НаСервереНа сервереДа, форма передаётся на серверДа
&НаСервереБезКонтекстаНа сервереНетДа
&НаКлиентеНаСервереБезКонтекстаТам, откуда вызвалиНетТолько когда выполняется на сервере

Если директива не указана, метод модуля формы считается серверным. Я всегда пишу директиву явно: это требование стандартов и защита от неожиданного серверного вызова.

&НаКлиенте

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

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

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

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

1С
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)

	Объект.ДоговорКонтрагента = ОсновнойДоговорКонтрагента(Объект.Контрагент);

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

// Возвращает основной договор контрагента. Контекст формы не нужен.
&НаСервереБезКонтекста
Функция ОсновнойДоговорКонтрагента(Контрагент)

	Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Контрагент, "ОсновнойДоговор");

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

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

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

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

Директивы компиляции 1С и препроцессор: часть кода отсеивается до сборки, остальное распределяется между клиентом и сервером
Директивы компиляции 1С и препроцессор: часть кода отсеивается до сборки, остальное распределяется между клиентом и сервером

Инструкции препроцессора 1С

Препроцессор обрабатывает текст модуля до компиляции. Основные конструкции:

  • #Если ... Тогда / #ИначеЕсли / #Иначе / #КонецЕсли — условная компиляция. В условии используются символы среды: Сервер, Клиент, ТонкийКлиент, ВебКлиент, ТолстыйКлиентУправляемоеПриложение, ТолстыйКлиентОбычноеПриложение, ВнешнееСоединение, МобильныйКлиент и другие. Их можно комбинировать через И, Или, Не.
  • #Область / #КонецОбласти — группировка кода. На логику не влияет, нужна для навигации и стандартной структуры модуля.
  • #Вставка / #КонецВставки, #Удаление / #КонецУдаления — используются в расширениях внутри методов с аннотацией &ИзменениеИКонтроль: помечают добавленный и удалённый относительно типовой конфигурации код.

Пример условной компиляции в клиентском методе:

1С
&НаКлиенте
Процедура ОткрытьФайлОтчета(Команда)

	#Если ВебКлиент Тогда
		// В веб-клиенте нет прямого доступа к файловой системе
		ПоказатьПредупреждение(, НСтр("ru = 'Скачайте файл через браузер.'"));
	#Иначе
		ЗапуститьПриложение(ПутьКФайлу);
	#КонецЕсли

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

Стандартная обёртка модулей объектов и менеджеров

В типовых конфигурациях модули объектов и менеджеров обёрнуты так:

1С
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ПрограммныйИнтерфейс
// ...
#КонецОбласти

#Иначе
ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли

Зачем это нужно: в толстом клиенте модули объектов и менеджеров могут компилироваться на клиенте. Обёртка исключает серверный код из клиентской компиляции и даёт понятную ошибку вместо загадочной.

Директивы и препроцессор: сравнение

Директивы компиляции (&)Инструкции препроцессора (#)
Что охватываютОдин метод целикомЛюбой фрагмент текста: строки, методы, переменные
Где применяютсяМодули форм и командМодули любых видов
Когда работаютПри компиляцииДо компиляции
Что делаютОпределяют контекст и место выполненияВключают или удаляют код для конкретной среды
Ошибка выбораЛишние серверные вызовы, недоступные методыКод «исчезает» в одной из сред

Типичные ошибки с препроцессором

«Метод объекта не обнаружен»

Процедуру модуля менеджера обернули в #Если ТолстыйКлиентУправляемоеПриложение Тогда, а вызывают из серверного метода формы. Для серверной компиляции этого кода нет, платформа не видит экспортной процедуры.

1С
// Модуль менеджера — неправильно
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Процедура ПересчитатьСуммы(ДокументСсылка) Экспорт
	// ...
КонецПроцедуры
#КонецЕсли

// Правильно: код доступен на сервере
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
Процедура ПересчитатьСуммы(ДокументСсылка) Экспорт
	// ...
КонецПроцедуры
#КонецЕсли

Разрыв процедуры

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

Переменная объявлена только в одной ветке

1С
Процедура ЗаполнитьСообщения() Экспорт

	МассивСообщений = Новый Массив; // инициализация до условий

	#Если Сервер Тогда
		МассивСообщений.Добавить(НСтр("ru = 'Выполнено на сервере'"));
	#КонецЕсли

	ОбработатьСообщения(МассивСообщений);

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

Если создать массив внутри #Если Сервер, в клиентской компиляции переменная будет не определена. Инициализируйте переменные до условий или в каждой ветке.

Лишние директивы и вызовы сервера

Не препроцессор, но та же тема: цепочка &НаКлиенте → &НаСервере в цикле. Каждый вызов — сетевой обмен. Собирайте данные в один серверный вызов, а в модуле формы предпочитайте &НаСервереБезКонтекста.

Как проверять

  • Включите в проверке конфигурации все режимы, которые реально используются: тонкий клиент, веб-клиент, толстый клиент управляемого приложения, внешнее соединение, сервер.
  • Запускайте синтаксический контроль после правок условий #Если: ошибки в «невидимой» ветке часто проявляются только так.
  • На ревью кода отдельно смотрите на &НаСервере в формах: половину из них обычно можно заменить на &НаСервереБезКонтекста.
Директива отвечает на вопрос «где выполнить метод», препроцессор — «существует ли этот код здесь вообще». Держите эти вопросы раздельно, и большинство странных ошибок исчезнет.

Если нужен разбор существующего кода на лишние серверные вызовы и ошибки контекста, это входит в аудит 1С; новые доработки сразу пишу по стандартам — в рамках доработки 1С и разработки расширений.

Коротко

  • Директивы компиляции 1С (&) определяют контекст выполнения метода, инструкции препроцессора (#) вырезают код до компиляции.
  • В формах начинайте с &НаСервереБезКонтекста, &НаСервере — только когда нужны данные формы.
  • Модули объектов и менеджеров оборачивайте стандартным условием #Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение.
  • Блок #Если должен охватывать метод целиком, а переменные — инициализироваться до условий.
  • Проверяйте конфигурацию во всех используемых режимах запуска.