Node.js: Templates e SSR

Última atualização: 2026-08-26

1. História: A interface da ferramenta de gerenciamento do Charlie

Charlie é desenvolvedor de back-end em uma startup. A equipe precisava de uma ferramenta de gerenciamento interna para visualizar pedidos, estatísticas de usuários e o status do sistema. Seus colegas sugeriram usar React ou Vue, mas Charlie achou que essas estruturas eram pesadas demais para uma ferramenta interna como essa — com apenas algumas páginas e interações simples, a renderização do lado do servidor com um mecanismo de modelos seria suficiente. Ele escolheu o EJS porque, essencialmente, trata-se apenas de HTML com algumas tags, o que torna incrivelmente fácil começar a usá-lo. Em um dia, ele já havia criado uma interface administrativa completa com layouts reutilizáveis usando Express e EJS.

Você aprenderá:



2. Processo de renderização de modelos

A função principal de um mecanismo de modelos é mesclar arquivos de modelo com dados para gerar a sequência de caracteres HTML final, que é então retornada ao navegador.

100%
flowchart LR
    A[Browser Request] --> B[Express Routing]
    B --> C[Retrieve Data]
    C --> D["res.render()"]
    D --> E[EJS / Pug Compilation]
    E --> F[Generate HTML]
    F --> G[HTTP Response]

Todo o processo pode ser resumido da seguinte forma:

  1. O navegador envia uma solicitação HTTP
  2. As rotas expressas são mapeadas para suas respectivas funções de tratamento
  3. O handler recupera dados de um banco de dados ou de uma API
  4. Chame res.render() e passe o nome do modelo e os dados
  5. O mecanismo de modelos compila o modelo e os dados em HTML
  6. O HTML é enviado de volta ao navegador como uma resposta HTTP


3. Sintaxe básica do EJS

O EJS (Embedded JavaScript) é o mecanismo de modelos mais intuitivo — os modelos são simplesmente HTML puro, com a lógica incorporada por meio de tags <% %>.

▶ Exemplo: As quatro tags principais do EJS

JAVASCRIPT
// <%= %> Output the escaped value (Safe, prevents XSS)
<p>Hello, <%= userName %>!</p>

// <% %> Execution Logic,No output
<% if (isAdmin) { %>
  <span>Admin Panel</span>
<% } %>

// <%- %> Output Raw HTML (Not escaped, use with caution)
<%- articleHtml %>

// <%- include %> Importing Subtemplates
<%- include('partials/header') %>
▶ Experimente

(1) Referência rápida à sintaxe do EJS

Tag Função Saída Escape Exemplo
<%= %> Valor da variável de saída Sim Sim <%= name %>
<%- %> Gerar conteúdo original Sim Não <%- html %>
<% %> Lógica de execução Não <% if (x) { %>
<%# %> Comentário Não <%# comment %>
<%- include('x') %> Incluir submodelos Sim Não <%- include('nav') %>

▶ Exemplo: Ciclos e condições no EJS

JAVASCRIPT
<ul>
  <% items.forEach(function(item) { %>
    <li>
      <strong><%= item.name %></strong>
      <% if (item.onSale) { %>
        <span class="badge">Sale</span>
      <% } %>
    </li>
  <% }); %>
</ul>
▶ Experimente

4. Configuração do Express + EJS

▶ Exemplo: Configurando um projeto completo com Express + EJS

Estrutura do projeto:

TEXT 📖 Somente leitura
views/
  partials/
    header.ejs
    footer.ejs
  layout.ejs
  index.ejs
app.js
BASH
npm init -y
npm install express ejs
JAVASCRIPT
const express = require('express');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Dashboard',
    user: { name: 'Charlie', role: 'admin' },
    stats: { orders: 128, users: 56 }
  });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

(1) Documentação da API principal

API Finalidade Exemplo
app.set('view engine', 'ejs') Definir o mecanismo de modelos padrão Não é necessário incluir a extensão do arquivo ao renderizar
app.set('views', path) Definir diretório de modelos O padrão é ./views
res.render(view, data) Renderizar o modelo e responder res.render('index', {title: 'Hi'})
res.render(view, data, callback) Recupera a string HTML após a renderização Pode ser usado para enviar e-mails e em outros cenários

▶ Exemplo: Passando dados como segundo parâmetro para res.render

JAVASCRIPT
app.get('/profile/:id', (req, res) => {
  const user = { id: req.params.id, name: 'Alice', bio: 'Full-stack dev' };
  res.render('profile', {
    user: user,
    pageTitle: user.name + "'s Profile",
    isLoggedIn: true
  });
});
▶ Experimente

Acessando variáveis diretamente pelo nome no modelo:

JAVASCRIPT
<h1><%= pageTitle %></h1>
<p>Name: <%= user.name %></p>
<p>Bio: <%= user.bio %></p>


5. Sintaxe básica do Pug

O Pug (anteriormente conhecido como Jade) usa recuo em vez de tags de fechamento, o que torna sua sintaxe extremamente concisa, mas tem uma curva de aprendizado mais íngreme do que o EJS.

▶ Exemplo: Sintaxe básica do Pug

TEXT 📖 Somente leitura
//- Pug Template
doctype html
html
  head
    title= pageTitle
  body
    h1= message
    p Welcome to #{siteName}

HTML compilado:

TEXT 📖 Somente leitura
<!DOCTYPE html>
<html>
  <head><title>My Page</title></head>
  <body>
    <h1>Hello</h1>
    <p>Welcome to MySite</p>
  </body>
</html>

(1) Referência rápida à sintaxe do Pug

Sintaxe Função Exemplo
tag= value Vinculação de propriedades h1= title
#{expr} Interpolação p Hello #{name}
- code Executar JS - const x = 1
if/else if/else Condição if (admin) ...
each val in arr loop each item in list
extends layout Herdar layout extends layout
block name Definição/Preenchimento de bloco block content
include path Importar submodelos include header

▶ Exemplo: Loops e condições em Pug

TEXT 📖 Somente leitura
ul
  each item in items
    li
      strong= item.name
      if item.onSale
        span.badge Sale


6. Reutilização de visualizações parciais e layouts

O principal benefício de um mecanismo de modelos é evitar a repetição do cabeçalho, do rodapé e da estrutura HTML em todas as páginas.

▶ Exemplo: Layout EJS + Visualização parcial

views/partials/header.ejs

JAVASCRIPT
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>
▶ Experimente

views/partials/footer.ejs

JAVASCRIPT
<footer>&copy; 2026 My App</footer>

views/layout.ejs

JAVASCRIPT
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('partials/header') %>
  <main>
    <%- body %>
  </main>
  <%- include('partials/footer') %>
</body>
</html>

O EJS não possui um mecanismo de layout nativo; ele deve ser implementado usando o middleware express-ejs-layouts ou incluindo arquivos manualmente.

BASH
npm install express-ejs-layouts
JAVASCRIPT
const expressLayouts = require('express-ejs-layouts');
app.use(expressLayouts);
app.set('layout', 'layout');

▶ Exemplo: Herança de layout no Pug

views/layout.pug

TEXT 📖 Somente leitura
doctype html
html
  head
    title= title
    link(rel="stylesheet" href="/style.css")
  body
    include partials/header
    block content
    include partials/footer

views/index.pug

TEXT 📖 Somente leitura
extends layout

block content
  h1= pageTitle
  p Welcome!

(1) Comparação de estratégias de layout

Imóvel EJS Pug
Herança de layout Requer middleware express-ejs-layouts Nativo extends + block
Importação local <%- include('partial') %> include partial
Passagem de parâmetros para modelos filhos <%- include('x', {data}) %> Passagem de dados pelo modelo pai
Suporte a vários blocos Limitado Suporte nativo a vários blocos


7. Comparação de mecanismos de modelos

(1) EJS x Pug x Handlebars

Recurso EJS Pug Handlebars
Estilo de sintaxe Tags HTML + JS Recuadas, sem tags de fechamento Mustache {{}}
Curva de aprendizagem Mais baixa Média Baixa
Raciocínio lógico JS completo JS completo Restrito (sem JS arbitrário)
Arquitetura/Herança Requer middleware Suporte nativo Requer configuração adicional
Saída de escape <%= %> = ou #{} {{}}
Saída original <%- %> !{} {{{}}}
Casos de uso Introdução rápida / Ferramentas internas Simplicidade em primeiro lugar / Projetos full-stack Segurança em primeiro lugar / Renderização para vários dispositivos
Atividade da comunidade Alta Alta Média

  1. 8 Comparação entre SSR e CSR

(1) Tabela de principais diferenças

Dimensão SSR (Renderização do lado do servidor) CSR (Renderização do lado do cliente)
Local de renderização Servidor Navegador
Velocidade de carregamento da primeira tela Rápida (o HTML carrega imediatamente) Lenta (é preciso esperar que o JS carregue e seja executado)
Otimização para SEO Alta Baixa (O conteúdo pode não estar visível para os rastreadores)
Complexidade da interação Baixa (a página é atualizada a cada solicitação) Alta (SPA, sem atualização da página)
Carga do servidor Alta Baixa
Tecnologias típicas EJS / Pug / PHP React / Vue / Angular
Casos de uso Ferramentas internas / Blogs / Páginas de SEO Interações complexas / Aplicativos SPA

▶ Exemplo: Diferenças nas respostas entre SSR e CSR

Resposta SSR — O navegador recebe o código HTML completo diretamente:

TEXT 📖 Somente leitura
GET /about
→ Server-Side Rendering HTML Return in Full
→ Display directly in the browser,No need to wait JS

Resposta ao CSR — O navegador recebe um shell vazio + pacote de JS:

TEXT 📖 Somente leitura
GET /about
→ Server Response <div id="app"></div> + bundle.js
→ Browser Download JS,Execute Rendering
→ Users view the content(Duration of the white screen)


8. Exemplo completo: Página do blog do EJS

Crie um aplicativo completo de blog com Express + EJS, incluindo layouts reutilizáveis, uma lista de posts, detalhes dos posts e navegação local.

▶ Exemplo:(1) Estrutura do projeto

TEXT 📖 Somente leitura
blog/
  views/
    partials/
      header.ejs
      footer.ejs
      nav.ejs
    index.ejs
    post.ejs
  app.js

▶ Exemplo: Ponto de entrada do aplicativo app.js

JAVASCRIPT
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');
app.use(expressLayouts);
app.set('layout', 'layout');

const posts = [
  { id: 1, title: 'Getting Started with Node.js', author: 'Charlie', date: '2026-06-20', excerpt: 'Learn the basics of Node.js runtime.' },
  { id: 2, title: 'Understanding Express Middleware', author: 'Alice', date: '2026-06-25', excerpt: 'Deep dive into middleware patterns.' },
  { id: 3, title: 'Template Engines Compared', author: 'Bob', date: '2026-07-01', excerpt: 'EJS vs Pug vs Handlebars showdown.' }
];

app.get('/', (req, res) => {
  res.render('index', { title: 'Blog Home', posts: posts });
});

app.get('/post/:id', (req, res) => {
  const post = posts.find(p => p.id === parseInt(req.params.id));
  if (!post) return res.status(404).send('Post not found');
  res.render('post', { title: post.title, post: post });
});

app.listen(3000, () => {
  console.log('Blog running on http://localhost:3000');
});
▶ Experimente

▶ Exemplo: Navegação local – arquivo parcial partials/nav.ejs

JAVASCRIPT
<nav class="blog-nav">
  <a href="/">All Posts</a>
  <% posts.forEach(function(p) { %>
    <a href="/post/<%= p.id %>"><%= p.title %></a>
  <% }); %>
</nav>
▶ Experimente

▶ Exemplo: Arquivo parcial de cabeçalho local partials/header.ejs

JAVASCRIPT
<header class="site-header">
  <h1><%= title %></h1>
</header>
▶ Experimente
JAVASCRIPT
<footer class="site-footer">
  <p>&copy; 2026 My Blog. Powered by Express + EJS.</p>
</footer>
▶ Experimente

▶ Exemplo: Visualizações da página de lista de artigos/index.ejs

JAVASCRIPT
<h2>Latest Posts</h2>
<div class="post-list">
  <% posts.forEach(function(post) { %>
    <article class="post-card">
      <h3><a href="/post/<%= post.id %>"><%= post.title %></a></h3>
      <p class="meta">By <%= post.author %> on <%= post.date %></p>
      <p><%= post.excerpt %></p>
    </article>
  <% }); %>
</div>
▶ Experimente

▶ Exemplo: Página de detalhes da postagem views/post.ejs

JAVASCRIPT
<h2><%= post.title %></h2>
<p class="meta">By <%= post.author %> on <%= post.date %></p>
<div class="post-body">
  <p><%= post.excerpt %></p>
  <p>Full article content goes here...</p>
</div>
<a href="/">&larr; Back to all posts</a>
▶ Experimente

Como fica a tela após abrir o aplicativo:

TEXT 📖 Somente leitura
http://localhost:3000       → Article List Page
http://localhost:3000/post/1 → Article Details Page
http://localhost:3000/post/2 → Second Article

❓ Perguntas Frequentes

P: O SSR ainda é necessário hoje em dia? R: O SSR ainda é ideal para ferramentas de gerenciamento interno, páginas com altos requisitos de SEO (como blogs e páginas de produtos em sites de comércio eletrônico) e sites simples que priorizam o conteúdo — sem a complexidade adicional das estruturas de front-end.

P: O que é melhor, EJS ou Pug? R: O EJS é mais próximo do HTML nativo, tem uma curva de aprendizado baixa e é adequado para desenvolvimento rápido; o Pug tem uma sintaxe mais concisa, mas exige que se acostume com suas regras de indentação, o que o torna adequado para equipes que priorizam a concisão do código. O EJS é recomendado para iniciantes.

P: Como posso reutilizar layouts em modelos? R: O EJS usa o middleware express-ejs-layouts para implementar a herança de layouts; alternativamente, você pode importar manualmente visualizações parciais usando <%- include() %>; o Pug oferece suporte nativo a extends + block para implementar a herança de layouts.

P: O que é o segundo parâmetro de res.render? R: É um objeto de dados cujas propriedades podem ser usadas diretamente como variáveis no modelo, como res.render('index', {title: 'Hi'}); no modelo, você escreveria <%= title %>.

P: O mecanismo de modelos suporta recarregamento dinâmico? R: Durante o desenvolvimento, você pode usar o nodemon para monitorar alterações nos arquivos e reiniciar automaticamente o serviço. Você também pode usar o middleware livereload para habilitar a atualização automática do navegador, eliminando a necessidade de atualizar manualmente a página.

P: Qual é a diferença entre <%- e <%=? R: <%= Exibe valores com escape HTML para evitar ataques XSS; essa é a opção segura. <%- Exibe conteúdo bruto, sem escape, e só deve ser usada quando o conteúdo for confiável ou ao incorporar submodelos.

P: Posso usar async/await nos modelos? R: O await de nível superior não é compatível com os modelos EJS. Você deve concluir toda a recuperação assíncrona de dados na rota e, em seguida, passar os resultados para o modelo usando res.render().


📖 Resumo


📝 Exercícios

  1. Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
  2. Modifique o exemplo completo e adicione suas próprias extensões
  3. Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
  4. Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
  5. Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%