Fragmentos de conteúdo visual - Enviar entrega com o URL de publicação visual-content-fragments-deliver-with-the-publish-url

Quando um fragmento de conteúdo baseado em um modelo com modelos do HTML anexados é publicado, o HTML renderizado desse fragmento é disponibilizado por meio do nível de publicação do Adobe Experience Manager (AEM) as a Cloud Service em um URL com esta estrutura:

https://publish-p<programId>-e<envId>.adobeaemcloud.com/adobe/stable/previewtemplates/contentFragments/<templateId>/<fragmentId>/<variation>.html

Esta URL retorna um documento HTML independente (incluindo CSS e estrutura embutidos) que pode ser incorporado em qualquer contexto da Web.

Técnicas de incorporação — visão geral embedding-techniques-overview

Há três abordagens distintas para consumir o HTML a partir de um fragmento de conteúdo visual em uma página de host. Cada uma delas vem com características distintas de isolamento de estilo, comportamento de layout, acessibilidade e complexidade.

Elemento em linha
iframe
Elemento personalizado + DOM de sombra
Mecanismo
fetch() a URL, insira o HTML de resposta em um <div> via innerHTML
<iframe src="publishURL">
Definir um Elemento personalizado, fetch() o HTML, inserir em uma raiz DOM Sombra anexada
Isolamento de estilo
Nenhum — o CSS do fragmento vaza para a página do host e o CSS do host afeta o fragmento
Completo — contexto de navegação separado, isolamento de CSS completo
Forte — os blocos de limite DOM de sombra hospedam cascata CSS; os estilos de fragmento permanecem encapsulados
Participação de layout
Completo — o conteúdo faz parte do fluxo normal do documento, responde ao dimensionamento de flexbox/grade/container
Nenhum — o iframe tem dimensões fixas; requer width/height explícito ou redimensionamento automático baseado em JS
Total — o elemento personalizado participa do fluxo normal do documento de host como qualquer outro elemento DOM
Acessibilidade (a11y)
Melhor: o conteúdo está na árvore DOM principal, totalmente navegável por leitores de tela e tecnologia assistiva
Moderado — contexto de navegação separado pode confundir a navegação do leitor de tela; requer o atributo title
Bom — o conteúdo está no mesmo documento; o DOM paralelo pode ser percorrido pelas tecnologias de assistência modernas
SEO
Fraco — o conteúdo carregado via JS fetch() não é indexado pela maioria dos rastreadores
Insatisfatório — o conteúdo do iframe normalmente não é indexado no contexto da página principal
Insatisfatório — igual ao inline; o conteúdo buscado por JS não é rastreável
Tempo de execução do JavaScript
Compartilhado — mesmo contexto de janela/documento; risco de colisões de script se o fragmento contiver <script> tags
Isolado — contexto de janela separada; sem risco de colisão
Compartilhado — mesmo contexto de janela, mas com escopo DOM; os scripts dentro da raiz sombra são executados no contexto de host
Suporte entre origens
Requer cabeçalhos CORS no URL de publicação (o serviço configura isso)
Funciona nativamente — iframes carregam conteúdo de várias origens sem o CORS
Requer cabeçalhos CORS no URL de publicação (o mesmo que em linha)
Complexidade de implementação
Mínimo — algumas linhas de JS
Trivial — JS zero necessário; HTML puro
Baixa — ~20 linhas de JS para a definição de Elemento personalizado, reutilizável na página
Mais adequado para
Protótipo, conteúdo confiável de mesma origem, contextos em que a integração de layout é essencial e os conflitos de CSS são gerenciáveis
Incorporação rápida, conteúdo em sandbox, cenários entre origens em que o CORS não está disponível, conteúdo que deve ser totalmente isolado
Uso de produção — equilibra isolamento, participação de layout e acessibilidade (recomendado para Componentes principais do AEM e sites externos)

Elemento em linha (fetch + innerHTML) inline-element-fetch-and-innerhtml

A abordagem mais simples:

  1. buscar o URL de publicação
  2. inserir o HTML em um elemento de contêiner

Um exemplo de incorporação de elemento em linha:

<div id="cf-container"></div>
<script>
  fetch("<publish-url>")
    .then(r => r.ok ? r.text() : Promise.reject(r.status))
    .then(html => {
      document.getElementById("cf-container").innerHTML = html;
    })
    .catch(err => console.error("Failed to load fragment", err));
</script>

Quando usar:

  • Protótipo rápido ou páginas de prova de conceito
  • Contextos de mesma origem nos quais você controla a página do host e os estilos de fragmento
  • Quando a flexibilidade máxima de layout é mais importante que o encapsulamento de estilo
CAUTION
Risco de colisão de CSS
Os estilos em linha do fragmento (incluindo seus blocos <style>, declarações de face de fonte e seletores de elemento) se mesclam na cascata da página de host.
Isso pode causar substituições de estilo não intencionais em ambas as direções.
Use essa técnica somente quando você puder tolerar ou gerenciar ativamente esses conflitos.

iframe iframe

Carregue a URL de Publicação diretamente como o src de um <iframe>. Nenhuma JavaScript é necessária.

Um exemplo de incorporação de iframe:

<iframe
  src="<publish-url>"
  title="Content Fragment Preview"
  width="100%"
  height="600"
  frameborder="0"
  style="border: none;"
></iframe>

Também é possível redimensionar automaticamente o iframe (opcional).

Para dimensionar dinamicamente o iframe à sua altura de conteúdo (evitando barras de rolagem), use um padrão postMessage ou uma biblioteca apropriada.

Um exemplo de abordagem mais leve é:

<iframe id="cf-iframe" src="<publish-url>" title="Content Fragment Preview"
  width="100%" frameborder="0" style="border:none; overflow:hidden;"
  onload="this.style.height = this.contentDocument.documentElement.scrollHeight + 'px';"
></iframe>
WARNING
A abordagem de redimensionamento automático onload acima só funciona para iframes de mesma origem.
Para URLs de Publicação de entre origens, você precisaria de uma solução baseada em postMessage ou para definir uma altura fixa.

Quando usar:

  • Bloco de incorporação do Edge Delivery Services (a integração padrão — consulte a seção abaixo)
  • Contextos em que o isolamento completo de CSS/JS é crítico
  • Incorporação entre origens, em que o CORS não está configurado
  • Integração rápida com código zero (basta colar o URL)

Defina um Elemento Personalizado <cf-visualization> reutilizável que busque a URL de publicação e injete a HTML em uma raiz DOM de sombra encapsulada.

Esse elemento fornece:

  • Isolamento de DOM de sombra
    • A marcação e os estilos do fragmento são encapsulados em uma raiz de sombra, evitando colisões com a cascata CSS da página do host.
  • Participação de layout em linha
    • O conteúdo renderizado participa do fluxo normal do documento de host, respondendo ao dimensionamento do container e aos contextos de flexbox/grade sem o gerenciamento manual de dimensões.
  • Contexto de navegação único
    • Nenhum contexto de documento secundário é criado; o conteúdo do fragmento compartilha o tempo de execução do JavaScript da página e é totalmente navegável por tecnologias assistivas.
  • Sobrecarga mínima
    • Uma única chamada fetch recupera a HTML pré-renderizada do nível de publicação. Nenhuma estrutura de renderização do lado do cliente é necessária.
IMPORTANT
Essa é a abordagem recomendada para o uso de produção e é a técnica usada pelos Componentes principais do AEM.

Para definir o Elemento personalizado, inclua o script a seguir uma vez por página. Todas as instâncias do <cf-visualization> na página usarão esta definição:

<script>
  class CfVisualization extends HTMLElement {
    connectedCallback() {
      const src = this.getAttribute("src");
      if (!src) return;

      const shadow = this.attachShadow({ mode: "open" });

      fetch(src)
        .then((r) => (r.ok ? r.text() : Promise.reject(r.status)))
        .then((html) => {
          shadow.innerHTML = html;
        })
        .catch((err) => {
          console.error("cf-visualization: failed to load", src, err);
        });
    }
  }

  if (!customElements.get("cf-visualization")) {
    customElements.define("cf-visualization", CfVisualization);
  }
</script>

Para usar o elemento personalizado:

<cf-visualization src="<publish-url>"></cf-visualization>

Quando usar:

  • Páginas do AEM Sites que usam os Componentes principais (esse é o comportamento incorporado)
  • Sites externos/de terceiros que precisam de uma integração limpa e reutilizável
  • Qualquer contexto em que você precise do isolamento de estilo e da participação de fluxo de layout

Integração com o Edge Delivery Services (bloco de inserção) integration-with-edge-services-embed-block

No Edge Delivery Services, a URL de Publicação é consumida por meio de um bloco incorporado, que a renderiza como <iframe>.

  1. Verifique se o bloco Incorporado existe em seu projeto.

    Se o projeto EDS ainda não incluir o bloco incorporado, copie-o do repositório aem-block-collection:

    code language-cmdline
    # From the aem-block-collection repo, copy blocks/embed/ into your project's blocks/ directory
    cp -r aem-block-collection/blocks/embed/ your-eds-project/blocks/embed/
    
  2. Criar o incorporado no editor de criação de documentos (no Edge Delivery Services)

    Na Criação de documentos, os blocos são representados como tabelas. Para adicionar um fragmento de conteúdo visual incorporado:

    table 0-row-1 1-row-1
    incorporar
    (cole o URL de publicação como um hiperlink)

    Como alternativa, se o projeto ou Sidekick estiver configurado com o bloco Incorporado na biblioteca de blocos, você poderá inseri-lo por meio do menu de barra e colar o URL de publicação no conteúdo do bloco.

  3. Resultado

    O bloco Incorporado renderiza a URL de Publicação dentro de um <iframe>. O conteúdo do fragmento é carregado no isolamento de CSS completo no layout de página do EDS.

Integração - AEM Sites com componentes principais integration-aem-sites-with-core-components

O componente principal do fragmento de conteúdo (core/wcm/components/contentfragment/v1/contentfragment) tem suporte interno para renderização de fragmentos de conteúdo visuais usando a técnica Elemento do cliente + DOM de sombra.

Como funciona:

  • Modo de autor:

    Quando o displayMode do componente é definido como vcf, a clientlib de criação (vcfRenderer.js) busca o fragmento HTML da API de visualização e o renderiza embutido na tela de criação.

    Um exemplo de um endpoint de visualização do autor é:

    code language-html
    GET /adobe/sites/cf/fragments/{fragmentId}/preview?templateId={templateId}&variation={variation}
    
  • Modo de publicação:

    Na página publicada (wcmmode.disabled), o modelo HTL renderiza um script integrado que busca da URL de publicação e injeta a HTML em uma raiz DOM de sombra.

    Um exemplo de Fragmento de conteúdo visual dos Componentes principais (templates.html):

    code language-html
    <div class="cmp-contentfragment cmp-contentfragment--vcf"
       data-cmp-contentfragment-id="{fragmentId}"
       data-cmp-contentfragment-vcf-template="{templateId}"
       data-cmp-contentfragment-variation="{variation}">
      <!-- Only rendered when wcmmode.disabled (publish) -->
      <div data-vcf-url="{vcfPublishUrl}" class="cmp-contentfragment__vcf-loader" style="display:none"></div>
      <script>
          (function() {
              var script = document.currentScript;
              var loader = script.previousElementSibling;
              var el = script.parentElement;
              if (!el || !loader) return;
              var url = loader.dataset.vcfUrl;
              if (!url) return;
              loader.remove();
              var shadow = el.attachShadow({ mode: "open" });
              var body = document.createElement("body");
              body.style.display = "none";
              shadow.appendChild(body);
              fetch(url)
                  .then(function(r) { return r.ok ? r.text() : Promise.reject(r.status); })
                  .then(function(html) {
                      body.innerHTML = html;
                      body.style.display = "";
                  });
          })();
      </script>
    </div>
    

    Publicar formato de URL:

    O Modelo Sling (ContentFragmentImpl) cria a URL de Publicação usando o seguinte padrão:

    code language-html
    /adobe/experimental/previewtemplates-expires-20260301/contentFragments/{templateId}/{fragmentId}/{variation}.html
    

    Esse URL relativo é resolvido no host de publicação no tempo de execução.

Integração com sites externos integration-with-external-sites

Para sites que não sejam da AEM, use a técnica Elemento do cliente + DOM de sombra. Isso proporciona uma integração limpa e declarativa sem dependências de estrutura.

Um exemplo é:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Product Page</title>
</head>
<body>
  <h1>Product Details</h1>
  <p>Some host-page content here...</p>

  <!-- Embed the Content Fragment visualization -->
  <cf-visualization
    src="https://publish-p12345-e67890.adobeaemcloud.com/adobe/experimental/previewtemplates-expires-20260301/contentFragments/product_template/abc-123/master.html"
  ></cf-visualization>

  <p>More host-page content below the fragment...</p>

  <!-- Custom Element definition (include once) -->
  <script>
    class CfVisualization extends HTMLElement {
      connectedCallback() {
        const src = this.getAttribute("src");
        if (!src) return;
        const shadow = this.attachShadow({ mode: "open" });
        fetch(src)
          .then(r => r.ok ? r.text() : Promise.reject(r.status))
          .then(html => { shadow.innerHTML = html; })
          .catch(err => console.error("cf-visualization: failed to load", src, err));
      }
    }
    if (!customElements.get("cf-visualization")) {
      customElements.define("cf-visualization", CfVisualization);
    }
  </script>
</body>
</html>
NOTE
Você pode colocar vários elementos <cf-visualization> na mesma página com URLs src diferentes. A definição do Elemento personalizado só precisa ser incluída uma vez.

Considerações sobre CORS e segurança cors-and-security-considerations

Preocupação
Detalhes
CORS
O serviço de Visualização de Fragmento de Conteúdo configura o CORS no caminho /adobe/** com origens permitidas configuráveis.
As técnicas Elemento Embutido (fetch + innerHTML) 1 e Elemento do Cliente + DOM de Sombra (que usam fetch()) exigem que a origem da página de host esteja na lista de permissões.
A técnica iFrame não requer o CORS.
CSP/X-Frame-Options
O serviço não define cabeçalhos Content-Security-Policy ou X-Frame-Options no HTML publicado. Se o CDN ou o Dispatcher adicionar esses cabeçalhos, verifique se eles permitem enquadramento (para iFrame) ou acesso fetch() (para DOM embutido/sombra) das origens do host.
Confiabilidade de conteúdo
O HTML publicado é pré-renderizado a partir dos dados de Fragmento de conteúdo criados usando os modelos Handlebars gerenciados pelo serviço. Ela não inclui scripts gerados pelo usuário. No entanto, como em qualquer injeção innerHTML, verifique se a origem de origem é confiável.

Escolha a técnica apropriada choose-the-appropriate-technique

Use o seguinte como guia de decisão para ajudá-lo a escolher a técnica apropriada:

Cenário
Solução
Precisa de JavaScript zero e isolamento total?
Iframe
Precisa de participação de fluxo de layout com isolamento de estilo?
Elemento personalizado + DOM de sombra (recomendado)
Precisa de protótipos mais rápidos, mesma origem e os conflitos de CSS são aceitáveis?
Elemento em linha
Incorporando no Edge Delivery Services?
Bloco incorporado (iframe sob o capô)
Incorporando nas páginas do AEM Sites?
Componente principal (DOM de sombra, interno)

Recursos adicionais additional-resources

Recursos adicionais estão disponíveis:

recommendation-more-help
experience-manager-cloud-service-help-main-toc