webview и узел app: превращаем сайт в настольное приложение
Всё, что дали предыдущие сорок девять глав, — это «сайт»: открыть браузер, ввести адрес. Эта глава даёт ему окно: сервисный узел class: "app" = локальный HTTP-сервис + окно WebView2. Достаточно конфигурации, деградация автоматическая, а вместе с xs pack получается одиночный exe «по двойному щелчку».
Введение
window и жизненный цикл фронта.50.1 Первое настольное приложение
Возьмите сервисный узел любого готового сайта, поменяйте класс на class: "app" и добавьте дескриптор window:
{
"enabled": true, "class": "app", "name": "main",
"ip": "127.0.0.1", "port": 0,
"host_default": { "name": "app", "path": "wwwroot", "devlang": "c",
"devfile": "script/http_main.c" },
"window": {
"title": "Моё приложение", "width": 1280, "height": 800,
"url": "/", "devtools": false,
"close": "stop", "install_fallback": "ask"
}
}
Запустите xs: сначала появляется окно загрузки (заглушка до готовности сервиса); когда локальный HTTP готов, автоматически открывается главный интерфейс по url. Обратите внимание на два отличия от класса http: ip по умолчанию 127.0.0.1 (привязка к loopback не вызывает диалог брандмауэра Windows) и port допускает 0 (автоназначение, см. 50.3). Постатейные ограничения девяти полей window (включая min_width / min_height и таблицу значений install_fallback) — в справочнике полей xs.json.
Вариант сборки должен включать расширение webview: build.bat webview (или all); вариант без расширения по-прежнему разбирает конфигурацию — просто без окна (см. матрицу в 50.4).
50.2 Жизненный цикл и семантика close
- Запуск: сперва окно загрузки (закрывает паузу инициализации сервиса) → сервис готов → автоматический переход на главный интерфейс
close: "stop"(по умолчанию): закрытие окна пользователем = плавная остановка по той же цепочке, что Ctrl+C — не убийство процессаclose: "hide": закрытие лишь скрывает окно, сервис продолжает работать. Повторный запуск того же приложения будит существующее окно через обнаружение единственного экземпляра (именованный mutex, производный от пути exe), а новый процесс сразу завершается. В v1 нет значка в трее; в режиме hide остановка идёт по сигнальному пути- Изоляция сбоев: рендерер WebView2 — отдельный процесс, падение страницы не заражает xs; любой сбой потока окна деградирует в строку лога и никогда не уводит сборку двигателя на путь fail
50.3 Автоназначение port: 0
Порт настольного приложения не должен быть виден пользователю и не должен конфликтовать с другими — port: 0 отдаёт выделение ОС атомарно (разрешено только классу app, остальные fail-fast):
- Считывание: после успешной привязки фактический порт попадает в
XS_ServerInfo.PortBound(Port также перезаписывается, идентичность конечной точки совпадает со скриптовым представлением); фронт собирает URL именно из него - Из скрипта:
xsServerFind("main")->PortBound, отрисовать в страницу, чтобы фронт знал, кто он (например адрес WS) - Прилипание при горячей перезагрузке: таблица прилипания по имени сервиса (живёт сквозь поколения) при смене поколения явно привязывает старый порт — открытые окна ничего не замечают; занятый прилипший порт — кандидат проваливается и чистится по обычному пути отказа
- Граница: таблица прилипания не сохраняется на диск. Для стабильного порта между перезапусками задайте фиксированный порт (особенно когда внешние системы ходят к вам обратными вызовами)
50.4 Матрица деградации и выпуск
| Вариант сборки | Платформа | WebView2 | Поведение |
|---|---|---|---|
| расширение webview | Windows | установлен | полная функциональность окна |
| расширение webview | Windows | отсутствует | по install_fallback: ask спрашивает / silent ставит молча / headless сразу деградирует; любая неудача — headless |
| расширение webview | Linux | — | headless, адрес доступа печатается в лог |
| без расширения | любая | — | то же headless плюс строка-уведомление; конфигурация разбирается как обычно |
Комбинация выпуска: xs pack (глава 48) упаковывает сайт в бинарник + xsw.exe (вариант GUI-подсистемы без консоли, идёт в релизном пакете) — продукт представляет собой одиночный exe «по двойному щелчку»: окно загрузки → интерфейс → закрытие-в-остановку, чёрной консоли нет ни на одном шаге. Ноль DLL времени выполнения: таблица импорта — только системные DLL; WebView2Loader.dll находится в рантайме в каталоге установленного Runtime, отсутствие Runtime покрывает install_fallback.
Типичные ошибки
Упражнения
Итоги
class: "app" = фронтендизированный http (та же конфигурация + 9 полей window) · загрузка → готовность → открытие URL → закрытие-в-остановку = жизненный цикл · port:0 + PortBound = невидимый порт (для стабильности между перезапусками — фиксированный) · матрица деградации = окно по возможности, сервис прежде всего · xs pack + xsw.exe = настольный exe по двойному щелчку