Контракт frontend и backend
Контракт frontend и backend — это набор соглашений по структуре каталогов, именам блоков и HTML-фрагментам, которые формируются на backend и используются во frontend.
Структура
Backend ожидает структуру вида www/{source-path}/{entity}/{name}/{file}, где {source-path} —
один из путей, заданных в src[].path файла www/config/frontend.json:
-
www/frontend/— штатный путь изsrc[].path; -
{entity}— имя сущности frontend-слоя (например,themes,templates,apps,componentsи т.д.); -
{name}— имя блока внутри сущности (например,light,default,hero,sidebarи т.д.); -
{file}— имя entry-файла или Twig-шаблона (например,entry.js,entry.custom.js,index.html.twig,view.html.twigи т.д.).
Mount и state
Backend формирует связанные фрагменты:
-
mount-контейнер:
<div data-medusa-app-id="{app-id}" data-medusa-state-id="medusa-state-{name}-{index}"></div> -
state-скрипт с raw JSON-данными:
<script type="application/json" id="medusa-state-{name}-{index}">{json}</script>
Frontend читает эти значения через цепочку:
mount('{app-id}')
.state(state)
.each((node, props) => {...});
Чтение JSON-данных выполняется методом state('medusa-state-{name}-{index}').json().
Twig-секции
При смешанном подходе Vite-секции передаются в Twig-контекст по фиксированному соответствию:
-
секция
preload-> контекстhead.preload; -
секция
css-> контекстhead.styles; -
секция
html-> контекстbody.content; -
секция
js-> контекстbody.scripts; -
секция
json-> контекстbody.state.
Результат сборки
Frontend-сборка формирует артефакты в каталоге out.path из www/config/frontend.json. При
штатном out.path = /build это www/build/ и манифест
www/build/.vite/manifest.json. Backend получает по манифесту итоговые теги ассетов с хэшами.
Пример смешанного подхода
use Medusa\Helpers\Frontend;
$html = Frontend::twig('themes')
->include('default', [
'html' => [
'lang' => 'ru',
],
'head' => [
'title' => 'Приветствие',
],
])
->with(
Frontend::vite('themes')->include('default', [
'title' => 'Добро пожаловать!',
'subTitle' => 'Это страница приветствия!',
])
)
->html(true);
Пример содержимого файла www/frontend/themes/default/index.html.twig:
<!doctype html>
<html lang="{{ html.lang | default('en') }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ head.title | default('Page') }}</title>
{{ head.preload | join('\n\t') | raw }}
{{ head.styles | join('\n\t') | raw }}
</head>
<body>
{{ body.content | join('\n\t') | raw }}
{{ body.scripts | join('\n\t') | raw }}
{{ body.state | join('\n\t') | raw }}
</body>
</html>
Пример содержимого файла www/frontend/themes/default/entry.js:
import {mount} from '@shared/js/mount';
import {state} from '@shared/js/state';
import {createApp} from 'vue';
import DefaultTheme from './DefaultTheme.vue';
import './style.css';
mount('default')
.state(state)
.each((node, props) => {
createApp(DefaultTheme, props).mount(node);
});
Пример содержимого файла www/frontend/themes/default/DefaultTheme.vue:
<script setup>
import {defaultThemeProps} from './props.js';
defineProps(defaultThemeProps);
</script>
<template>
<h1>{{ title }}</h1>
<p>{{ subTitle }}</p>
</template>
Пример содержимого файла www/frontend/themes/default/props.js:
export const defaultThemeProps = {
title: {
type: String,
required: true,
},
subTitle: {
type: String,
required: true,
},
};
Пример содержимого файла www/frontend/themes/default/style.css:
h1 {
color: blue;
}
p {
color: green;
}