Components: one piece of markup, many insertions
/docs/componentsdraftМашинный перевод с английского· проверено 2026-09-13
Статус: draft · проверенный: 2026-09-13, стенд (создать → zip) → publish → page; TypeScript → app.js); 2026-10-27, §4.7.1 own зоны на стенде (классические и main-only pages, Обязанность, чтение, наследование, откат order) владелец: гладиатр API документация · Служить: GET /docs/components связанный: own-markup.md (твой own HTML в shop), transfer.md (законченный site как папка)
1.Что вы можете сделать здесь
Корабль с одной наценкой, что shop owner Можно много раз поместить в разные content, Без прикосновений code. Вы пишете папку и schema; Они выбирают values. А. new Выпуск папки обновляет каждую вставку сразу.
Это NOT Второй способ поставить HTML на page. Компонент является html document с kind=component: Та же папка, те же релизы, те же самые /_html/{doc}/{release}/ gateway, Те же правила изоляции. Добавлено это schema и замещение.
2. Решать first
| Если ты хочешь… | Возьми это path | Стоимость |
|---|---|---|
| это exact Накладка на этот page | html widget (own-markup.md) | none — Редактировать - значит редактировать code |
| Одинаковая накладка на многие pages с разными content | компонент (этот доктор) | Вы должны написать component.json |
| Целое завершенное site как есть | transfer.md | — |
| live каталог data внутри разметки | vz- ключи и те page scope (own-markup.md) — Они также работают внутри компонента. | параметры являются values, Не вопросы: data Вытекает из ключей, см. §7 |
| Отзывы, Q&A, galleries сохранено в виде записи sets | а level-3 Компонент, читающий public GET /record-sets (/docs/record-sets §4.4) | text появляется after JavaScript; Открыть set покупателям first |
Приходит из Shopify
Компонентом является theme section: component.json это {% schema %}, params это settings, {{key}} - это та же скоба. Имена типов отображают один на один:
Шоппинг settings[].type | здесь | заменяются как |
|---|---|---|
text, textarea | text | HTML-сбежавшая нитка |
richtext, html | html | тот author's разметка через level‑1 санитар |
image_picker | image | а file_id из медиа-библиотеки → public URL (затем рециркулирующий) |
url | link | http(s)://, /path, mailto:, tel: — что-либо else считать отсутствующим |
color | color | #rgb…#rrggbbaa, else отсутствие |
number, range | number | тот number |
checkbox | bool | true/false, и {{#key}}…{{/key}} показывает block — см. ниже |
select, radio | select | одного из options, else отсутствие |
product | — | не на шаге 1, см. § 7 |
collection | — | не на шаге 1, см. § 7 |
Ан отсутствие value (Пропавший, неправильный type, Вне белого списка) показывает параметр's default; Это empty Только когда нет default.
Условный blocks: {{#key}}…{{/key}}
Все, что находится между двумя маркерами, сохраняется. key это true и удаляется когда это false — Разметка, вложенные замены и все такое. drop а Вся колонна, card или row Вместо того, чтобы оставить empty один:
{{#show4}}<li><img src="{{icon4}}" alt=""><h3>{{head4}}</h3></li>{{/show4}}Три правила, все соблюдаются, когда вы publish освобождение:
- `key` должны быть объявлены в `component.json` и должны быть `type: "bool"`. Нет. другой type открывается a block; а text параметр не "правда"".
- Маркеры должны быть парными.
{{#a}}нуждается в own{{/a}}. - Блоки гнезда, они не перекрываются.
{{#a}}…{{#b}}…{{/b}}…{{/a}}Хорошо на любой глубине;{{#a}}…{{#b}}…{{/a}}…{{/b}}Нет, не поворачивать внешнюю сторону. block off Также удаляет все вложенное внутри него.
Разбейте любую из трех, и освобождение будет отклонено. COMPONENT_MANIFEST_INVALID, Назвать маркер и file. Маркер написал внутри HTML comment являются прозой, а не разметкой и не проверяются.
Не взято из Shopify: Жидкие фильтры и теги (замена здесь набрана), не template язык), blocks внутри section (Не гнездиться на шаге 1), limit/max_blocks, и theme как единица delivery — Здесь единица является один document.
3. Объекты и где они находятся live
The document. POST /html-documents с kind: "component". The value set это "" | widget | component; theme (а layout's Сервисная папка) не здесь можно. kind Вернемся к каждому прочтению document и является только после прочтения.
В папке. Именно так. html-проектная папка: index.html В корневой, CSS, JS, images рядом с ним, загруженный POST /html-documents/{id}/releases/zip и повернулся live через .../releases/{rid}/publish. Освобождение компонента также должно содержать `component.json` — Публикация без отказа.
`component.json`.
{
"name": "Баннер акции",
"params": [
{ "key": "title", "type": "text", "label": "Заголовок", "default": "Скидки недели", "max": 80 },
{ "key": "image", "type": "image", "label": "Картинка" },
{ "key": "link", "type": "link", "label": "Ссылка" },
{ "key": "sale", "type": "bool", "label": "Показать бейдж", "default": false },
{ "key": "size", "type": "select", "options": ["s", "m", "l"], "default": "m" }
],
"presets": [ { "name": "Осень", "params": { "title": "Осенняя распродажа" } } ]
}Все правила, соблюдаемые в publish с обидчиком field названных в докладе (error code COMPONENT_MANIFEST_INVALID):
keyматчи^[a-z][a-z0-9_]{0,31}$и является уникальным;- в лучшем случае 32 параметры, file в лучшем случае 16 KB;
typeОдин из восьми вышеперечисленных — unknown type является error, Потому что type Именно это и решает побег.;selectТребуется не-emptyoptions(≤ 64) и ничего else может нести;defaultдолжен быть параметром's own type (Струна дляtext, а number дляnumber,true/falseдляbool, а#hexдляcolor, членoptionsдляselect);maxПлотность длинtext/html(в персонажах, нарезанных на персонажа граница);- unknown ключи Объекты являются ignored Нарочно — папка, написанная для позже schema Публикуется и сегодня.
presets это optional. Шаг 1 - читать first Предустановленный как base values из вставки, на top в соответствии с параметром defaults.
разорвавшийся schema хранится на выпуске row, Таким образом, регресс восстанавливает оба разметка и параметр set из этого version — И он будет прочитан обратно из API, never Из папки: см. §4.5.
Вставка. Обычный page section. Писать path:
{ "type": "text",
"payload": { "v": 2, "kind": "component",
"props": { "component": { "ref": 42, "params": { "title": "Своё", "sale": true } } } } }type это text Потому что бэкэнд section Белый список image|slideshow|text|video и каждый widget Поездка на нем — The widget's реальный идентичность payload.kind, именно так, как kind: "html". ref Это document id; params это plain {key: value} объект (≤ 8) KB).
4. Рецепты
4.1 Создавать, загружать, publish
API=https://api.vizen.shop; T="Bearer $VIZEN_TOKEN"
# 1. the document
DOC=$(curl -s -X POST "$API/html-documents" -H "Authorization: $T" \
-H 'Content-Type: application/json' \
-d '{"item":{"name":"Баннер акции","kind":"component","level":1}}' | jq -r .result.id)
# 2. the folder (index.html + component.json + style.css) in one zip, published at once
cd banner && zip -qr ../banner.zip . && cd ..
curl -s -X POST "$API/html-documents/$DOC/releases/zip?publish=1" \
-H "Authorization: $T" -H 'Content-Type: application/zip' --data-binary @banner.zipThe publish Ответьте на имена schema он принял:
{ "document_id": 42, "release_id": 17, "active": true, "kind": "component",
"component": { "name": "Баннер акции",
"params": [ {"key":"title","type":"text","default":"Скидки недели"}, … ] } }Отказ является 422 с {"error":"RELEASE_INVALID","report":{"errors":[…]}}; а schema error перевозить code: COMPONENT_MANIFEST_INVALID, ref = тот field (params[0].key) и message = тот reason.
4.2 Поставьте его на page, дважды, с разными values
GET /content-blocks/{id}, добавить два sections, PUT /content-blocks/{id} с тот целое список (sections являются заменой-set):
{"item":{"sections":[
{"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
"ref":42,"params":{"title":"Осень","sale":true}}}}},
{"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
"ref":42,"params":{"title":"Зима"}}}}}
]}}Для существующего block, Сохранить другой sections в result.sections и Отправить слившийся список в item.sections. отказываться item недействительным; а empty item.sections: [] не ясно sections (повторный-field семантика). POST /docs/validate только принимать {"sections":[…]}, без item конверт.
проверять: Открыть page и охапать HTML в двух заголовках. Два добавления один document должны показать два разных texts; тот image должен пройти через рециркулятор (/w/1024/webp/files/…), Не как оригинал file.
4.3 Прочитать собранный HTML непосредственно
curl -s -G "$API/html-documents/42/content" \
--data-urlencode 'company_id=12' \
--data-urlencode 'params={"title":"Осень","sale":true}'Ответ такой же, как и для html widget — {html, level, updated_at} — специально: витрина магазина не должна быть в состоянии отличить компонент от widget, Или будет два погрузчика на одного kind из block. params это JSON объект URL-закодированный в query; участвует в кэше key, В двух вставках never видеть друг друга's HTML. static=true ведет себя точно так же, как и для widget (payment/cart/account Поверхности получают разметку без author's code).
4.4 Корабль new version
Загрузить а new zip с ?publish=1. Каждая вставка поднимает его after чистка — магазин вставок values, never Ценности, которые key исчезнувший из new schema есть ignored; параметры вставки never set Отступать назад к new default. POST /html-documents/{id}/rollback Возвращает предыдущий релиз Вместе с его own schema.
А. PAT's contour это fixed от key паспорт (GET /v1/account/token); X-Vizen-Contour cannot Переключай. header Приведенный ниже пример относится к уполномоченного заседания, а не на PAT.
История выпуска - это разделяемый между собой draft и live site — только указатель на active Освобождение на contour. Публикуется релиз с X-Vizen-Contour: dev Появляется в списке А live Сеанс также, несущий was_live: false. Активировать его там будет draft работы над live site За спиной owner's Ответ: Так что ответ 409 RELEASE_DRAFT_ONLY: publish тот draft (POST /v1/dev/publish) И освобождение продолжается live Через эту дверь. draft Ничего не меняется.
4.5 Читать далее schema назад
Ты не приносишь component.json Чтобы узнать, что за компонент берет. schema Возвращается на document И на каждом выпуске, который имеет:
# the document — the schema of its ACTIVE release
curl -s "$API/html-documents/42" -H "Authorization: $T" \
| jq -r '.result | {active_release_id, manifest}'
# { "active_release_id": "17", "manifest": "eyJuYW1lIjoi…" }
# the releases — each with its OWN schema, which is what a rollback restores
curl -s "$API/html-documents/42/releases" -H "Authorization: $T" \
| jq '.items[] | {id, active, manifest}'Две формы для одного schema, Потому что два ответа приходят off Два транспорта:
| где | field | форма |
|---|---|---|
GET /html-documents/{id}, GET /html-documents | result.manifest | прото bytes → основание 64 из сырых JSON (расшифровать его before разбор) |
GET /html-documents/{id}/releases, …/releases/{rid} | items[].manifest | а plain JSON объект |
manifest это empty, never {}, Когда нет никакого schema Чтобы показать: a html widget, а layout папка, а draft релиз, компонент, папка которого имеет never Издана. "Пустота" "" на document (ан empty bytes) и key Пропавший без вести. empty *объект* будет читаться как "компонент, не имеющий Параметры», что совсем другое — так что ни один ответ не посылает ни одного..
active_release_id Возвращается на document И это тоже, и это честно. test для "Можно ли еще разместить этот компонент": нет active Отпуск, без разметки и без schema. The field только для чтения — publish откат move это, PUT /html-documents/{id} не.
4.6 Исправить один file без new застежка
# read the source of one file of a release (text only, ≤ 2 MB)
curl -s "$API/html-documents/42/releases/17/file?path=style.css" \
-H "Authorization: $T" | jq -r .text > style.css
# {"release_id":17,"status":"published","path":"style.css",
# "mime":"text/css","size":812,"sha256":"…","text":"…"}
# ship it back: one file, the rest inherited from the active release
curl -s -X POST "$API/html-documents/42/releases" -H "Authorization: $T" \
-d '{"base":"active","note":"edit","files":[{"path":"style.css","sha256":"'$SHA'","size":'$SZ'}]}'
curl -s -X PUT "$UPLOAD_URL" --data-binary @style.css
curl -s -X POST "$API/html-documents/42/releases/$RID/publish" -H "Authorization: $T"ошибки: 404 FILE_NOT_FOUND, 415 FILE_NOT_TEXT (Бинарный — Reupload It), 413 FILE_TOO_LARGE. index.html Возвращаться publish-очищенный, Не то bytes Вы загрузили: конвертер переписывает его на месте, оригинал не сохраняется.
4.7 Наденьте его ONE product или ONE category
Три пути; для одного page Первый — это почти всегда то, что вы хотите.:
| Тебе нужно | Делай | The page Сохраняет shop шаблон? |
|---|---|---|
| Ваш блок before или after тот body это page наследовать | Добавить его в page's own зоны — §4.7.1 | да |
| а Разная структура для этого page's body | копировать body группировать и связывать копию - §4.7.2 | Нет, он перестает наследовать |
| тот то же самое блокировать каждый product одного или нескольких sections | один feed привязка к поддереву каждого раздела - §4.7.3 | да |
значения, которые различаются на product (material, sizes, Состав не является blocks: keep В них, в product's attributes и description И пусть template шоу Они. Иная структура для целого section это body variant привязанный к category (category_children/<id>), не blocks на всех product.
4.7.1 Добавить block вокруг наследуемого body
Каждый product и category page имеет свой own blocks Рядом с ним body наследует. Они live на службе blocks названный __page:<zone> — а manifest section kind:"zone" чей refs список block ids в display order — и в явном виде и blocks Они вписаны в список page с PUT /products/{id}/content-blocks (PUT /categories/{id}/content-blocks для category). Именно это и есть shop's page editor пишет, так что owner видеть и редактировать ваши block Там и там, и там body Продолжает следовать за shop template.
Сначала выясните, какие зоны page рисовать. Его геометрия исходит от ближайшей к нему Три слоя: The page's own __page:top (page.layout или page.layoutConfig в его section), затем template (design.page.body_page), затем layout (design.page.theme_page); Последние два кодируются как base64. JSON объекты в GET /products/{id} — Читайте те же самые ключи.
| Ближайший layer иметь | Зоны, вытянутые | Перед тем как body | После этого body |
|---|---|---|---|
нет layout и нет layoutConfig в любом месте | классический top · body · bottom | __page:top | __page:bottom |
предустановленный (main-only, stacked, content-aside, content-aside-left) или layoutConfig.rows | тот zoneIds в его rows; тот body Колонна с которой "body": true — main В каждой предустановке | __page:main, zoneFlow side:"before" | __page:main, side:"after" |
Используйте только зоны, которые находятся в геометрии. zone Внешне он не нарисован на layoutConfig v:2 сетки и земли за пределами сетки иначе — либо не Где ты имел в виду (__page:bottom на main-only page).
API=https://api.vizen.shop; T="Bearer $VIZEN_TOKEN"
# 1. what the page already has: content_blocks[] sits NEXT TO result
curl -s "$API/products/2505" -H "Authorization: $T" > page.json
# {"result":{…}, "content_blocks":[{"id":"3600","name":"__page:top",…}, {"id":"3601",…}],
# "design":{"page":{"body_page":"<base64>","theme_page":"<base64>",…}}, …}
jq -r '.design.page.body_page // empty' page.json | base64 -d | jq '{layout, layoutConfig}'
# 2. your insertion as a block — here an existing component; an html block or any
# other block goes in the same way, a finished folder is not turned into a component
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"item":{
"name":"Promo banner (Volstreet 120)",
"sections":[{"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
"ref":42,"params":{"title":"Осень"}}}}}]}}'
# → {"result":{"id":"3701", …}}
# 3. the zone after the body. Classic page without "__page:bottom" in step 1 → create it:
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"page_design_version":1,
"item":{"name":"__page:bottom",
"sections":[{"type":"text","payload":{"v":2,"kind":"zone","refs":[3701]}}]}}'
# → {"result":{"id":"3702", …}}
# Grid page → the same call with "__page:main" and a placement:
# "payload":{"v":2,"kind":"zone","refs":[3701],
# "props":{"zoneFlow":{"v":1,"entries":[{"entryId":"promo-3701","ref":3701,"side":"after"}]}}}
# The manifest exists → GET /content-blocks/{id}, add 3701 to refs (and its zoneFlow entry),
# PUT the whole sections list back with "page_design_version":1.
# 4. bind the manifest AND the block — replace-set: also send every id from step 1
curl -s -X PUT "$API/products/2505/content-blocks" -H "Authorization: $T" -d '{"items":[
{"block_id":"3600","sort_order":0}, {"block_id":"3601","sort_order":1},
{"block_id":"3702","sort_order":2}, {"block_id":"3701","sort_order":3}]}'Правила зон:
- `refs` есть JSON номера (
[3701], не["3701"]). Сервер проверяетzoneFlowпротив числовых refs, и его "block "Сторонники" номера матчей Только: Струна ref Позволь block быть deleted Из-под него page. - А. ref должны быть привязаны к page (Шаг 4. А). ref Это не является обязательным skipped, Это не является error.
- `__page:top` Также держится page settings в его section's
pageобъект (hideSiteHeader,hideSiteFooter,hideSystemBlock, тот layout). Редактировать это manifest — Читать, менятьrefs, Отправляйте каждый section назад — never воссоздать его. - `zoneFlow` это
{"v":1,"entries":[…]}с одной записью на ref, В том же самом order:entryIdУникальный не-empty струна,refто же самое number,sideэтоbeforeилиafterтот body Внутри этого zone. Если manifest иметьzoneFlow, добавлять Вход для каждого ref Добавить: Несоответствие ответов400 PAGE_ZONE_FLOW_INVALID. БезzoneFlowкаждый ref считать какafter. - Отправить
"page_design_version":1В корне каждой буквы а__page:*manifest: Сетка для письма data (page.layoutConfig,page.zoneProps,zoneIdвzoneFlow), или редактировать manifest Что уже есть, ответы400 PAGE_DESIGN_WRITER_UNSUPPORTEDбез него. - Утвержденный product Поделиться на рынке (
is_shared) возвращается к умеренность after Шаг 4, как after Любые изменения покупателя content.
проверять: GET /products/2505 Список manifest и block в content_blocks, и manifest's refs Возвращайтесь в числа; GET /design/bindings/product/2505 Ответы на вопросы body source из shop, category:<id> или default, не own — тот template Потом наследуется. тот page.
откат, в этом order:
PUT /products/2505/content-blocksс точно таким items Ступень 1.- The manifest ты created →
DELETE /content-blocks/3702; а manifest Это было там →PUTего sections с шагом 1 назад (с"page_design_version":1). DELETE /content-blocks/3701. Хотя любой live manifest Все еще перечисляет block, Ответы на эти вопросы400 DESIGN_BLOCK_IN_USE— Это то, что why Последний.
4.7.2 Дайте один page другой body
А. page body Один из них design group (chrome.md §3.2) и a group принадлежащий Кто бы это ни был, изменить структуру одного page's body ты копировать body group это page наследовать, Измените копию и свяжите копию к этой resource.
Если group Вы копируете, что пришло из page Строитель (его) section иметь props.zoneFlow или page.zoneProps), копировать его sections целое, keep его group_type (category для category page), наносить "page_design_version": 1 В корневой части Запрос и изменение refs в обоих widgets и zoneFlow.entries (/docs/chrome §3.1 Сохранить карту связывания before Шаг 4: Восстановить THAT карта на откат назад (§4.8 шаг 6)) — {"slots":{}} При этом правильнее будет только тогда, когда resource иметь нет own связывание.
API=https://api.vizen.shop; T="Bearer $VIZEN_TOKEN"
# 1. what does this page use today, and where does it come from?
curl -s "$API/design/bindings/product/2505" -H "Authorization: $T"
# {"own":[], "resolved":{"body":{"block_id":"3410","source":"shop"}, …}}
# ^ inherited from the shop template
# 2. read that group (4.2 endpoint — a group is a content block)
curl -s "$API/content-blocks/3410" -H "Authorization: $T" > body.json
# 3. a group does NOT hold sections: it holds ONE section kind:"group" whose
# props.widgets lists widget BLOCKS. So first make your insertion a block…
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"item":{
"name":"Promo banner insertion",
"sections":[{"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
"ref":42,"params":{"title":"Осень"}}}}}]}}'
# → {"result":{"id":"3701", …}}
# …then create YOUR copy of the group: the widgets from step 2 plus yours
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"item":{
"name":"Product card + promo banner (Volstreet 120)",
"group_type":"product",
"sections":[{"type":"text","payload":{"v":2,"kind":"group",
"props":{"widgets":[ …ids from step 2…, 3701 ]}}}]}}'
# → {"result":{"id":"3702", …}}
# Sections put straight into the group answer 400 DESIGN_SLOT_NEEDS_GROUP at step 4.
# 4. bind the copy to this product only
curl -s -X PUT "$API/design/bindings/product/2505" -H "Authorization: $T" \
-d '{"slots":{"body":3702}}'category/<id> вместо product/<id> Делает то же самое для одного category page; category_children/<id> для каждого product из субдерево. group_type должен соответствовать тот page kind (product, category) — Лестница в §3.2 chrome.md выбор groups через type.
проверять: GET /design/bindings/product/2505 должен отвечать "body":{"block_id":"3702","source":"own"}, и соседней product должен Ответить "source":"shop". Откройте оба pages. The product's own address Находится на product само собой — GET /products/2505 возврат url (/catalog/beds/krovat-loft-volstrit-120) и slug; список return url слишком.
Сколько стоит копия. Это копия, а не наложение:
- тот page Прекратить наследовать shop template. Изменить shop-широкий product body Позже и это page Старый держит - никто не предупреждает. связывать resource Возвращение к наследованию, когда исключение больше не требуется;
- замена на CATEGORY listing меняет свою capabilities: фильтры для запасов, Сортировочные средства и их
ItemListJSON-LD Не появляется только потому, что компонент рисует плитки. Собственная разметка поддерживаетproducts_pageломка для page's section. Выберите сознательно, capabilities то keep; Замена только одного listing в существующем group Рецепт в §4.8; - тот product feed 48. items за page (
own-markup.md§2): page's own section pages с?page=Nиproducts_page, а section названный в вставке (productsCategoryId) получает first page Только в том section's own order. The Вставка также может попросить выбранного attributes (productsAttributes) и одной плиткой на product group с переключаемым members (productsMode: "collapse", paging через groups) — рядом сrefиparams, never внутриparams(own-markup.md§2).
откат, в этом order:
curl -s -X PUT "$API/design/bindings/product/2505" -H "Authorization: $T" -d '{"slots":{}}'
curl -s -X DELETE "$API/content-blocks/3702" -H "Authorization: $T" -d '{}'
curl -s -X DELETE "$API/content-blocks/3701" -H "Authorization: $T" -d '{}'{"slots":{}} является заменой-set Это разъединяет все и ставит page назад Наследственная лестница (chrome.md §3.2) — сделать before удалять group, или page рендеринг с висячим связыванием. {"slots":{"body":0}} это Не откат: 0 Это значит "это" page не имеет body В любом случае" (§3.3 там)).
4.7.3 Один и тот же блок на каждом product одного или нескольких sections
Один баннер, записка о доставке или промо-стрип на всех стульях и на всех полках: поставьте его в feed (chrome.md §3.4 и связать feed в поддрево каждой секции. The body Продолжает следовать за shop Шаблон, блок существует один раз, и один редактировать Каждый меняет page. Копировать body Для этого (§ 4.7.2) также работает, но копии перестают наследовать шаблон до тех пор, пока они связаны.
# 1. the block itself — any widget or html block; here an existing one, 3801
# 2. a design group holding it
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"page_design_version":1,
"item":{"name":"Promo","group_type":"design",
"sections":[{"type":"text","payload":{"v":2,"kind":"group","props":{"widgets":[3801]}}}]}}'
# → {"result":{"id":"3802", …}}
# 3. the feed: the body marker -1 and your group in display order (here after the body)
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"page_design_version":1,
"item":{"name":"Promo feed","group_type":"layout",
"sections":[{"type":"text","payload":{"v":2,"kind":"group","props":{"widgets":[-1,3802]}}}]}}'
# → {"result":{"id":"3803", …}}
# 4. per section: PUT is a replace-set, so read the subtree's own slots first
# and send them back together with feed
curl -s "$API/design/bindings/category_children/48" -H "Authorization: $T"
# → {"own":[], "resolved":{"feed":{"block_id":"0","source":"default"}, …}}
curl -s -X PUT "$API/design/bindings/category_children/48" -H "Authorization: $T" \
-d '{"slots":{"feed":3803}}'
# the same for category_children/49 …проверять: GET /design/bindings/product/<a product of section 48> ответы "feed":{"block_id":"3803","source":"category:48"} и его body Что это было before. Открыть product После этого из каждого раздела один раз изменить блок 3801 и перезагрузить оба.
Что? feed ты стоишь. Это заменять тот feed эти products наследуемый Родительский отдел, то есть layout (theme) или shop. Если шаг 4 показывает resolved.feed кроме других 0, Начните свой список с этого feed'Список и добавить Ваша группа или эти страницы теряют то, что она показала. product с его own feed Обязательный сохраняет свою own. Заголовок, body, footer и каждый page's own зоны (§4.7.1)) Не меняйся.
откат, в этом order: PUT каждый category_children/<id> Вернемся к own Вы читаете шаг 4 (own: [] → {"slots":{}}), затем DELETE /content-blocks/3803 и 3802 (и 3801, если вы сделали это. Все еще указывая на feed ответы 400 DESIGN_GROUP_IN_USE, Список все еще удерживая группу 400 DESIGN_BLOCK_IN_USE — Сначала сцепление, затем Одинокий неиспользованный layout или design Группа может быть удалена. DELETE мягкий (deleted_after +30 дней, но нет звонка для восстановления и Удаленный блок больше не читается: сохраните то, что вы читаете before удаление.
4.8 Заменить category's Сеть через сетку через PAT, с откатом
Этот рецепт заменяет собой listing внутри существующего category body group, Сохранять его banner и другие blocks. Это не то, что product-body Копия рецепта §4.7.2 Если resolved group это inherited/shared и только один page должны меняться, first копировать и связывать его с этим category (§4.7.2); кроме предыдущего связывания. Никогда не редактируйте общий group Если предположить, что он принадлежит только одному page.
- удостоверять подлинность first использовать Ключи и разрешения, включая обязательную проверку паспорта before пишет. Читать
GET /design/bindings/category/<id>, затем GET тот resolved body group. инспектироватьGET /content-blocks/{id}/usage:totalсчитать прямым design связывание плюс manifest ссылки (groups/layouts/zones);refsВозврат максимум 100 Из них не все pages достигаемый по наследству. В ответе на связывание,resolved.body.source: "own"означает прямой body выбор; другой source (Например,"shop") Наследственность. own Только связывание не доказывает исключительность: проверка usage И спаси меня. GET ответ локально и подготовить запрос на откат before писать:
``bash API=https://api.vizen.shop GROUP=YOUR_RESOLVED_BODY_ID # resolve for your target category curl --fail-with-body -sS "$API/content-blocks/$GROUP" \ -H "Authorization: Bearer $VIZEN_TOKEN" > group-before.json jq '{page_design_version:1,item:(.result|{name,group_type,sections})}' \ group-before.json > group-rollback.json ``
Уже заменены? Если body это own, запас listing отсутствует и Ваша вставка blocks уже присутствует, keep Обновить эти ссылки. существующий вставной settings или publish а new освобождение его document (§4.4); не создавать дубликаты вставок или запускать замену снова. Ожидаемый старый listing И ваши вставки отсутствуют, проверьте body before Писать вместо того, чтобы угадывать замену target.
- Подготовить компонент. Проверяйте каждый scope field Ваша наклейка читается против
GET /docs/vz-keys.json→scope. А. field отсутствует cannot быть передан Замена сервера: не publish , в зависимости от того, изобрели ли они key; О необходимости создания платформы owner. рекорд sets не находится в scope — Прочитайте их вместе с publicGET /record-sets(/docs/record-sets§4.4). Upload/publish Компонент по §4.1 и проверить принятого schema. Доступ покупателя для attributes и топоры:/docs/catalogue§8.3; правила разметки и images:/docs/own-markup§§4,6; theme выравнивание:/docs/chrome§3.7 Не трудно-code каталожные цены или вычисления combo цитаты.
- Создать вставку blocks без привязки к ним. Использовать
POST /content-blocksВ двух полных телах в семья cards §3. Это и есть каноническая вставка. content, включаяshow_catalog:falseдля тот combo вставки и их full-width, ноль-padding wrapper. Заменить все иллюстративный ids с твоим own. Спасти семью block'sresult.idкакFAMILIES, и optional combo block's id какCOMBOS. Последний выбирает set ids и петли надp.combo_items. Держите оба несвязанными до тех пор, пока не проверят. Семантика выбора:/docs/catalogue§4.1 и/docs/own-markup§2.
- Создайте замену из snapshot. Определить запас listing block id в group's
props.widgets, Читать далее block, и проверить егоkindЭто listing Ты собираешься заменить, сохранить все остальные. block, page собственности и zone Настройка./docs/chrome§3.1 для двух списков Инвариант и писатель version. Пример требует OLD Происходит ровно один раз; повторяющийся refs, target конкретныйentryIdВместо этого, пример. Заменять один listing с двумя blocks:
``bash OLD=YOUR_LISTING_BLOCK_ID # verified listing id, not the whole group # FAMILIES and COMBOS are the new result.id values from step 3 jq --argjson old "$OLD" --argjson a "$FAMILIES" --argjson b "$COMBOS" ' .item.sections |= map( if .payload.kind == "group" then .payload.props.widgets |= map(if . == $old then $a,$b else . end) | if .payload.props.zoneFlow then .payload.props.zoneFlow.entries |= map( if .ref == $old then (.ref=$a | .entryId=("pat-"+($a|tostring)+"-families")), (.ref=$b | .entryId=("pat-"+($b|tostring)+"-combos")) else . end) else . end else . end)' group-rollback.json > group-next.json ``
Проверьте сгенерированную запись ids Уникальность в этом group, Оба списка имеют совпадение order/count, И только предполагаемое событие было заменено. Пример сохраняет старое явление's zone/side для обоих new Если вы не против. Хочется другого zone, select явно не падая page.zoneProps. подтверждать item.sections с POST /docs/validate (§4.2; не проверяется существование refs, писатель version или group Потребители. Перечитайте group before PUT; Если он изменился с тех пор, как snapshot, Помириться вместо переписывания.
- Переключай один раз, потом проверь.
``bash curl --fail-with-body -sS -X PUT "$API/content-blocks/$GROUP" \ -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \ --data-binary @group-next.json ``
GET Опять же: подтвердить оба списка ссылок, new ids и сохраненный zone свойства. Проверьте сервер HTML, Затем браузер (/docs/troubleshooting §2): first без панелей JS, size/colour переключатели, недоступная комбинация, SKU/price/ image/cart Аргументы вместе, combo состав, mobile выравнивание и images. Проверь еще category Только 200 не принимаются, это не должно оставаться неизменным..
- откат использует один и тот же автор и exact спасенный sections, не является empty Удаление или переплетение старого listing:
``bash curl --fail-with-body -sS -X PUT "$API/content-blocks/$GROUP" \ -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \ --data-binary @group-rollback.json ``
Перечитайте и проверьте page. Если шаг 1 created Эксклюзивная копия, также восстанавливается Сохраненная карта (не слепо) slots:{}). Сохраните компонентный выпуск Rollback (§4.4 отдельно: восстановление group не восстанавливает высвобождение разделяемого компонента. Не удаляйте старые blocks или snapshot files во время принятия.
Учебник использует один многоразовый level-1 компонент с семейной вставкой и ан optional combo Решить каждый вопрос target category's body независимо; пример ids являются локаторами, never Платформы по умолчанию.
5. TypeScript
Поставить app.ts в папке и указать разметку в составленный file:
<div class="promo">{{title}}</div>
<script type="module" src="app.js"></script>// app.ts
type Options = { root: string };
const opts: Options = { root: ".promo" };
export function init(): void {
const el = document.querySelector<HTMLElement>(opts.root);
if (el) el.dataset.ready = "yes";
}
init();Публикация релиза собирает все *.ts Отделение в сибирской *.js и вносит его в освобождение Рядом с ним source. The source оставаться: Следующий выпуск построен из base=active, и папку, которая .ts был Выброшенный не может быть отредактирован без его загрузки снова. и ничто не минируется — типы стираются, то есть все.
Что составляется
- каждый
*.tsНа любой глубине папки —app.ts,ui/menu.ts; - ничего else.
*.tsxнуждается в JSX Завод, где нет выбора,*.d.tsкомпиляции в один empty file, иnode_modules/кто-то есть else's delivery. Те, кто files Оставайтесь в освобождении нетронутыми и неиспользованными.
Как? result Загруженный определяется вашим тегом, а не компилятором. The модульный синтаксис source Сохраняется (target ES2020): а source с import/export Остается модуль и работает под <script type="module" src="app.js">; а source Без них остается обыкновенным script С теми же самыми глобалами, что и у него, onclick="init()" в накладной держит Работает. IIFE Обернут вокруг вашего code За спиной.
Импортные пути - это не Переписывается также: Пишите import { x } from "./util.js" — тот file Браузер действительно будет. "./util.ts" публикует с предупреждением, а затем 404s в браузере; не имеющий расширения "./util" Отклоняется как сломанная ссылка, точно так же, как в рукописный .js.
Только `kind=component`. Ан html widget и а layout папка publish точно так же, как before — а .ts В них есть только file Что никто не компилирует.
Отказы — 422 с {"error":"RELEASE_INVALID","report":{"errors":[…]}}, той же формы, что и schema error:
code | когда | file · ref |
|---|---|---|
COMPONENT_TS_ERROR | тот source не поддается компиляции; а также size limit или тайм-аут | тот source · app.ts:3:19 — line 1-я колонка на основе |
COMPONENT_TS_CONFLICT | app.js находится в папке рядом с app.ts | тот .js · тот .ts сталкиваясь с |
Готовый app.js это never Скриншоты из игры If You Compiled the script себя, drop тот .ts или одно из двух (построено)'s own warnings приходить обратно report.warnings с code: COMPONENT_TS_WARNING и не block тот publish.
пределы: 512 KB за source, 4 MB из TypeScript за освобождение, 10 s из Компонент - это часть разметки, а не приложение. — а bundle это hits Они принадлежат к html проект (transfer.md).
⚠️ В level 1. page never получает script. составленный file обслуживается тот gateway В любом случае (/_html/{doc}/{release}/app.js, text/javascript), но тот level‑1 санитарная полоска <script> из собранных HTML. Для Браузер для запуска его document должен быть level 3 (author-принадлежащих ему, как и для html Проект — тот же rule который ест <link rel="stylesheet">, См. § 6.
⚠️ Две вставки выполняются script дважды. Каждая вставка a level‑3 Компонент несет в себе own <script>, И каждый бег видит EVERY копия на нем page. А. script которая связывает все .my-block nodes Каждый дарит каждому button два хозяина — а text Обновление скрывает его, переключатель дважды загорается и выглядит мертвым. инициализировать и пропустить:
document.querySelectorAll('.my-block:not([data-ready])').forEach(function (root) {
root.setAttribute('data-ready', '')
// bind inside root only
})6.Безмолвно ignored
- А. key Не в том schema — в обоих
paramsсбрасывается (сбрасывается) и в разметке ({{whatever}}становится empty Нет сырой нити.{{ }}Для компонента: a placeholder либо имеет type Или это ничто. - А. value Неправильное type — Отсутствующий, поэтому
defaultшоу. А.200наPUT /content-blocksНичего не доказывается о value принимать effect; читать page. - А. `link`/`image` вне белого списка (
javascript:,data:,//other-host) — тот value становится empty, и ан empty URL value означает атрибутом является Не написаны вообще, не написанный empty. Писать CSS противimg:not([src])иa:not([href]), не противimg[src=""]. Белый список этоhttps://,http://,mailto:,tel:, и тот же-site path — так#anchor,form:<id>иmodal:…делать не выжить вlinkпараметр. Чтобы сделать компонент button Открытый form или быстро-view окно, Объявить параметрtextи поместить его value на витрине магазина key вместо того, чтобыhref:<a class="btn" href="{{btnlink}}" vz-modal="{{openmodal}}">{{btnlabel}}</a>, сopenmodalгрузmodal:product:2808?size=full. The value это Сбежавший, как любойtext, и витрина магазина строго разбирает его. form Открывается тем же способом —textпараметр, заменяемый наhref, с тех пор level-1 санитар принимаетform:<id>там. - А. `select` value снаружи `options`, а `color` Это не то, что `#hex` — empty.
- Писать `params` Неправильное гнездование level (
props.params, илиrefрядом сkind) — тот section Только с неустойкой. path этоprops.component.ref/props.component.paramsИ ничего else. - `kind` в `PUT /html-documents/{id}` — тот document's kind это fixed в Творение, field Не читается на редактировании.
- Уровень 1-1 CSS/attribute дезинфицирующий — канонические правила и внутренняя
<style>Рецепт находится в/docs/own-markup§4.А. CSS file в компоненте ZIP Сам по себе не означает, что page грузить его. - `alt` / `title` нести маркировку — эти attributes разрешены только для plain text, так
textпараметр, чей value В ней содержатся теги, в которых тот body но от этого атрибута. Это санитар, а не человек, который его оскорбляет. замена.
7. Пределы этапа 1
- Нет. `products` / `category` параметры. Замена - это строка Операция; а product Список есть data, и data принадлежит к page сборщик ("а widget И только его own"). The type Он появится вместе с этим path, не before — И до тех пор, пока не будет в schema и а manifest использовать Отказано, громко, в publish.
- Внутри компонента нет компонента, и нет
<vz-component>звонить кто-то else's HTML. - Нет патрона. TypeScript Составлено (§5), но ничего не сложено: нет
npmимпорта, нет JSX/.tsx, нет source Карты. Все import должны решиться на a file из папки. - Платформа `vz-` Ключи остаются одни, не заменяются:
{{ product.name }}проходит через это stage нетронутым и расширенным витриной's own двигатель с помощью page scope. Только голый{{key}}принадлежит компоненту.