App Layout
@gravity-ui/app-layout ·

Installation
npm install --save-dev @gravity-ui/app-layout
Utilisation
Avec express :
import express from 'express';
import {createRenderFunction} from '@gravity-ui/app-layout';
const app = express();
const renderLayout = createRenderFunction();
app.get('/', function (req, res) {
res.send(
renderLayout({
// RenderParams
title: 'Page d\'accueil',
bodyContent: {
root: 'Bonjour le monde !',
},
}),
);
});
app.listen(3000);
où
interface RenderParams<Data, Plugins> {
// Toutes données compatibles JSON, sera défini sur window.__DATA__ sur la page
data?: Data;
// favicon
icon?: Icon;
// nonce à définir sur les balises appropriées
nonce?: string;
// options communes
// Titre de la page
title: string;
// langue de la page, sera défini sur la balise html
lang?: string;
isMobile?: boolean;
// attributs html
htmlAttributes?: string;
// contenu de la balise header
// balises meta
meta?: Meta[];
// balises link
links?: Link[];
// balises script
scripts?: Script[];
// feuilles de style
styleSheets?: Stylesheet[];
// balises script avec code inline
inlineScripts?: string[];
// balises style avec styles inline
inlineStyleSheets?: string[];
// contenu de la balise body
bodyContent?: {
// nom de classe pour la balise body
className?: string;
// attributs de la balise body
attributes?: string;
// contenu de la balise body avant la balise div avec id root
beforeRoot?: string;
// contenu innerHtml de la balise div avec id root
root?: string;
// contenu de la balise body après la balise div avec id root
afterRoot?: string;
};
// options des plugins
pluginsOptions?: Partial<PluginsOptions<Plugins>>;
}
Meta
Décrit la balise meta :
interface Meta {
name: string;
content: string;
}
Exemple :
const meta = [
{name: 'description', content: 'quelque texte'},
{name: 'robots', content: 'noindex'},
{name: 'og:title', content: 'Un titre'},
];
Sera rendu comme :
<meta name="description" content="quelque texte" />
<meta name="robots" content="noindex" />
<meta property="og:title" content="Un titre" />
Icon
Décrit le favicon de la page :
interface Icon {
type?: string;
sizes?: string;
href?: string;
}
La valeur par défaut est :
const icon = {
type: 'image/png',
sizes: '16x16',
href: '/favicon.png',
};
Links
Décrit la balise link :
interface Link {
as?: string;
href: string;
rel?: string;
type?: string;
sizes?: string;
title?: HTMLLinkElement['title'];
crossOrigin?: '' | 'anonymous' | 'use-credentials';
}
Exemple :
const link = {
href: 'myFont.woff2',
rel: 'preload',
as: 'font',
type: 'font/woff2',
crossOrigin: 'anonymous',
};
sera rendu comme :
<link href="myFont.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous" />
Scripts
Décrit le lien vers un script avec préchargement :
interface Script {
src: string;
defer?: boolean;
async?: boolean;
crossOrigin?: '' | 'anonymous' | 'use-credentials';
type?: 'importmap' | 'module' | string;
}
Exemple :
const script = {
src: 'url/to/script',
defer: true,
async: false,
crossOrigin: 'anonymous',
};
sera rendu comme :
<link href="url/to/script" rel="preload" as="script" crossorigin="anonymous" />
<script src="url/to/script" defer="true" async="false" crossorigin="anonymous" nonce="..."></script>
Feuilles de style
Décrivent le lien vers les styles :
interface Stylesheet {
href: string;
}
Exemple :
const styleSheet = {
href: 'url/to/stylesheet',
};
sera rendu comme :
<link href="url/to/stylesheet" rel="stylesheet" />
Plugins
La fonction de rendu peut être étendue par des plugins. Un plugin peut réécrire le contenu de rendu défini par l'utilisateur.
Un plugin est un objet avec les propriétés name et apply :
interface Plugin<Options = any, Name = string> {
name: Name;
apply: (params: {
options: Options | undefined; // passé via la fonction `renderLayout` dans le paramètre `pluginsOptions`.
commonOptions: CommonOptions;
renderContent: RenderContent;
/** @deprecated utiliser `renderContent.helpers` à la place */
utils: RenderHelpers;
}) => void;
}
interface CommonOptions {
name: string;
title: string;
lang?: string;
isMobile?: boolean;
}
export interface HeadContent {
scripts: Script[];
helpers: RenderHelpers;
links: Link[];
meta: Meta[];
styleSheets: Stylesheet[];
inlineStyleSheets: string[];
inlineScripts: string[];
title: string;
}
export interface BodyContent {
attributes: Attributes;
beforeRoot: string[];
root?: string;
afterRoot: string[];
}
export interface RenderContent extends HeadContent {
htmlAttributes: Attributes;
bodyContent: BodyContent;
}
export interface RenderHelpers {
renderScript(script: Script): string;
renderInlineScript(content: string): string;
renderStyle(style: Stylesheet): string;
renderInlineStyle(content: string): string;
renderMeta(meta: Meta): string;
renderLink(link: Link): string;
attrs(obj: Attributes): string;
}
Il existe quelques plugins dans ce package :
Google analytics
Ajoute un compteur Google Analytics sur la page.
Utilisation :
import {createRenderFunction, createGoogleAnalyticsPlugin} from '@gravity-ui/app-layout';
const renderLayout = createRenderFunction([createGoogleAnalyticsPlugin()]);
app.get((req, res) => {
res.send(
renderLayout({
title: 'Page d\'accueil',
pluginsOptions: {
googleAnalytics: {
useBeaconTransport: true, // active l'utilisation de navigator.sendBeacon
counter: {
id: 'un identifiant',
},
},
},
}),
);
});
Options de plugin :
interface GoogleAnalyticsCounter {
id: string;
}
interface GoogleAnalyticsOptions {
useBeaconTransport?: boolean;
counter: GoogleAnalyticsCounter;
}
Yandex Metrika
Ajoute des compteurs Yandex Metrika à la page.
Utilisation :
import {createRenderFunction, createYandexMetrikaPlugin} from '@gravity-ui/app-layout';
const renderLayout = createRenderFunction([createYandexMetrikaPlugin()]);
app.get((req, res) => {
res.send(
renderLayout({
title: 'Page d\'accueil',
pluginsOptions: {
yandexMetrika: {
counter: {
id: 123123123,
defer: true,
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
},
},
},
}),
);
});
Options de plugin :
export type UserParams = {
[x: string]: boolean | string | number | null | UserParams;
};
export interface MetrikaCounter {
id: number;
defer: boolean;
clickmap: boolean;
trackLinks: boolean;
accurateTrackBounce: boolean | number;
webvisor?: boolean;
nonce?: string;
encryptedExperiments?: string;
triggerEvent?: boolean;
trackHash?: boolean;
ecommerce?: boolean | string;
type?: number;
userParams?: UserParams;
}
export type MetrikaOptions = {
src?: string;
counter: MetrikaCounter | MetrikaCounter[];
};
Layout
Ajoute les scripts et styles du fichier de manifeste des assets webpack.
Utilisation :
import {createRenderFunction, createLayoutPlugin} from '@gravity-ui/app-layout';
const renderLayout = createRenderFunction([createLayoutPlugin({manifest: 'path/to/assets-manifest.json', publicPath: '/build/'})]);
app.get((req, res) => {
res.send(
renderLayout({
title: 'Page d\'accueil',
pluginsOptions: {
layout: {
name: 'home',
},
},
}),
);
});
Options de plugin :
export interface LayoutOptions {
name: string;
prefix?: string;
}
@gravity-ui/uikit
Ajoute des attributs au corps (body).
Utilisation :
import {createRenderFunction, createUikitPlugin} from '@gravity-ui/app-layout';
const renderLayout = createRenderFunction([createUikitPlugin()]);
app.get((req, res) => {
res.send(
renderLayout({
title: 'Page d\'accueil',
pluginsOptions: {
uikit: {
theme: 'dark',
direction: 'ltr',
},
},
}),
);
});
Options de plugin :
interface UikitPluginOptions {
theme: string;
direction?: 'ltr' | 'rtl';
}
Remote Versions
Ajoute des informations sur les versions des microfrontends à la page.
Ce plugin crée un objet global window.__REMOTE_VERSIONS__ contenant les versions des microfrontends fournies, qui peut être utilisé par le module federation ou des architectures de microfrontends similaires pour déterminer quelles versions des modules distants charger.
Il peut être utilisé en combinaison avec App Builder et l'option moduleFederation.remotesRuntimeVersioning pour charger automatiquement les modules distants avec les versions correspondantes.
Utilisation :
import {createRenderFunction, createRemoteVersionsPlugin} from '@gravity-ui/app-layout';
const renderLayout = createRenderFunction([createRemoteVersionsPlugin()]);
app.get((req, res) => {
res.send(
renderLayout({
title: 'Page d\'accueil',
pluginsOptions: {
remoteVersions: {
header: '1.2.3',
footer: '2.1.0',
sidebar: '0.5.1',
},
},
}),
);
});
Options de plugin :
type RemoteVersionsPluginOptions = Record<string, string>;
Helpers
Il existe une aide pour créer tous les plugins :
import {createMiddleware, createDefaultPlugins} from '@gravity-ui/app-layout';
const renderLayout = createRenderFunction(
createDefaultPlugins({layout: {manifest: 'path/to/assets-manifest.json'}})
);
app.get((req, res) => {
res.send(renderLayout({
title: 'Page d\'accueil',
pluginsOptions: {
layout: {
name: 'home'
},
googleAnalytics: {
counter: {...}
},
yandexMetrika: {
counter: {...}
},
},
}));
})
Utilisation alternative
Avec les générateurs de parties generateRenderContent, renderHeadContent, renderBodyContent via le streaming HTML :
import express from 'express';
import htmlescape from 'htmlescape';
import {
generateRenderContent,
renderHeadContent,
renderBodyContent,
createDefaultPlugins,
} from '@gravity-ui/app-layout';
const app = express();
app.get('/', async function (req, res) {
res.writeHead(200, {
'Content-Type': 'text/html',
'Transfer-Encoding': 'chunked',
});
const plugins = createDefaultPlugins({layout: {manifest: 'path/to/assets-manifest.json'}});
const content = generateRenderContent(plugins, {
title: 'Page d\'accueil',
});
const {htmlAttributes, helpers, bodyContent} = content;
<!DOCTYPE html>
<html ${helpers.attrs({...htmlAttributes})}>
<head>
${renderHeadContent(content)}
</head>
<body ${helpers.attrs(bodyContent.attributes)}>
${renderBodyContent(content)}
`);
const data = await getUserData();
res.write(`
${content.renderHelpers.renderInlineScript(`
window.__DATA__ = ${htmlescape(data)};
`)}
</body>
</html>
`);
res.end();
});
app.listen(3000);