Como tornar seu site estático dinâmico


Um site estático parece ser um bom ajuste para um projeto pequeno e estável, certo? Como um que não requer nenhum recurso avançado ou interação com os usuários. Mas como você pode aproveitar os benefícios de desempenho e ainda manter seu site estático dinâmico, personalizado e interativo?

Sempre que eu menciono um “site estático” para desenvolvedores que ainda não funcionaram com geradores de sites estáticos, eles se desaprovam. A palavra da moda está trabalhando contra mim e realmente não descreve o que você está recebendo se decidir usar um gerador de site estático (como Gatsby ou Gridsome).

Então, explico a eles como tudo funciona, incluindo recriações automáticas quando o conteúdo ou a implementação é alterado. Eles sempre têm o mesmo comentário:

“É bom e tudo, mas a pré-geração do site não funcionará para cenários dinâmicos como comércio eletrônico ou personalização. Portanto, é bom apenas para pequenos projetos”.

E isso simplesmente não é verdade. Eu vou te mostrar o porquê.

Existem duas maneiras de tornar um site estático dinâmico:

  • durante a pré-renderização do site
  • através das interações do usuário no site

Eu deveria explicar isso usando um site real. Ultimamente, tenho enfrentado a tarefa de criar um site de casamento. Eu sei, existem milhares de modelos simples para isso. Mas, sendo empregados em TI, as pessoas implicitamente esperam que o site seja o estado da arte. Então eu cedi. Eu mostro para eles.

Para a parte da implementação, decidi usar o gerador de site estático Gridsome, pois prefiro o Vue.js ao React. Eu vou estar usando um CMS sem cabeça para armazenar o conteúdo e duas funções sem servidor para lidar com a interação do usuário.

Prefere vídeo? Veja o Twitch série no YouTube. E certifique-se de siga-me no Twitch para pegar todos os fluxos futuros.

Conteúdo dinâmico durante a pré-renderização do site

Reuni todas as informações que eu conhecia antes de criar o site. Eu sei quem eu quero convidar. Sei quando o evento acontece e sei com quem estou me casando. Assim como você sabe quais produtos deseja vender ou quais serviços deseja oferecer em seu site.

Com isso em mente, criei vários modelos de conteúdo para o meu site:

  • Convidado
  • Alojamento
  • Seção
  • Item da linha do tempo

E é assim que eles se parecem no design do site:

Como conheço todos os convidados, usei o conteúdo do CMS decapitado para (automaticamente) gerar uma página separada para cada convidado (confira o rótulo URL personalizado na imagem). Como resultado, no momento da construção, os componentes conhecem o contexto do convidado. Imagine as possibilidades de personalização – posso até devolver um 404 para alguns dos meus parentes menos favoritos.

Na verdade, eu o usei para exibir saudações personalizadas e apenas itens relevantes da linha do tempo.

Se você estivesse criando um site de comércio eletrônico, poderia implementar uma página de produto que exibisse uma lista de produtos similares. Você provavelmente também vincularia aos serviços relevantes ao produto que sua empresa oferece. Você conhece todos os detalhes necessários no momento da construção.

A modelagem de conteúdo é a chave para sites de pré-construção

Eu identifiquei três modelos de conteúdo para o meu site, mas geralmente é muito mais do que isso. Uma boa maneira de abordar a modelagem de conteúdo é dar uma olhada nos wireframes do seu site futuro. Não se trata apenas de como colocar os dados no CMS, é necessário pensar em:

  • Como o conteúdo será exibido e consumido?
    Veja produtos e categorias, por exemplo. Na maioria dos casos, você os encontrará em um relacionamento N: N, mas estou apontando para o lado da implementação aqui. Pense em quão complicadas serão as consultas de dados. Ajustar os modelos de conteúdo para representar melhor a estrutura real do site pode ajudar muito na implementação.
    No meu exemplo, os itens da linha do tempo são vinculados a partir de convidados (1: N), o que permite uma implementação simples enquanto o gerenciamento de conteúdo ainda é direto. Como reorganizar a ordem dos itens.

  • Como o conteúdo está relacionado a outros itens de conteúdo?
    Qual é a relação entre produtos, pacotes de produtos, categorias, ofertas especiais ou descontos? As respostas a essas perguntas ajudarão você a escolher a ferramenta certa para conectar itens de conteúdo, como taxonomia ou itens vinculados.
  • Como o conteúdo será criado?
    Os editores entenderão a estrutura do conteúdo que você possui? Além disso, na maioria dos casos, eles não têm acesso a projetos inteiros, mas apenas a partes relevantes para eles. Sua estrutura permite um nível suficiente de granularidade de permissões? Seus modelos de conteúdo são restritivos o suficiente para evitar problemas de conteúdo ausentes no site ativo?

Há muito mais na modelagem de conteúdo. Se você estiver interessado, dê uma olhada essa grande série sobre modelagem de conteúdo escrito por Michael Kinkaid.

Componentes dinâmicos

Portanto, com os modelos de conteúdo certos, podemos gerar o site estático. Bem, pré-gerado é provavelmente um rótulo melhor para ele. Seu conteúdo não é antigo e estático – toda alteração de conteúdo efetivamente reconstruirá o site novamente.

Mas e se precisarmos interagir com os visitantes? Às vezes, precisamos obter alguma contribuição deles ou mostrar a eles um conteúdo diferente com base em suas ações. Nesses casos, podemos usar componentes dinâmicos. Eles são pré-inicializados com valores durante a criação do site, mas podem continuar interagindo com os sistemas de back-end com base nas ações dos visitantes.

No meu site, tenho um formulário que os convidados podem usar para confirmar em que tipo de acomodação estão interessados. A seleção deles precisa ser armazenada no mesmo item de conteúdo do convidado que criei originalmente no CMS sem cabeçalho.

Eu pude me comunicar com o CMS diretamente do componente no site. No entanto, estamos falando de JavaScript do lado do cliente aqui. Expor a chave seria um grande problema de segurança, mesmo que eu não espere que nenhum dos meus convidados entenda o que é uma chave de segurança ou como ela pode ser mal utilizada. Portanto, o intermediário entre o site estático e o CMS é uma função sem servidor.

Componente reativo em um site estático

Vamos começar com o componente. Eu usei Vue.js e Gridsome como SSG, mas o conceito de componente dinâmico é o mesmo, independentemente da estrutura usada. O CMS decapitado que usei aqui é Kontent. Ele tem um nível gratuito generoso, mas se você gosta de código aberto (para citar meu professor de sistemas operacionais “Não confio nele, a menos que veja seu código”) ouvi dizer Strapi é uma boa escolha

Implementação de componentes

No momento da construção, o componente receberá dados iniciais – os dados que conhecemos naquele momento específico. Se Michael selecionou uma das opções na semana passada e estamos reconstruindo o site hoje, conhecemos sua seleção.


Por outro lado, se ele ainda não interagir com o site, a seleção estará vazia.


O componente fica assim:



O Vue.js está vigiando as propriedades de dados usadas. Quando Michael altera sua seleção, o evento de alteração de dados é acionado. Observe que o nome da propriedade no objeto de inspeção deve corresponder ao nome da propriedade de dados.

Nesse ponto, precisamos armazenar sua seleção – formamos os dados e fazemos uma solicitação assíncrona para a função sem servidor – todos usando JS do lado do cliente.

...

Implementação da função sem servidor

Eu usei o Netlify para criar e implantar a função sem servidor. Se esta é sua primeira função do Netlify, fique à vontade para dar uma olhada no meu vídeo de introdução onde mostro como configurar o ambiente de desenvolvimento local do Netlify.

O CMS decapitado possui duas APIs. Um para entrega de dados – usei esse para obter todos os dados durante a criação do site – e outro para gerenciamento de dados. Na função sem servidor, preciso usar as duas APIs, para adicionar a ID do projeto e a chave da API de gerenciamento ao arquivo .env na raiz do projeto de funções do Netlify:

KONTENT_PROJECT_ID={project ID}
KONTENT_CM_KEY={management API key}

E é sempre melhor usar um SDK do que lutar com chamadas simples da API REST:

npm i @dotenv --save
npm i @kentico/kontent-delivery --save
npm i @kentico/kontent-management --save

O início da função é assim:

require("dotenv").config();
const KontentDelivery = require('@kentico/kontent-delivery')
const KontentManagement = require('@kentico/kontent-management')

A função está disponível mais ou menos publicamente – sua URL é armazenada no código JS do lado do cliente em texto sem formatação – portanto, primeiro precisamos fazer algumas verificações elementares. Toda solicitação para essa função precisa conter um parâmetro de ID na string de consulta que contém um identificador de um convidado existente. Essa é a pessoa que preencheu o formulário. Se o ID estiver ausente ou for inválido, retornamos 404.

exports.handler = async (event, context, callback) => {
  const { KONTENT_PROJECT_ID, KONTENT_CM_KEY } = process.env;
  const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: KONTENT_PROJECT_ID });
  let id = event.queryStringParameters.id;
  const invitee = await deliveryClient.items()
                    .type('invitee')
                    .elementsParameter(['accommodation'])
                    .equalsFilter('system.id', id)
                    .toPromise();
  if (invitee.items == null || invitee.items.length == 0)
  {
    return {
      statusCode: 404,
      body: `Invitee not found`
    };
}

A solicitação deliveryClient é limitada apenas a um único elemento – accommodation. Isso ocorre porque as informações do formulário não são armazenadas no diretório Convidado modelo, mas em um item vinculado do tipo Alojamento.

O modelo de conteúdo Accommodation corresponde diretamente ao formulário no site:

Queremos armazenar os dados que obtivemos do JS do lado do cliente como um novo registro. A atualização do item de conteúdo existente também é uma possibilidade, mas perderíamos todo o histórico se os convidados alterassem sua seleção no futuro.

Primeiro, observamos o ID do existente Alojamento item de conteúdo e inicialize o cliente do Content Management.

let accommodationId = invitee.items[0].accommodation.value[0].system.id;
const client = new KontentManagement.ManagementClient({ projectId: KONTENT_PROJECT_ID, apiKey: KONTENT_CM_KEY });

Então, precisamos criar uma nova variante de idioma do Alojamento item de conteúdo. Mesmo se houver apenas um idioma no projeto, o conteúdo é armazenado em um bloco separado rotulado com o nome de código desse idioma. Isso garante uma transição suave, caso você decida adicionar idiomas adicionais no futuro.

await client.createNewVersionOfLanguageVariant()
      .byItemId(accommodationId)
      .byLanguageCodename('default')
      .toPromise();

Esse código faz o mesmo que se você clicar em “Criar uma nova versão” na interface do usuário.

Em seguida, precisamos preencher a variante com dados. Os dados estão chegando como JSON no corpo da solicitação.

let accommodation = JSON.parse(event.body);
await client.upsertLanguageVariant()
    .byItemId(accommodationId)
    .byLanguageCodename('default')
    .withElements([{
        element: { codename: 'option' },
        value: [{ codename: accommodation.option }]
     }])
    .toPromise();

A última etapa é publicar a nova variante:

await client.publishOrScheduleLanguageVariant()
    .byItemId(accommodationId)
    .byLanguageCodename('default')
    .withData()
    .toPromise();
return { statusCode: 200, body: `OK` }

Problemas de CORS com o Netlify

Mesmo se você estiver executando as funções e o site estático localmente, encontrará um problema do CORS, pois as duas implementações estão sendo atendidas a partir de portas diferentes. Em todas as respostas da função sem servidor, você precisa retornar o cabeçalho “Access-Control-Allow-Origin”.

O Netlify tem uma maneira simples de lidar com isso globalmente através do netlify.toml arquivo de configuração na raiz do projeto de funções:

[build]
  Functions = "lambda"
[[headers]]
  for = "/*"
  [headers.values]
    Access-Control-Allow-Origin = "*"

Dados antigos após a atualização da página

Agora o componente reage às ações dos visitantes. No entanto, o estado inicial (que também será exibido se o visitante atualizar a página) ainda é proveniente da criação do site estático. Se um visitante alterar sua seleção, a alteração será salva no CMS, mas o site não será reconstruído.

Não seria eficiente reconstruir o site inteiro após cada interação do usuário. Mesmo se fizéssemos isso, levaria alguns segundos ou minutos até a conclusão da compilação e implantação.

Em vez disso, fazemos uma solicitação assíncrona quando o componente é renderizado pela primeira vez:


O componente será pré-inicializado com dados durante a pré-construção do site. Porém, depois que o componente é criado, ele obtém novos dados do CMS usando outra função sem servidor. Essa função é muito semelhante à anterior:

exports.handler = async (event, context, callback) => {
  const { KONTENT_PROJECT_ID } = process.env;
  let id = event.queryStringParameters.id;
  const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: KONTENT_PROJECT_ID });
  const invitee = await deliveryClient.items()
                    .queryConfig({ waitForLoadingNewContent: true })
                    .type('invitee')
                    .elementsParameter(['accommodation', 'option'])
                    .equalsFilter('system.id', id)
                    .toPromise();
  if (invitee.items == null || invitee.items[0] == null)
  {
    return {
      statusCode: 404,
      body: `Invitee not found`
    };
  }
  return {
	statusCode: 200,
	body: JSON.stringify({
		option: invitee.items[0].accommodation.value[0].codename
	})
  };
};

Nesse caso, precisamos adicionar configurações adicionais à consulta de dados – waitForLoadingNewContent. O conteúdo proveniente do CMS decapitado é armazenado em cache e entregue via CDN, para que possamos obter conteúdo desatualizado se ele tiver sido alterado nos últimos minutos. A opção de configuração garante que a resposta sempre contenha novos dados.

Portanto, o processo geral de um componente dinâmico em um site estático se parece com o seguinte:

É rápido e interativo

Veja bem, o grande benefício que os sites estáticos trazem é que todas as informações disponíveis no momento da compilação podem ser veiculadas como arquivos estáticos, que são rápidos e facilmente escaláveis ​​usando uma CDN.

Mas eles também podem fornecer funcionalidades dinâmicas que podem ser entregues através de funções sem servidor – também baratas e facilmente escaláveis.

Se você tomar meu site como exemplo – em vez de ter que implantar todo o aplicativo na nuvem, eu só precisava hospedar um monte de pequenos arquivos estáticos e duas pequenas funções sem servidor. E também sou capaz de escalar essas funções de forma independente.

Sites estáticos podem não ser a melhor opção para o desenvolvimento da Web, mas para muitos projetos trazem benefícios de clareza, desempenho e segurança, além de custos de manutenção reduzidos. Qual a sua experiência? Avise-se me.

Certifique-se de verificar também o meu Twitch streams e Canal do Youtube sobre desenvolvimento web.





Fonte

Leave a Reply

Your email address will not be published. Required fields are marked *