Сборка страницы и статическая копия
У страницы с фреймом два способа попасть к посетителю: сборка на запросе (PHP собирает фрейм по шаблону для этой страницы) и статическая копия (готовый HTML лежит файлом, веб-сервер отдаёт его без PHP за миллисекунды). Какой способ выбран, решает не страница и не вы, а содержимое фрейма.
Сборка на запросе
- По адресу находится страница и её язык.
- Тема берёт фрейм страницы (
_onpress_frame) и собирает его из исходника: выполняет каждую вставкуonpress/…для этой страницы и этого языка, подставляетdata-op-bind. Вложенные черезonpress/frameфреймы собираются так же, в том же контексте. - Результат оборачивается в документ темы —
<head>с SEO, иконками, стилями темы, токенами дизайна, собственным CSS и счётчиками. Если фрейм сам является документом (<!doctype html>), обвязка встраивается в его<head>и<body>. - Ответ кладётся в кэш страниц (WP Super Cache).
Всё, что меняется через API (меню, наборы, языки, настройки, вложенные фреймы), попадает на такие страницы на следующем же запросе — API сам сбрасывает кэш затронутых страниц.
Статическая копия
Страница становится файлом, когда выполнены все условия:
| Условие | Причина отказа (reason) |
|---|---|
| страница опубликована и без пароля | not_public |
| у неё есть фрейм | no_frame |
| ни фрейм, ни тело страницы, ни секции, которые тело зовёт, не зависят от запроса (зависимость от самой страницы — тела и её полей — копии не мешает, см. ниже) | renders_on_request, причины — в request_reasons |
страница не отвечает 404 по замыслу (страница 404) | answers_404 |
| адрес ложится в дерево файлов: латиница без процентов, первый сегмент не зарезервирован WordPress, адрес не занят другим приложением | bad_path |
Когда условие перестаёт выполняться, копия снимается, а не остаётся устаревшей. Копия — это ответ сайта, снятый целиком, вместе с обвязкой <head>, поэтому SEO и счётчики в ней те же, что при сборке на запросе.
Состояние копии видно в трёх местах:
- в ответе публикации —
printed:{"how": "static", "url": "/promo/", "published_by": "php", …}; - в списке и карточке страницы —
static:{"published": true, "url": "/promo/", "app_key": "page-6432"}; - в ответах
PATCH /pages/{ref}иPOST /frames/{ref}/assign—staticс полямиpublished,url,path,bytes,reason,was_published,was_url,removed,changed.
printed.how | Что значит |
|---|---|
static | страница отдаётся копией; per_page: true — фрейм зависит от страницы, но у каждой его страницы своя копия |
on_request | фрейм, тело или вызванная секция зависят от запроса — собирается на каждом запросе, причины в request_reasons |
not_published | у страницы пока нет адреса: черновик, отложена или неопубликованный родитель |
theme | у страницы нет фрейма. На сайте v2 это 404 — см. ниже |
Когда фрейм зависит от страницы
Фрейм получает renders_on_request: true, если в нём есть хоть одно из:
| Признак | Как выглядит в live_reasons |
|---|---|
вставки onpress/content, onpress/pager, onpress/docs-nav, onpress/outline, onpress/share-ai, onpress/featured-image, onpress/terms, onpress/assistant, onpress/faq-schema, onpress/assistant-welcome, onpress/partial | inserts: op/content, … |
data-op-bind с токеном page.* или атрибут вставки, целиком равный токену page.* | data-op-bind uses page.* tokens |
onpress/repeat с match от page.* | op/repeat match uses page.* tokens |
onpress/repeat с limit или offset от page.* | op/repeat limit/offset uses page.* tokens |
повторитель по контекстному источнику: pages.children, pages.siblings, pages.adjacent, breadcrumbs | contextual sources: breadcrumbs |
вложенный через onpress/frame зависимый фрейм | — (правило транзитивное) |
Фрейм с параметрами вызова (onpress/frame {"name":"…","agent":"…"}) и фрейм со слотом собираются на месте вызова из исходника: своей копии с чужими параметрами или чужим содержимым у них нет.
Вызов фрейма в теле страницы собирается вместе с телом. Оглавление onpress/outline в теле собирается после всего тела, поэтому видит те же заголовки и якоря, что и оглавление во фрейме страницы. Если FAQ-фреймов в теле несколько, их схемы FAQPage при сборке тела сливаются в первую, остальные снимаются.
Общей копии у такого фрейма нет: она показала бы на всех страницах текст одной. Не делают фрейм зависимым: onpress/faq (вопросы внутри вставки), onpress/repeat по набору без page.*, onpress/repeat {"empty":"frame"}, вложенный повторитель item.<поле>, onpress/lang-switcher, onpress/menu.
Зависимость от страницы и статика
Зависимость бывает двух видов, и статику отменяет только второй.
Известна при сборке. Тело страницы через onpress/content, вопросы onpress/faq-schema и токены page.id, page.title, page.url, page.slug, page.data.<ключ>, page.meta.<ключ> — в привязках, атрибутах вставок и match повторителя. На каждом запросе одного адреса они одинаковы, поэтому страница всё равно становится файлом: копия снимается с ответа именно этой страницы. Такой фрейм помечен static_per_page: true в списке и карточке фрейма. Так устроены лендинги на вызовах: оболочка с onpress/content, тело — вызовы секционных фреймов. Так же site__cta, который показывается по флагу страницы {"match": {"show": "page.data.cta"}}, и подвал, который его вкладывает.
Зависит от запроса. Разбивка по номеру страницы (page.paged, limit/offset от page.*), контекстные списки (pages.children, pages.siblings, pages.adjacent, breadcrumbs), прочие вставки из таблицы выше и остальные токены page.* (дата, автор). Это проверяется у фрейма страницы, у вложенных в него фреймов, у тела страницы и у секций, которые тело зовёт, — в том числе названных данными вызова ({"name": "item.art"}): их видно только при сборке, и если среди собранных попался такой фрейм, копия снимается. Причины видны в request_reasons ответа публикации.
Как сделать страницу файлом. Держите во фрейме, теле и секциях только то, что известно при сборке. Лендинг с телом из вызовов секций, флагами страницы и подвалом станет файлом; лента с разбивкой на страницы, оболочка документации с навигацией по разделу или статья с оглавлением onpress/outline собираются на запросе — и это правильно.
Копии по языкам
Фрейм с языкозависимыми вставками (onpress/menu, onpress/lang-switcher, onpress/footer-columns, onpress/repeat, onpress/frame, onpress/content, onpress/socials, onpress/copyright, onpress/docs-nav, onpress/outline, onpress/share-ai, onpress/pager, onpress/partial) собирается отдельно на каждый язык сайта: копия основного языка и копии остальных. Фрейм без таких вставок собирается один раз. Страница получает копию своего языка.
Пересборка
Копии обновляются, когда меняется то, из чего они собраны. API делает это сам после каждой записи и сообщает результат полем rebaked:
| Запись | Кого пересобирает |
|---|---|
POST/PUT/PATCH/DELETE /frames/… | фреймы, вкладывающие изменённый, транзитивно, и копии страниц, чьё тело зовёт его по имени — в том числе иллюстрации art__*, названные в данных вызова |
| любая запись меню | фреймы с onpress/menu, onpress/footer-columns, повторителями по menu.* |
запись набора данных (PUT, PATCH, DELETE /data/{name}) | фреймы, читающие этот набор, вкладывающие их и копии страниц, чьё тело зовёт такую секцию |
| правка тела страницы | копия этой страницы |
запись системного источника через /data/… | зависимых от этого источника |
"rebaked": {
"status": "ok",
"total": 2,
"baked": 0,
"not_needed": 2,
"frames": [
{ "id": "838a082b034be249", "slug": "article__faq", "because": ["datasets: faq"], "bake": "not_needed", "detail": "renders on request from the source, has no baked copy" },
{ "id": "e627bf4dbefcc374", "slug": "article", "because": ["contains article__faq"], "bake": "not_needed", "detail": "renders on request from the source, has no baked copy" }
],
"cache": { "status": "ok", "scope": "pages", "pages": [6428], "flushed": { "urls": { "status": "flushed", "urls": ["https://example.onpress.pro/tarify/"] } } }
}
| Поле | Смысл |
|---|---|
status | ok — всё нужное собрано; partial — часть; failed — ничего; nothing — от изменения никто не зависит |
total | сколько фреймов найдено |
baked | сколько сборок сделано: языкозависимый фрейм собирается по разу на язык |
not_needed | фреймы на запросе — копии у них нет, собирать нечего. У фрейма с static_per_page вместо этого bake: "ok", static: "per_page" и pages — копии его страниц собраны заново |
frames | по записи на сборку: slug, lang, because (почему попал), bake (ok, not_needed, ошибка), pages (страницы фрейма) |
pages | страницы, у которых зависимая вставка стоит прямо в теле: id и because (datasets: faq, nested frames: …, contains …); есть, только если такие нашлись |
static | копии страниц из pages, собранные заново: status и по странице id, published, url, reason (renders_on_request — страница собирается на запросе, ей хватает сброса кэша) |
cache | сброс кэша страниц, которые собираются этими фреймами, включая фреймы на запросе, и страниц из pages. scope: "pages" — точечно по адресам; scope: "site" — весь кэш сайта, если зависимых страниц больше 400 |
skipped, cap | за один вызов пересобирается не больше 25 фреймов, остальные перечислены здесь |
Неудачная пересборка никогда не отменяет запись: данные уже сохранены, копию можно пересобрать повтором или вручную.
Зависимые страницы находятся по фрейму страницы и цепочке onpress/frame внутри фреймов, а также по вставкам в теле страницы: onpress/repeat по набору, onpress/frame (и всё, что вложено в этот фрейм), привязки и прочие вставки тела разбираются так же, как во фрейме. Такие страницы приходят в pages, их кэш сбрасывается вместе с остальными.
Ручная пересборка
Нужна, когда изменение прошло мимо API — меню правили в wp-admin, опцию меняли руками — или копии надо обновить после обновления платформы:
# всё
curl -s -X POST "$API/site/$SITE/frames/rebake" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"all": true}' | jq '.rebaked | {status, total, baked, not_needed}'
# только то, что зависит от меню и от набора faq
curl -s -X POST "$API/site/$SITE/frames/rebake" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"menus": true, "datasets": ["faq"]}' | jq '.rebaked.frames[] | {slug, because, bake}'
| Поле тела | Кого пересобирает |
|---|---|
all | все фреймы сайта |
menus | фреймы с меню: onpress/menu, onpress/footer-columns, повторители и бинды по menu.* |
pages | фреймы, выводящие страницы, их заголовки и адреса: меню, зависимые от страницы вставки, повторители по pages.*, breadcrumbs, site.front_page, бинды page.* |
languages | фреймы с языкозависимыми вставками, повторителями по site.languages, биндом site.lang |
settings | фреймы с onpress/socials, onpress/copyright, повторителями по site.settings и site.front_page, биндами site.* и option.* |
datasets | массив имён наборов |
frames | массив слагов или id — фреймы, вкладывающие эти |
Пустое тело — 400 nothing_to_rebake. Изменения, сделанные в wp-admin, платформа ловит и сама (правка меню, сохранение записи, изменение опций ставят в очередь тех же зависимых), ручной вызов — для уверенности.
Кэш страниц
Готовые ответы кэширует WP Super Cache, и отдаются они веб-сервером. Все записи API сбрасывают кэш сами: запись фрейма, меню и набора — кэш зависимых страниц, запись CSS и токенов дизайна — весь кэш сайта, настройки, языки и SEO — весь кэш сайта и объектный кэш. Если правка не видна:
# точечно по адресам или id страниц
curl -s -X POST "$API/site/$SITE/cache/flush" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"urls": ["/tarify/"]}' | jq .flushed
# весь кэш страниц сайта
curl -s -X POST "$API/site/$SITE/cache/flush" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"supercache": true}' | jq .flushed
Подробно — настройки, главная и кэш. Проверяйте результат запросом без кук (curl по адресу страницы): вошедший в WordPress видит страницы мимо кэша.
Страница без фрейма и страница 404
На сайте v2 страницу выводит только её фрейм. Опубликованная страница без фрейма (или с удалённым фреймом) отвечает 404, не попадает в кэш и помечается noindex — чтобы по адресу не жила пустая или чужая разметка.
Ответ «не найдено» собирает страница сайта со слагом 404, если она опубликована и у неё есть фрейм: такой же страницей, как остальные, со своим текстом и оболочкой. Её собственный адрес тоже отвечает 404, чтобы поисковик не принял её за обычную страницу. Нет такой страницы — ответ пустой с кодом 404. Опубликуйте страницу 404 на каждом языке сайта — и все несуществующие адреса получат оформленный ответ.
Отчёт сборки
Ответ записи фрейма несёт bake.response — отчёт сборки на стороне сайта:
| Поле | Смысл |
|---|---|
baked | собрано |
bytes, source_bytes | размер результата и исходника |
renders_on_request | фрейм зависит от страницы — общей копии не хранится |
static_per_page | зависимость только от того, что известно при сборке: копии получают его страницы |
lang, default_lang | язык сборки |
inserts, unknown_inserts | сколько вставок выполнено; вставки, которых платформа не знает |
binds, bind_tags | подставленных значений и помеченных тегов |
unresolved | токены без значения |
repeats | что вывели повторители: источник, откуда взят, сколько элементов |
frames | вложенные фреймы: выведен ли, глубина, свои unresolved |
published | копии страниц этого фрейма: pages[] с id, url, published, path, bytes, reason |
bake.status: ok — собрано; unavailable — исполнитель на сайте не установлен, исходник сохранён; failed — исполнитель ответил ошибкой или не ответил (reason); skipped — сборка выключена ("bake": false).