Bibliotecas / App Layout

App Layout

Gerador de layout HTML usado em nossos aplicativos SPA.

@gravity-ui/app-layout · npm package CI

Instalar

npm install --save-dev @gravity-ui/app-layout

Uso

Com 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: 'Página inicial',
      bodyContent: {
        root: 'Olá mundo!',
      },
    }),
  );
});

app.listen(3000);

onde

interface RenderParams<Data, Plugins> {
  // Qualquer dado compatível com JSON, será definido em window.__DATA__ na página
  data?: Data;
  // favicon
  icon?: Icon;
  // nonce a ser definido nas tags apropriadas
  nonce?: string;
  // atributos da tag base
  base?: Base;

  // opções comuns
  // Título da página
  title: string;
  // idioma da página, será definido na tag html
  lang?: string;
  isMobile?: boolean;

  // atributos html
  htmlAttributes?: string;
  // conteúdo da tag header
  // tags meta
  meta?: Meta[];
  // tags link
  links?: Link[];
  // tags script
  scripts?: Script[];
  // folhas de estilo
  styleSheets?: Stylesheet[];
  // tags script com código inline
  inlineScripts?: string[];
  // tags style com estilos inline
  inlineStyleSheets?: string[];

  // conteúdo da tag body
  bodyContent?: {
    // nome da classe para a tag body
    className?: string;
    // atributos do body
    attributes?: string;
    // conteúdo do body antes da tag div com id root
    beforeRoot?: string;
    // conteúdo innerHtml da tag div com id root
    root?: string;
    // conteúdo do body depois da tag div com id root
    afterRoot?: string;
  };
  // opções de plugins
  pluginsOptions?: Partial<PluginsOptions<Plugins>>;
}

Base

Descreve a tag base:

interface Base {
  href?: string;
  target?: HTMLBaseElement['target'];
}

Exemplo:

renderLayout({
  title: 'Página inicial',
  base: {target: '_top'},
});

Será renderizado como:

<base target="_top" />

Meta

Descreve a tag meta:

interface Meta {
  name: string;
  content: string;
}

Exemplo:

const meta = [
  {name: 'description', content: 'algum texto'},
  {name: 'robots', content: 'noindex'},
  {name: 'og:title', content: 'Algum título'},
];

Será renderizado como:

<meta name="description" content="algum texto" />
<meta name="robots" content="noindex" />
<meta property="og:title" content="Algum título" />

Icon

Descreve o favicon da página:

interface Icon {
  type?: string;
  sizes?: string;
  href?: string;
}

O valor padrão é:

const icon = {
  type: 'image/png',
  sizes: '16x16',
  href: '/favicon.png',
};

Descreve a tag link:

interface Link {
  as?: string;
  href: string;
  rel?: string;
  type?: string;
  sizes?: string;
  title?: HTMLLinkElement['title'];
  crossOrigin?: '' | 'anonymous' | 'use-credentials';
}

Exemplo:

const link = {
  href: 'myFont.woff2',
  rel: 'preload',
  as: 'font',
  type: 'font/woff2',
  crossOrigin: 'anonymous',
};

será renderizado como:

<link href="myFont.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous" />

Scripts

Descreve o link para um script com preload:

interface Script {
  src: string;
  defer?: boolean;
  async?: boolean;
  crossOrigin?: '' | 'anonymous' | 'use-credentials';
  type?: 'importmap' | 'module' | string;
}

Exemplo:

const script = {
  src: 'url/to/script',
  defer: true,
  async: false,
  crossOrigin: 'anonymous',
};

será renderizado como:

<link href="url/to/script" rel="preload" as="script" crossorigin="anonymous" />

<script src="url/to/script" defer="true" async="false" crossorigin="anonymous" nonce="..."></script>

Folhas de estilo

Descrevem o link para estilos:

interface Stylesheet {
  href: string;
}

Exemplo:

const styleSheet = {
  href: 'url/to/stylesheet',
};

será renderizado como:

<link href="url/to/stylesheet" rel="stylesheet" />

Plugins

A função de renderização pode ser estendida por plugins. Um plugin pode reescrever o conteúdo de renderização definido pelo usuário. Um plugin é um objeto com as propriedades name e apply:

interface Plugin<Options = any, Name = string> {
  name: Name;
  apply: (params: {
    options: Options | undefined; // passado através da função `renderLayout` no parâmetro `pluginsOptions`.
    commonOptions: CommonOptions;
    renderContent: RenderContent;
    /** @deprecated use `renderContent.helpers` instead */
    utils: RenderHelpers;
  }) => void;
}

interface CommonOptions {
  name: string;
  title: string;
  lang?: string;
  isMobile?: boolean;
}

export interface HeadContent {
  base?: Base;
  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;
}

Existem alguns plugins neste pacote:

Google analytics

Adiciona um contador do Google Analytics à página.

Uso:

import {createRenderFunction, createGoogleAnalyticsPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createGoogleAnalyticsPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        googleAnalytics: {
          useBeaconTransport: true, // habilita o uso de navigator.sendBeacon
          counter: {
            id: 'algum id',
          },
        },
      },
    }),
  );
});

Opções do plugin:

interface GoogleAnalyticsCounter {
  id: string;
}

interface GoogleAnalyticsOptions {
  useBeaconTransport?: boolean;
  counter: GoogleAnalyticsCounter;
}

Yandex Metrika

Adiciona contadores do Yandex Metrika à página.

Uso:

import {createRenderFunction, createYandexMetrikaPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createYandexMetrikaPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        yandexMetrika: {
          counter: {
            id: 123123123,
            defer: true,
            clickmap: true,
            trackLinks: true,
            accurateTrackBounce: true,
          },
        },
      },
    }),
  );
});

Opções do 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

Adiciona scripts e estilos do arquivo de manifesto de assets do webpack.

Uso:

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: 'Página inicial',
      pluginsOptions: {
        layout: {
          name: 'home',
        },
      },
    }),
  );
});

Opções do plugin:

export interface LayoutOptions {
  name: string;
  prefix?: string;
}

@gravity-ui/uikit

Adiciona atributos ao body.

Uso:

import {createRenderFunction, createUikitPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createUikitPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        uikit: {
          theme: 'dark',
          direction: 'ltr',
        },
      },
    }),
  );
});

Opções do plugin:

interface UikitPluginOptions {
  theme: string;
  direction?: 'ltr' | 'rtl';
}

Remote Versions

Adiciona informações de versões de microfrontends à página.

Este plugin cria um objeto global window.__REMOTE_VERSIONS__ contendo as versões dos microfrontends fornecidas, que pode ser usado por module federation ou arquiteturas de microfrontend semelhantes para determinar quais versões de módulos remotos carregar.

Pode ser usado em combinação com App Builder e a opção moduleFederation.remotesRuntimeVersioning para carregar automaticamente módulos remotos com as versões correspondentes.

Uso:

import {createRenderFunction, createRemoteVersionsPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createRemoteVersionsPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        remoteVersions: {
          header: '1.2.3',
          footer: '2.1.0',
          sidebar: '0.5.1',
        },
      },
    }),
  );
});

Opções do plugin:

type RemoteVersionsPluginOptions = Record<string, string>;

Helpers

Existe um helper para criar todos os 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: 'Página inicial',
        pluginsOptions: {
            layout: {
                name: 'home'
            },
            googleAnalytics: {
                counter: {...}
            },
            yandexMetrika: {
                counter: {...}
            },
        },
    }));
})

Uso alternativo

Com renderizadores de partes generateRenderContent, renderHeadContent, renderBodyContent via streaming de 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',
  });

```markdown
# @gravity/uikit

Um wrapper simples para o Axios que fornece funcionalidades adicionais para facilitar o desenvolvimento de aplicações React.

## Instalação

```bash
npm install @gravity/uikit
# ou
yarn add @gravity/uikit

Uso

Este wrapper estende o Axios, então você pode usá-lo da mesma forma que usaria o Axios. No entanto, ele também fornece algumas funcionalidades adicionais.

Configuração

Você pode configurar o wrapper Axios com opções adicionais, como:

  • baseUrl: A URL base para todas as requisições.
  • headers: Cabeçalhos padrão a serem incluídos em todas as requisições.
  • interceptors: Interceptadores para requisições e respostas.
import axios from '@gravity/uikit';

const api = axios.create({
  baseUrl: 'https://api.example.com',
  headers: {
    'Content-Type': 'application/json',
  },
  interceptors: {
    request: [
      (config) => {
        // Faça algo com a configuração da requisição
        return config;
      },
    ],
    response: [
      (response) => {
        // Faça algo com a resposta
        return response;
      },
    ],
  },
});

Requisições

Você pode fazer requisições HTTP da mesma forma que faria com o Axios:

// GET
api.get('/users').then((response) => {
  console.log(response.data);
});

// POST
api.post('/users', { name: 'John Doe' }).then((response) => {
  console.log(response.data);
});

// PUT
api.put('/users/1', { name: 'Jane Doe' }).then((response) => {
  console.log(response.data);
});

// DELETE
api.delete('/users/1').then((response) => {
  console.log(response.data);
});

Funcionalidades Adicionais

Interceptadores de Requisição e Resposta

O wrapper permite que você defina interceptadores de requisição e resposta para modificar requisições antes que elas sejam enviadas ou respostas antes que sejam retornadas para o seu código.

api.interceptors.request.use((config) => {
  // Adicionar um token de autenticação aos cabeçalhos
  config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
  return config;
});

api.interceptors.response.use(
  (response) => {
    // Lidar com respostas de sucesso
    return response;
  },
  (error) => {
    // Lidar com erros de resposta
    if (error.response.status === 401) {
      // Redirecionar para a página de login
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

Gerenciamento de Erros

O wrapper fornece um gerenciamento de erros mais robusto, permitindo que você lide com diferentes códigos de status de erro de forma centralizada.

api.get('/users').catch((error) => {
  if (error.response) {
    // A requisição foi feita e o servidor respondeu com um status code
    // que está fora do range de 2xx
    console.error(error.response.data);
    console.error(error.response.status);
    console.error(error.response.headers);
  } else if (error.request) {
    // A requisição foi feita mas nenhuma resposta foi recebida
    console.error(error.request);
  } else {
    // Algo aconteceu na configuração da requisição que disparou um erro
    console.error('Error', error.message);
  }
});

Exemplo de Código

import React, { useState, useEffect } from 'react';
import axios from '@gravity/uikit';

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await axios.get('/users');
        setUsers(response.data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []);

  if (loading) {
    return <div>Carregando usuários...</div>;
  }

  if (error) {
    return <div>Erro ao carregar usuários: {error.message}</div>;
  }

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default UserList;

Contribuição

Sinta-se à vontade para contribuir com este projeto. Por favor, consulte o arquivo CONTRIBUTING.md para obter mais informações.

Licença

Este projeto está licenciado sob a Licença MIT.

Sobre a biblioteca
Apoie a biblioteca com uma estrela
Versão
2.4.0
Última atualização
07.04.2026
Repositório
github.com/gravity-ui/app-layout
Licença
MIT License
Colaboradores