O que é Server Side Rendering e como usar na prática

Fernando Rocha
TechBlogHotmart
Published in
7 min readAug 14, 2018

Server Side Rendering ou SSR é o processo de pegar todos os Javascript e todos os CSS de um site que, geralmente é carregado no browser (client-side), e renderizá-los como estático do lado do servidor.

Com isso podemos obter um site com um tempo de carregamento reduzido e totalmente indexável por SEO’s. Mas para entender um pouco desse tempo de carregamento, temos que entender os princípios do carregamento que é realizado no seu browser para ver o real benefício de se usar algum framework SSR.

Quando uma página é carregada ela necessita de input (que chamamos de assets). Estes são os primeiros conteúdos a serem entregues para o browser para que, a partir dali, ele possa realizar o seu trabalho e renderizar a página o mais rápido para o usuário.

Para continuarmos a abordagem, vamos entender como o browser recebe e faz o uso disso no nosso site.

Envio de arquivos do servidor para o browser

Inicialmente quando acessamos um conteúdo na web, temos uma referência no documento HTML que nos indica o que será carregado e também será a primeira coisa que o browser vai receber, esse documento contém todas as referências necessárias para os seguintes assets, como imagens, CSS e javascripts.

O browser sabe que a partir disso ele precisa ir a algum lugar para localizar e baixar esses assets, enquanto ele vai construindo o documento. Então, mesmo que o browser contenha toda a estrutura HTML, ele não poderá renderizar algo amigável até que o CSS correspondente, que contém toda a sua estilização, seja também carregado.

Uma vez feito, o seu browser poderá ter algum conteúdo relevante para entregar ao usuário. Após esse processo finalizado, o browser irá baixar todos os arquivos javascripts e é aí que costuma estar a maioria dos problemas.

Os arquivos poderão ser grandes e com o uso de uma internet ruim o tempo de carregamento poderá ser grande. Desta maneira, a experiência do seu usuário não será a ideal, o que pode piorar se a primeira renderização depender de algum arquivo javascript.

Carregamento de Arquivos pelo Browser

O grande diferencial de usar SSR é que podemos entregar, quase que imediatamente, um conteúdo significante para o usuário.

Isto acontece porque o HTML e seus principais assets são carregados em um mesmo arquivo e entregue ao client.

Portanto, eliminamos algumas etapas no processo de download de assets e o browser fica encarregado somente de carregar os componentes criados e seus fluxos que também ficam em um arquivo minificado. Ainda como boa prática, recomendo fortemente a utilização de algum CDN para realizarmos o cache desses arquivos.

Benefícios ao se usar SSR

  • Carregamento mais rápido na renderização inicial
  • Por termos toda a estrutura pronta, temos uma página HTML totalmente indexável. Nesse caso ótimo para SEO e Crawlers.

Diferenças entre CSR e SSR

Com dois infográficos bem simples irei compilar todas as ideias passadas anteriormente para poder explicar melhor ainda as diferenças entre oClient Side Rendering e o Server Side Rendering.

SSR na Prática

Com toda a evolução de Javascript, hoje temos uma série de frameworks que abstraem a complexidade da criação de estrutura e até de desenvolvimento e nesse caso eu apresentarei um pouco do framework NextJS.

Benefícios ao se utilizar NextJS

  • Server Rendered por Default
  • Divisão automática de código para carregamentos de páginas mais rápidos
  • Criação de rotas simples
  • Ambiente baseado em Webpack com suporte a HMR (Hot Module Replacement)
  • Capaz de integrar com Node.js HTTP server
  • Configurações customizavéis de Babel e Webpack

Instalação via NPM

npm install — save next react react-dom

Instalação Via Yarn

yarn add next react react-dom

Começando com NextJS

Criação do package.json com o seguinte conteúdo.

{
"name": "**Nome do Projeto**",
"version": "0.0.1",
"scripts": {
"dev": "next"
},
"dependencies": {
"next": "7.0.1",
"react": "16.4.2",
"react-dom": "16.4.2"
}
}

O NextJS trabalha com uma estrutura básica para criação de pasta com roteamento simples.

Isso significa que toda página deverá ser criada dentro de um diretório chamado pages e qualquer página criada dentro desse diretório já será acessado pelo nome colocado, devido ao roteamento baseado em estrutura de arquivo.

Estrutura de arquivo

Então, se eu criar um arquivos dentro do pages com o nome posts ela estará acessível em /posts .

Roteamento baseado em estrutura de pastas

Com poucas linhas de código já temos algo funcionando e ainda contamos com o HMR. Tudo que fizer no código será enviado automaticamente para o browser sem termos que dar refresh para visualizar as alterações.

Mas sabemos que isso não é o bastante para criar um sistema robusto e que conte com busca de dados em uma API e coisas do tipo. Para tal o NextJS já proporciona uma estrutura inicial que será utilizada tanto Server Side quanto Client Side, a função getInitialProps.

import React from 'react'
import axios from 'axios'

export default class extends React.Component {
static async getInitialProps ({ req }) {
const res = await axios.get('https://api.github.com/repos/zeit/next.js')
return { stars: res.data.stargazers_count }
}

render() {
const { stars } = this.props

return (
<div>
Next stars: <strong>{stars}</strong>
</div>
)
}
}

O método getInitialProps recebe o contexto do objeto com as seguintes propriedades:

  • pathname — nome do caminho da URL
  • query — queryString da url
  • asPath — String do caminho atual
  • req — Objeto HTTP da chamada (servidor apenas)
  • res — Objeto HTTP de resposta (servidor apenas)
  • Err — Objeto de error se houve alguma falha durante a renderização

Vale ressaltar que o getInitialProps será sempre o primeiro método a ser acessado e ele será sempre disparado no javascript que fica dentro do pages. As demais chamadas poderão ser realizadas dentro de funções e de preferência após o componente carregar.

Com o NextJS ainda é possível injetar dados no <head> da páginas. Este caso é muito utilizado, caso você queira setar algum meta tag somente depois que o conteúdo for carregado, por dependência de alguma chamada em uma API, o acesso poderá ser feito da seguinte maneira:

import React from 'react'
import Head from 'next/head'

class HtmlHead extends React.Component {
render () {
const { title, url, description } = this.props
return (
<Head>
<title>{titleTuned}</title>
<meta name="description" content={description} />
<meta name="abstract" content={description} />
<meta property="og:title" content={title} />
<meta property="og:url" content={url} />
</Head>
)
}
}
export default HtmlHead

Ainda é possível manipular o conteúdo a ser carregado no documento adicionando um wrapper. Por padrão, o NextJS permite configurar qualquer propriedade entre o <html> e o <body>, essas tags estão fora do escopo das páginas. Mas como faço se eu quiser customizar essas tags?

Nesse caso, isso é possível criando um arquivo dentro do pages chamado _document.js, permitindo carregar estilos, scripts e meta tags. Veja no exemplo abaixo:

import Document, { Head, Main, NextScript } from "next/document";

export default class MyDocument extends Document {
render() {
return (
<html>
<Head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="/_next/static/style.css" />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}

Como dito anteriormente, o NextJS permite o uso de servidores NodeJS, embora o comportamento padrão do NextJS é de servir o que estiver armazenado dentro de pages como sua própria rota.

No entanto, à medida que um aplicação cresce, talvez seja necessário ajustar isso ou adicionar uma lógica personalizada do lado do servidor. Felizmente, o NextJS expõe todo o servidor ExpressJS para nós.

Para realizar tais mudanças é necessário realizar alguns ajustes no nosso código. Primeiro abra o package.json e troque o conteúdo de “dev” para node server.js.

"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
}

Agora, iremos criar um arquivo chamado server.js na raiz do projeto. Veja a seguir um template sugerido na documentação do NextJS:

const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
createServer((req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl

if (pathname === '/a') {
app.render(req, res, '/b', query)
} else if (pathname === '/b') {
app.render(req, res, '/a', query)
} else {
handle(req, res, parsedUrl)
}
}).listen(3000, err => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})

Com isso temos nosso próprio servidor, que nos possibilita gerenciar as rotas e dizer para o NextJS qual página renderizar quando houver uma chamada no nosso servidor. O responsável por fazer essa renderização é o app.render().

Nele podemos passar a referência de qual página renderizar usando a referência de rotas app.render(req, res, ‘/posts’, query).

Acredito que com esse guia, você terá o conhecimento, mesmo que embrionário sobre os pontos positivos de se utilizar SSR, e como começar a construir sua próxima aplicação utilizando-se de um framework SSR.

Peace!

--

--