Глобальный, модульный и серверный контекст в 1С 8.3 на примерах
Содержание
Контекст в 1С — это набор переменных, процедур, свойств и объектов, которые доступны коду в данной точке программы. Если имя есть в контексте, его можно вызвать без указания модуля. Если нет — платформа выдаст Переменная не определена или Процедура или функция с указанным именем не определена.
В 8.3 у слова два измерения. Первое — область видимости: глобальный, модульный и локальный контекст. Второе — где выполняется код: на клиенте или на сервере, с данными формы или без них. Почти все «непонятные» ошибки начинающих разработчиков связаны со вторым.
Контекст простыми словами: что видит строка кода
Представьте, что каждая строка кода стоит в комнате. В комнате лежат её локальные переменные. Дверь ведёт в коридор — это модуль с его переменными и процедурами. Коридор выходит в общий холл — глобальный контекст, доступный всем. Строка видит всё, что лежит в её комнате, в коридоре и в холле. В чужие комнаты она не заглядывает.
Добавьте к этому второе здание — сервер. Клиент и сервер работают в разных процессах, часто на разных компьютерах. Переменная, которая живёт на клиенте, на сервере не существует, и наоборот. Чтобы передать что-то между зданиями, нужен вызов сервера и параметры.
Пользователи встречают слово «контекст» и в другом смысле: контекстное меню (правая кнопка мыши) и контекстная подсказка в Конфигураторе (Ctrl+Пробел). Подсказка показывает ровно то, что доступно в текущем контексте, — удобный способ проверить, видит ли код нужное имя.
Три уровня видимости в 1С: глобальный, модульный, локальный
| Уровень | Что входит | Где объявляется | Сколько живёт |
|---|---|---|---|
| Глобальный | Свойства и методы платформы (Сообщить, ТекущаяДата, Метаданные), менеджеры (Справочники, Документы — на сервере), общие модули, экспортные методы глобальных общих модулей | Платформа и конфигурация | Весь сеанс |
| Модульный | Переменные модуля (Перем ... ; в начале), все процедуры и функции модуля, свойства объекта-владельца | Модуль объекта, формы, менеджера, общий модуль | Пока жив объект-владелец |
| Локальный | Параметры процедуры и переменные внутри неё | Внутри процедуры или функции | Один вызов |
Глобальный контекст
Сюда входит всё, что можно вызвать из любого места без префикса. Методы платформы, перечисленные в синтакс-помощнике в разделе «Глобальный контекст». Имена общих модулей: ОбщегоНазначения.ЗначениеРеквизитаОбъекта(...). А если у общего модуля стоит флажок «Глобальный», его экспортные методы вызываются вообще без имени модуля.
К глобальному клиентскому контексту относятся и экспортные переменные модуля управляемого приложения. Их видит любой клиентский код конфигурации.
Глобальные общие модули компилируются при старте сеанса. Чем их больше, тем дольше запуск. Для новых модулей флажок «Глобальный» обычно не ставят: вызов ИмяМодуля.Метод() читается понятнее и не тормозит старт.
Модульный контекст
Каждый модуль формирует свой контекст. В модуле объекта это реквизиты и табличные части объекта, а также ЭтотОбъект. В модуле формы — реквизиты формы, элементы (Элементы.Поле), основной реквизит Объект и сама форма через ЭтаФорма или ЭтотОбъект.
Переменные модуля объявляются в самом начале, до процедур:
&НаКлиенте
Перем СчетчикНажатий; // живёт, пока открыта форма
&НаКлиенте
Процедура КнопкаНажатие(Команда)
СчетчикНажатий = ?(СчетчикНажатий = Неопределено, 1, СчетчикНажатий + 1);
КонецПроцедуры
Локальный контекст
Параметры и переменные внутри процедуры. Они создаются при входе и исчезают при выходе. Переменную внутри процедуры можно не объявлять через Перем — она появляется при первом присваивании.
Как контексты взаимодействуют и кто кого видит
Когда платформа встречает имя, она ищет его снизу вверх: сначала в локальном контексте процедуры, затем в модуле, затем в глобальном контексте. Поэтому локальная переменная с тем же именем, что и переменная модуля, перекрывает её. Это частый источник тихих ошибок: значение «пропадает», хотя код выглядит верным.
Чтобы чужой модуль увидел процедуру, нужно ключевое слово Экспорт:
- 📦 экспортная процедура общего модуля доступна как
ИмяМодуля.Процедура(); - 📄 экспортная процедура модуля объекта — как метод объекта:
ДокументОбъект.ЗаполнитьПоОснованию(); - 🗂 экспортная переменная модуля объекта — как свойство объекта;
- 🪟 экспортная процедура модуля формы — как метод формы, полученной через
ПолучитьФорму()илиОткрытьФорму().
Без Экспорт процедура остаётся частью модульного контекста и снаружи не видна. Это не ограничение, а инструмент: служебные процедуры прячут, наружу выставляют только программный интерфейс.
Контекст выполнения: клиент, сервер и директивы компиляции
В управляемом приложении каждая процедура формы и команды помечается директивой. Директива говорит платформе, где выполнять код и что ему будет доступно.
| Директива | Где выполняется | Данные формы | Что доступно |
|---|---|---|---|
&НаКлиенте | На клиенте | Есть | Элементы формы, клиентские методы, диалоги, работа с файлами клиента |
&НаСервере | На сервере | Есть, передаются с клиента | Реквизиты формы, РеквизитФормыВЗначение, запросы, менеджеры объектов |
&НаСервереБезКонтекста | На сервере | Нет | Только параметры, запросы, менеджеры, общие серверные модули |
&НаКлиентеНаСервереБезКонтекста | Там, откуда вызвана | Нет | Только то, что есть и на клиенте, и на сервере |
Для общих модулей директивы заменяют флажки в свойствах модуля: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера», «Внешнее соединение», «Привилегированный». Серверный модуль без флажка «Вызов сервера» с клиента напрямую не вызвать — нужен посредник с этим флажком.
Внутри одного модуля можно разделять код инструкциями препроцессора #Если Сервер Тогда ... #КонецЕсли. Это не то же самое, что директивы компиляции: инструкция отсекает фрагмент текста при компиляции под конкретную сторону, а директива назначает место выполнения целой процедуре.
Что можно вызывать из чего
- ✅ клиент → сервер (с контекстом и без) — это и есть вызов сервера;
- ✅ сервер с контекстом → сервер без контекста;
- ❌ сервер без контекста → процедура
&НаСерверетой же формы: у неё нет формы, с которой работать; - ❌ сервер → клиент: обратного вызова нет, результат возвращают через возвращаемое значение.
Вызов сервера с контекстом и без: что уходит по сети
При вызове &НаСервере платформа упаковывает данные формы, отправляет их на сервер, восстанавливает форму там, выполняет процедуру и возвращает изменённые данные обратно. Если на форме табличная часть на 5 000 строк, она поедет туда и обратно ради одной строки кода.
&НаСервереБезКонтекста передаёт только параметры. Поэтому совет практиков простой: если серверной процедуре не нужны реквизиты формы, делайте её без контекста и передавайте нужное параметрами.
Медленно — вся форма уходит на сервер:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
ЗаполнитьЦенуНаСервере();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьЦенуНаСервере()
Объект.Цена = ЦенаНаДату(Объект.Номенклатура, Объект.Дата);
КонецПроцедуры
Быстро — по сети идут две ссылки и число:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
Объект.Цена = ЦенаНаДату(Объект.Номенклатура, Объект.Дата);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЦенаНаДату(Номенклатура, Дата)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура = &Номенклатура) КАК Цены";
Запрос.УстановитьПараметр("Дата", Дата);
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Возврат ?(Выборка.Следующий(), Выборка.Цена, 0);
КонецФункции
Имя регистра и его измерения в примере условные — подставьте свои из конфигурации.
Переменные модуля формы на сервере не сохраняются
Клиентская переменная &НаКлиенте Перем живёт, пока открыта форма. Серверная переменная модуля формы инициализируется заново при каждом вызове сервера. Положили в неё значение в одном вызове — в следующем там снова Неопределено. Хранить данные между вызовами нужно в реквизитах формы или во временном хранилище (ПоместитьВоВременноеХранилище).
Ещё два приёма для экономии вызовов
- 🔁 Общий модуль с повторным использованием возвращаемых значений («На время сеанса» или «На время вызова») кэширует результаты. Повторный вызов с теми же параметрами не идёт на сервер. Подходит для настроек и констант, которые редко меняются.
- 🏷
ПредопределенноеЗначение("Перечисление.СтавкиНДС.НДС20")работает на клиенте. Ради сравнения с предопределённым значением ходить на сервер не нужно.
Где контекст встречается на практике
| Ситуация | Какой контекст | На что смотреть |
|---|---|---|
Обработчик формы ПриСозданииНаСервере | Сервер с контекстом формы | Можно читать Параметры и заполнять реквизиты |
Модуль объекта ПередЗаписью | Сервер, контекст объекта | ЭтотОбъект, реквизиты, ОбменДанными.Загрузка |
| Фоновое задание | Отдельный серверный сеанс | Нет формы и клиентских переменных; всё передаётся параметрами |
| Регламентное задание | Серверный сеанс от имени указанного пользователя | Нет интерактивных методов, Сообщить пользователь не увидит |
| Внешнее соединение (COM) | Контекст «Внешнее соединение» | Доступны только модули с этим флажком |
| HTTP-сервис, веб-сервис | Сервер без формы | Данные — из запроса, ответ — через объект ответа |
| Расширение конфигурации | Контекст расширяемого модуля | &Перед, &После, &Вместо видят переменные исходного модуля |
Фоновые задания — классическая ловушка. Код, который работал в форме, переносят в фон и получают ошибки: в фоне нет данных формы, а клиентские методы недоступны. Результат возвращают через временное хранилище, адрес которого передают параметром.
В регламентных и фоновых заданиях также не работают диалоги вроде Вопрос() и ПоказатьПредупреждение(). Сообщения пишут в журнал регистрации через ЗаписьЖурналаРегистрации().
Ошибки, связанные с контекстом, и что с ними делать
Отдельно про производительность. Формально ошибкой это не считается, но вызов &НаСервере в обработчике ПриИзменении поля табличной части замедляет ввод на каждой строке. Если форма «подвисает» при наборе, первым делом проверьте директивы обработчиков.
Отладка: как увидеть, в каком контексте выполняется код
Остановитесь в нужной точке
Поставьте точку останова (F9) и запустите отладку. Когда выполнение остановится, откройте «Вычислить выражение» (Shift+F9) и введите имя переменной. Если в ответ пришла ошибка, имени нет в текущем контексте — значит, вы на другой стороне или в другом модуле.
Посмотрите стек вызовов
Окно стека вызовов (Ctrl+Alt+C) показывает цепочку процедур, которая привела к текущей строке. По нему видно, где клиентский код передал управление серверу. Типичная находка — цепочка из трёх-четырёх лишних переходов на сервер, которые можно схлопнуть в один.
Если серверные точки останова не срабатывают
В файловом варианте серверный код отлаживается сразу. В клиент-серверном варианте сервер 1С нужно запустить с поддержкой отладки: агент сервера ragent стартует с ключом -debug. Обычно ключ дописывают в параметры запуска службы агента.
Перезапуск службы агента сервера обрывает все сеансы пользователей этого кластера. Делайте это в нерабочее время или на тестовом сервере. На рабочем сервере режим отладки без нужды не держите — он добавляет нагрузку.
После перезапуска в Конфигураторе откройте Отладка → Подключение и убедитесь, что нужные предметы отладки (клиент, сервер, фоновое задание) подключены. Для фоновых заданий включите автоматическое подключение к ним в настройках там же.
Замер производительности
Режим замера производительности в отладчике показывает, сколько времени заняла каждая строка. Если на строке вызова серверной процедуры висит большая доля времени при простом коде внутри, причина обычно в объёме данных формы, которые уходят на сервер. Это сигнал перевести процедуру в &НаСервереБезКонтекста.