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á:
- As quatro tags sintáticas principais do EJS e seus usos
- Como configurar a integração do EJS com o Pug no Express
- Sintaxe de indentação do Pug, variáveis, loops e condições
- O processo completo de renderização de modelos
- Estratégias para reutilizar visualizações e layouts parciais
- Como os dados são transmitidos do roteador para o template
- Principais diferenças entre SSR e CSR e critérios de seleção
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.
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:
- O navegador envia uma solicitação HTTP
- As rotas expressas são mapeadas para suas respectivas funções de tratamento
- O handler recupera dados de um banco de dados ou de uma API
- Chame
res.render()e passe o nome do modelo e os dados - O mecanismo de modelos compila o modelo e os dados em HTML
- 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
// <%= %> 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') %>
(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
<ul>
<% items.forEach(function(item) { %>
<li>
<strong><%= item.name %></strong>
<% if (item.onSale) { %>
<span class="badge">Sale</span>
<% } %>
</li>
<% }); %>
</ul>
4. Configuração do Express + EJS
▶ Exemplo: Configurando um projeto completo com Express + EJS
Estrutura do projeto:
views/
partials/
header.ejs
footer.ejs
layout.ejs
index.ejs
app.js
npm init -y
npm install express ejs
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
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
});
});
Acessando variáveis diretamente pelo nome no modelo:
<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
//- Pug Template
doctype html
html
head
title= pageTitle
body
h1= message
p Welcome to #{siteName}
HTML compilado:
<!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
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
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
views/partials/footer.ejs
<footer>© 2026 My App</footer>
views/layout.ejs
<!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-layoutsou incluindo arquivos manualmente.
npm install express-ejs-layouts
const expressLayouts = require('express-ejs-layouts');
app.use(expressLayouts);
app.set('layout', 'layout');
▶ Exemplo: Herança de layout no Pug
views/layout.pug
doctype html
html
head
title= title
link(rel="stylesheet" href="/style.css")
body
include partials/header
block content
include partials/footer
views/index.pug
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 |
- 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:
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:
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
blog/
views/
partials/
header.ejs
footer.ejs
nav.ejs
index.ejs
post.ejs
app.js
▶ Exemplo: Ponto de entrada do aplicativo app.js
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');
});
▶ Exemplo: Navegação local – arquivo parcial partials/nav.ejs
<nav class="blog-nav">
<a href="/">All Posts</a>
<% posts.forEach(function(p) { %>
<a href="/post/<%= p.id %>"><%= p.title %></a>
<% }); %>
</nav>
▶ Exemplo: Arquivo parcial de cabeçalho local partials/header.ejs
<header class="site-header">
<h1><%= title %></h1>
</header>
▶ Exemplo: Partial de rodapé local /footer.ejs
<footer class="site-footer">
<p>© 2026 My Blog. Powered by Express + EJS.</p>
</footer>
▶ Exemplo: Visualizações da página de lista de artigos/index.ejs
<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>
▶ Exemplo: Página de detalhes da postagem views/post.ejs
<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="/">← Back to all posts</a>
Como fica a tela após abrir o aplicativo:
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-layoutspara implementar a herança de layouts; alternativamente, você pode importar manualmente visualizações parciais usando<%- include() %>; o Pug oferece suporte nativo aextends+blockpara 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, comores.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
livereloadpara 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/awaitnos modelos? R: Oawaitde 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 usandores.render().
📖 Resumo
- 1 Artigo: Conceitos-chave e como usar a interface da ferramenta de gestão do Charlie
- 2 Conceitos fundamentais e utilização do processo de renderização de modelos
- 3 Conceitos fundamentais e uso da sintaxe básica do EJS
- 4 Conceitos fundamentais e uso da configuração do Express + EJS
- 5 Conceitos fundamentais e uso da sintaxe básica do Pug
- 6 Conceitos fundamentais e uso de visualizações parciais e reutilização de layouts
- 7 conceitos-chave e métodos de uso para comparar mecanismos de modelos
- 8 conceitos-chave e métodos de aplicação na comparação entre SSR e CSR
📝 Exercícios
- Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
- Modifique o exemplo completo e adicione suas próprias extensões
- 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.
- Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
- Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.