Novidades no Angular 17

Novidades no Angular 17
Rafaela Petelin Silvério
Rafaela Petelin Silvério

Compartilhe

Introdução

A comunidade Angular recebeu com entusiasmo a tão aguardada versão 17, que transformou significativamente o framework, ampliando o foco em desempenho e na experiência da pessoa desenvolvedora, o que levou essa atualização a ser conhecida como "Angular Renaissance", ou seja, Renascimento Angular.

Vamos conhecer o Angular 17 e como ele busca otimizar a experiência do desenvolvimento em Angular?

Banner promocional da Alura, com um design futurista em tons de azul, apresentando o texto

Identidade Visual e nova documentação

Para refletir a evolução do framework, a equipe do Angular lançou uma nova identidade visual, que segundo eles representa o futuro dessa ferramenta, projetado para significar a evolução do Angular para um framework moderno.

Novo logo do Angular, composto por um escudo hexagonal com um gradiente de roxo até rosa e uma letra A na cor branca, centralizada e em negrito.

Além disso, também foi lançada uma nova documentação, o angular.dev. Este novo site apresenta uma estrutura renovada, guias atualizados e conteúdo aprimorado, cheio de tutoriais incorporados implementados com WebContainers e até mesmo um Playground.

A atual versão do Angular.dev é uma prévia beta, a antiga documentação do Angular ainda está funcionando, mas a equipe está planejando tornar o Angular.dev o site padrão do Angular na versão 18.

Agora vamos mergulhar nos recursos da versão 17 do Angular!

Control flow integrado

A partir de pesquisas a equipe do Angular identificou que muitas pessoas desenvolvedoras ainda passavam por perrengues com a sintaxe *ngIf, *ngSwitch, e *ngFor.

Pensando na melhoria da experiência da pessoa desenvolvedora, eles lançaram uma nova sintaxe para blocos de renderização de templates mais otimizada e intuitiva em que, internamente, o compilador Angular transforma essa sintaxe em instruções JavaScript que executam o control flow, o lazy loading e muito mais.

Além disso, essa mudança é mais um passo em direção ao objetivo de remover por completo a biblioteca zone.js, que já começou através do uso dos Signals na v16. Com o uso das diretivas *ngIf, *ngSwitch, e *ngFor, seria muito complexo remover por completo essa biblioteca.

A nova sintaxe se aproxima muito mais do JavaScript, por isso é mais intuitiva e exige menos pesquisas de documentação, além de ter uma melhor verificação de tipos, graças a um type narrowing otimizado, melhorias de desempenho e está automaticamente disponível para uso nos templates sem exigir importações adicionais.

Declarações condicionais

Para observarmos a mudança de sintaxe, criamos um componente para renderizar a seguinte lista de tópicos de estudo:

listaDeEstudos: String[] = ['HTML', 'CSS', 'JavaScript', 'TypeScript', 'Angular', 'React', 'Vue'];

Nesse componente, precisaremos utilizar uma estrutura condicional para, se houver itens na lista, exibir a quantidade. Se não houverem itens, a mensagem “ A lista está vazia!” deve aparecer.

Fazendo isso com *ngIf na versão 16 do Angular, vamos obter o seguinte código:

A seguir, temos a estrutura de um *ngIf:

<section *ngIf="listaDeEstudos.length > 0; else listaVazia">

  <p> A lista tem {{ listaDeEstudos.length }} itens</p>

</section>

<ng-template #listaVazia>
  <p>A lista está vazia!</p>
</ng-template>

Observe que foi preciso estruturar um ng-template para o caso da condição não ser verdadeira. O código também segue uma sintaxe muito específica do Angular, o que dificulta o entendimento para quem vai ter seu primeiro contato com o framework.

Com a instrução if built-in, esse bloco condicional será semelhante a:


@if (listaDeEstudos.length > 0) {
  A lista tem {{ listaDeEstudos.length }} itens
} @else {
  A lista está vazia!
}

O código se torna mais limpo, conciso e mais compreensível para quem não conhece o Angular mas já teve contato com JavaScript, enquanto obtemos o mesmo resultado desejado de exibição da quantidade de itens na lista.

A possibilidade de fornecer o conteúdo do @else de forma direta simplifica muito a escrita em relação ao *ngIf, além de que a nova sintaxe permite o uso do @else if, o que anteriormente era impossível.

Também conseguimos observar essas melhorias no *ngSwitch. Para isso criamos uma variável nivelEstudante do tipo String que poderá ser definida como “iniciante” ou “intermediario” em nosso componente.

Caso seja definida como “Iniciante”, devemos exibir a mensagem “Comece por HTML, CSS e JS”. Caso seja definida como “intermediario”, vamos exibir a mensagem “Escolha seu framework!”. Como caso padrão, vamos exibir a mensagem “Vamos estudar?”.

Observe como isso ficaria com o *ngSwitch na versão 16:

<div [ngSwitch]="nivelEstudante">
  <p *ngSwitchCase="'iniciante'"> Comece por HTML, CSS e JS </p>
  <p *ngSwitchCase="'intermediario'"> Escolha seu framework! </p>
  <p *ngSwitchDefault> Vamos estudar? </p>
</div>

Com a nova sintaxe, o mesmo código se transforma em algo mais sucinto e semelhante ao JavaScript, como percebemos no exemplo à seguir:

@switch (nivelEstudante) {
  @case ('iniciante') { <p> Comece por HTML, CSS e JS </p> }
  @case ('intermediario') { <p> Escolha seu framework! </p> }
  @default { <p> Vamos estudar? </p> }
}

Loop for integrado

Um problema comum no desempenho nas aplicações é observado pela falta de função trackBy no *ngFor. Por isso, a nova sintaxe do loop @for garante que o track seja obrigatório para garantir um desempenho melhor.

O novo loop também tem o atalho @empty para exibição de coleções que não tenham nenhum item. Mas o maior destaque vem para o novo algoritmo de diffing, que é mais otimizado em relação ao *ngFor, com um tempo de execução até 90% mais rápido.

Vamos aplicar esse loop ao nosso componente de lista de estudos para entender seu funcionamento. Usando o *ngFor, vamos listar os tópicos de estudo na tela:

  <ul *ngFor="let conteudo of listaDeEstudos">
    <li>{{ conteudo }}</li>
  </ul>

Como comentamos anteriormente, o *ngFor não obriga que seja usado um parâmetro “track”. Esse parâmetro é necessário para renderizar e atualizar os itens da lista de forma eficiente, pois é uma função que retorna um identificador único para cada item da lista, ajudando o Angular a rastrear quais itens foram adicionados, removidos ou reordenados na lista, e evitando a renderização desnecessária de itens inalterados. Isso pode melhorar o desempenho e o uso de memória da aplicação, especialmente ao lidar com listas grandes ou dinâmicas.

Por isso, esse é um ponto que chama atenção na nova sintaxe do for. Aplicando a renderização da lista de estudos no novo padrão da versão 17, podemos visualizar o uso do track:

<ul>
  @for (conteudo of listaDeEstudos; track $index) {
    <li>{{ conteudo }}</li>
  } @empty {
    <p>Preencha a lista para visualizar itens!</p>
  }
</ul>

Por fim, o control flow integrado já está disponível como prévia para desenvolvedores na versão 17 e um dos objetivos do projeto do time Angular é viabilizar uma migração totalmente automatizada. Para testar essa funcionalidade em seus projetos existentes, você pode utilizar o seguinte comando de migração:

ng generate @angular/core:control-flow

Deferrable views

O lazy loading é uma técnica importante quando criamos páginas web pois tem como objetivo carregar recursos apenas quando são necessários. No contexto do Angular, isso significa carregar módulos ou componentes somente quando são requisitados, melhorando o tempo de carregamento inicial da aplicação.

A versão 17 do Angular trouxe uma nova ferramenta para otimizar esse processo: as deferrable views ou visualizações adiáveis. Esse mecanismo pode ser utilizado para tornar seus apps mais rápidos e com melhor desempenho.

A estrutura se baseia em um bloco de código adiável @defer que pode ser acompanhado de um bloco @placeholder, que vai conter um conteúdo que será exibido enquanto o bloco adiado não estiver disponível, um bloco @loading que traz a mensagem de carregamento e um bloco @error para exibição em caso de erro.

Para exemplificar o uso desta ferramenta, vamos utilizar o projeto que utilizamos nos exemplos anteriores, a lista de estudos.

No projeto criamos um novo componente chamado conteudo-estudos, que será uma página com texto e imagens. No nosso componente de lista, adicionamos um botão que leva para o novo componente.

Nesse novo componente, temos um grande volume de texto e algumas imagens:

Esta é a captura de tela da página web do projeto, com fundo branco. No canto superior esquerdo há o título "Titulo" em texto preto. Em seguida há 8 parágrafos de texto lorem ipsum em preto. Abaixo, no canto esquerdo há 3 imagens dispostas verticalmente: o logo de escudo do HTML 5, na cor laranja com o número 5 em branco no centro, o logo de escudo do CSS 3, na cor azul com o número 3 em branco no centro e o logo do JavaScript, um quadrado amarelo com o texto “JS” na cor preta.

O template desse componente está assim:

<h1>Título</h1>
<p>
<!-- blocos de texto lorem ipsum omitidos -->
</p>

<img src="../../assets/logo-html.png" alt="Logo do HTML" width="100px" height="100px"><br>
<img src="../../assets/logo-css.png" alt="Logo do CSS" width="100px" height="100px"><br>
<img src="../../assets/logo-javascript.png" alt="Logo do JavaScript" width="100px" height="100px"><br>

Podemos utilizar o bloco @defer para fazer o carregamento sob demanda dessas imagens e otimizar o desempenho da página. Esse bloco permite receber diferentes parâmetros para o controle do carregamento, sendo o parâmetro padrão idle, que será carregado apenas quando o navegador atingir a inatividade:

<!-- @defer em modo idle -->
@defer{
  <img src="../../assets/logo-html.png" alt="Logo do Angular" width="100px" height="100px"><br>
}

Ou seja, no bloco acima a imagem será exibida quando o navegador tiver carregado todo o conteúdo e ficado inativo. Assim, o adiamento no carregamento da imagem fica quase imperceptível:

Esta é um gif da tela do projeto, com fundo branco. O gif transita entre o primeiro componente de lista, onde há o botão “Quero estudar!” que quando é clicado redireciona para o segundo componente. Nesse componente, no canto superior esquerdo há o título "Título" em texto preto, seguido por 8 parágrafos de texto lorem ipsum em preto. Abaixo, ao rolar a barra de rolagem, no canto esquerdo há uma imagem disposta verticalmente: o logo de escudo do HTML 5, na cor laranja com o número 5 em branco no centro. O carregamento dessa imagem é imediato.

Para manipularmos as outras imagens, vamos conhecer alguns outros parâmetros e, para entendermos melhor, selecionei a opção de “slow 3g” na aba network das ferramentas do desenvolvedor. Assim, conseguiremos observar como o @defer faz diferença no melhor desempenho em redes mais lentas.

No modo on viewport, a imagem só será carregada quando estiver em um ponto visível da tela. Observe:

<!-- modo on viewport -->
@defer (on viewport) {
  <img src="../../assets/logo-css.png" alt="Logo do Angular" width="100px" height="100px"><br>
} @placeholder {
  <p>Futura imagem</p>
}@loading (minimum 2s) {
  <p>Carregando imagem...</p>
}
Esta é um gif da tela do projeto, com fundo branco. O gif apresenta o carregamento do componente de conteúdo, onde no canto superior esquerdo há o título "Titulo" em texto preto, seguido por 8 parágrafos de texto lorem ipsum em preto. Abaixo, ao rolar a barra de rolagem, no canto esquerdo há duas imagens dispostas verticalmente: o logo de escudo do HTML 5, na cor laranja com o número 5 em branco no centro. O carregamento dessa imagem é imediato. E a segunda imagem que é o logo de escudo do CSS 3, na cor azul com o número 3 em branco no centro. O carregamento dessa imagem é lento pois só começa quando descemos a barra de rolagem até sua posição, antes de a imagem aparecer é exibido o texto “Carregando imagem…”.

No exemplo acima, o Angular primeiro renderiza o conteúdo do bloco de espaço reservado. A imagem apenas começa a ser carregada quando descemos a barra de rolagem até onde ela está localizada e ela entra em visualização. É possível observar seu carregamento lento através do bloco @loading com a mensagem “Carregando imagem…” que é exibido em um tempo de no mínimo 2 segundos antes da imagem ser carregada. Assim que o carregamento é concluído, o Angular renderiza a imagem.

Agora, caso queiramos estabelecer um tempo para que a imagem seja carregada, podemos recorrer ao on timer. Vamos aplicar esse recurso na última imagem, o logo do JS:

<!-- modo on timer -->
@defer (on timer(4s)) {
  <img src="../../assets/logo-javascript.png" alt="Logo do Angular" width="100px" height="100px">
} @placeholder {
  <p>Futura imagem</p>
}@loading {
  <p>Carregando imagem...</p>
} @error {
  Carregamento falhou!
}

Observe o comportamento da última imagem (logo do JavaScript) sendo carregada com o on timer:

Esta é um gif da tela do projeto, com fundo branco. O gif apresenta o carregamento do componente de conteúdo, onde no canto superior esquerdo há o título "Titulo" em texto preto, seguido por 8 parágrafos de texto lorem ipsum em preto. Abaixo, ao rolar a barra de rolagem, no canto esquerdo há três imagens dispostas verticalmente: o logo de escudo do HTML 5, na cor laranja com o número 5 em branco no centro. O carregamento dessa imagem é imediato. A segunda imagem é o logo de escudo do CSS 3, na cor azul com o número 3 em branco no centro. O carregamento dessa imagem é lento pois só começa quando descemos a barra de rolagem até sua posição, antes de a imagem aparecer é exibido o texto “Carregando imagem…”.  A terceira imagem é o logo do JavaScript, um quadrado amarelo com o texto “JS” na cor preta. O carregamento dessa imagem também é lento pois definimos para que esperasse 4 segundos até ser carregada, antes de a imagem aparecer é exibido o texto do placeholder “Futura imagem”.

Nesse caso serão contados 4 segundos até que a imagem seja carregada. Enquanto isso, o placeholder que definimos como “Futura imagem” é exibido.

E esses são apenas alguns dos parâmetros que podem ser aplicados nas deferrable views. Para conhecer outros, observe os detalhes na nova documentação do Angular clicando aqui.

Usamos as imagens como exemplo na melhoria de desempenho, mas as deferrable views suportam o carregamento sob demanda de componentes, diretivas e pipes e qualquer CSS associado e podem ser extremamente úteis em cenários distintos como para tratar a exibição de dados retornados de APIs ou para controlar a renderização de componentes mais pesados como animações 3D, por exemplo. Fazer o lazy loading de um componente quando um elemento DOM é exibido poderia gerar uma lógica grande e complexa. Assim, o uso do de deferrable views torna muito mais simples e prática a utilização da API IntersectionObservers.

SSR / Experiência de renderização híbrida renovada

O Server-Side Rendering (SSR) é um passo importante na otimização do desempenho de páginas Angular, permitindo que as páginas HTML sejam geradas no servidor e enviadas ao navegador do usuário já prontas.

No Angular v17, temos um novo prompt que te pergunta se deseja habilitar server-side rendering (SSR) e static-site generation (SSG/pre rendering) diretamente ao criar um novo projeto com ng new. Mas como isso impacta o desempenho da aplicação e porque essa era uma das novidades mais esperadas do Angular 17?

Essa abordagem resolve problemas de desempenho associados à renderização exclusiva no lado do cliente (Client-Side Rendering), em que o navegador recebe um documento HTML básico e, em seguida, executa o JavaScript para preencher dinamicamente o conteúdo da página, o que pode levar a um atraso na renderização.

Por isso, com SSR as páginas chegam ao navegador já renderizadas, melhorando a indexação de busca e otimizando a experiência do usuário, especialmente em conexões mais lentas.

Por isso, a Angular CLI agora inclui a opção para habilitar SSR no momento da criação do projeto. Além disso, ainda temos o comando alternativo ng new nome-do-app --ssr.

Hydration

Outra novidade é que agora hydration ou hidratação, que no contexto de desenvolvimento web consiste na transformação do conteúdo HTML inicialmente processado no servidor, convertendo-o em uma página web altamente interativa no lado do cliente, não está mais em preview. Ou seja, agora esse recurso está habilitado por padrão em novos apps que usem server-side rendering.

Novo pacote @angular/ssr

O repositório do Angular Universal foi movido para o repositório do Angular CLI, o que representa uma incorporação mais profunda da renderização no lado do servidor (SSR) nas ferramentas Angular.

A partir de agora, para integrar suporte à renderização híbrida em sua aplicação existente, basta executar o seguinte comando:

ng add @angular/ssr

Ao adotar o comando ng add @angular/ssr, ocorre a ativação automática de hydration juntamente com a inclusão da capacidade de compilação SSR e SSG. O @angular/ssr proporciona funcionalidades equiparáveis às do @nguniversal/express-engine, que está atualmente em modo de manutenção. Caso esteja utilizando o express-engine, a atualização automática do código para @angular/ssr será realizada pelo Angular CLI.

Deploy de apps SSR

A equipe Angular também colaborou com provedores de nuvem para facilitar o deploy em suas plataformas, visando aprimorar a experiência da pessoa desenvolvedora. Assim, graças à prévia antecipada de sua nova CLI que reconhece frameworks, o Firebase agora reconhecerá e fará o deploy automático de aplicações Angular com praticamente nenhuma configuração.

firebase experiments:enable webframeworks
firebase init hosting
firebase deploy

Já para quem prefere ferramentas nativa, o AngularFire permite o deploy no Firebase com ng deploy:

ng add @angular/fire
ng deploy

Novos hooks de ciclo de vida

A equipe Angular desenvolveu um conjunto de novos hooks de ciclo de vida para aprimorar o desempenho de SSR (server-side rendering) e SSG (static-site generation) no Angular a longo prazo. O objetivo é se afastar da emulação e das manipulações diretas no DOM. Ao mesmo tempo, ao longo do ciclo de vida da maioria das aplicações, é comum a necessidade de interagir com elementos.

Esses novos hooks oferecem uma solução viável para equilibrar a otimização de desempenho e a demanda contínua de interação com elementos durante o desenvolvimento de aplicações:

afterRender: registra um callback para ser invocado sempre que a aplicação termina a renderização.

afterNextRender: registra um callback para ser invocado na próxima vez que a aplicação terminar a renderização.

Suporte experimental de view transitions

A API View Transitions proporciona transições fluidas durante as alterações no DOM. No Angular router, agora há suporte direto para essa API por meio do recurso withViewTransitions. Ao utilizá-lo, você pode tirar proveito das capacidades nativas do navegador para criar transições animadas entre rotas.

É possível incorporar esse recurso à sua aplicação hoje, configurando-o na declaração do provider do router na inicialização. Vamos exemplificar aplicando esse recurso ao nosso projeto de Lista de Estudos. No arquivo main.ts, vamos adicionar a configuração:

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
import { provideRouter, withViewTransitions } from '@angular/router';
//alguns imports omitidos

  bootstrapApplication(AppComponent, {
    providers: [
      provideRouter(routes, withViewTransitions()),
    ]
  });

Na inicialização da aplicação, no provider de rotas, passamos o array de rotas que foi configurado em app.routes.ts (lembre-se de importar), seguido do recurso withViewTransitions(). Também é importante lembrar de importar o provideRouter e withViewTransitions de @angular/router.

Em seguida, no arquivo styles.css vamos escrever a animação que queremos aplicar na transição de rotas. Aqui eu defini a seguinte:

@keyframes fade-in{
  from{
    opacity: 50%;
  }
}

@keyframes fade-out{
  to{
    opacity: 0;
  }
}

@keyframes slide-right{
  from{
    transform: translateX(33px);
  }
}

@keyframes slide-left{
  to{
    transform: translateX(-33px);
  }
}

::view-transition-old(root){
  animation: 1s fade-out, 1s slide-left;
}
::view-transition-new(root){
  animation: 2s fade-in, 1s slide-right;
}

Quando houver a transição de rotas, ::view-transition-old será responsável pela animação do que estava sendo exibido, enquanto ::view-transition-new cuidará da nova exibição. O resultado da animação das rotas é o seguinte:

Esta é um gif da tela do projeto, com fundo branco. O gif transita entre o primeiro componente de lista, onde há o botão “Quero estudar!” que quando é clicado redireciona para o segundo componente. No redirecionamento, o primeiro componente desliza para a esquerda perdendo opacidade enquanto o novo componente entra deslizando da direita para a esquerda ganhando opacidade, devido à animação adicionada com `withViewTransitions()`.

O withViewTransitions também aceita um objeto de configuração opcional com uma propriedade onViewTransitionCreated, que é um callback que proporciona controles adicionais. Isso inclui a capacidade de decidir se deseja pular animações específicas, adicionar classes ao documento para personalizar a animação e remover essas classes quando a animação for concluída, entre outras opções.

Signal-based components

Os signals, que aprendemos a usar na versão 16, agora saem da preview para desenvolvedores, com exceção da função effect, que permanecerá em fase de prévia por enquanto a fim de melhorar sua semântica.

O Angular introduziu uma significativa mudança no seu framework com o novo sistema reativo baseado em Signals. Agora, é possível designar um componente como Signal-based adicionando a propriedade signal: true no decorator @Component. Essa abordagem pode substituir os decorators @Input e @Output, proporcionando uma maneira mais centrada nos Signals e simplificando consideravelmente o controle da reatividade, já que estratégias de verificação de mudanças, como o ngOnChanges, podem ser substituídas. A principal vantagem é o controle total sobre as mudanças proporcionado pelos Signals.

Temos um artigo detalhado explicando o funcionamento dos Signals. Caso queira explorar mais sobre os Signals no Angular, você pode acessar o artigo Entendendo os Signals do Angular clicando aqui.

Standalone APIs

Desde a versão 16 do Angular já havia uma movimentação para implementação de Standalone APIs, com o objetivo de simplificar a experiência de desenvolvimento, diminuir a quantidade de código repetitivo e reduzir a dependência dos NgModules.

Assim, é possível marcar componentes, diretivas e pipes como standalone: true. As classes Angular marcadas como standalone não precisam ser declaradas em um NgModule e emitem um erro caso declaradas.

Os componentes standalone especificam suas dependências diretamente, em vez de obtê-las por meio de NgModules. Por exemplo, no nosso projeto de exemplo, o componente ListarEstudosComponent é standalone, por isso ele pode importar diretamente outro componente standalone, como o ConteudoEstudosComponent:

@Component({
  selector: 'app-listar-estudos',
  standalone: true,
  imports: [ConteudoEstudosComponent],
  templateUrl: './listar-estudos.component.html',
  styleUrl: './listar-estudos.component.css'
})
export class ListarEstudosComponent {

//lógica do componente
}

Sendo assim, as Standalone APIs, que incluem componentes, diretivas e pipes autônomos, estão agora habilitadas em todas as novas aplicações do Angular. Todos os comandos ng generate agora criarão estruturas para esses elementos standalone.

Além disso, nas documentações (tanto angular.io como angular.dev) houveram atualizações quanto a esse tema para garantir consistência na experiência de aprendizado, práticas de desenvolvimento e boas práticas.

Os NgModules serão mantidos por enquanto, mas a equipe recomenda fortemente a migração gradual para as novas Standalone APIs, e para isso disponibilizaram um guia de migração para facilitar esse processo, que você pode acessar clicando aqui.

Conclusão

O Angular 17 representa uma verdadeira renascença para o framework. Com melhorias visuais, aprimoramentos de desempenho e novos recursos, a comunidade Angular está pronta para uma experiência de desenvolvimento mais moderna e eficiente. As inovações introduzidas na identidade visual, documentação, control flow, SSR, e outros aspectos refletem o compromisso contínuo da equipe Angular em atender às necessidades da comunidade de desenvolvedores. O futuro do Angular parece empolgante, e a versão 17 é um passo significativo nessa jornada de evolução.

Nesse artigo, trouxemos os maiores destaques da v17 do Angular, mas caso você queira se aprofundar mais e ver as demais atualizações desse framework, acesse este artigo no Blog Angular.

Caso você queira explorar o projeto com os exemplos que trouxemos neste artigo, pode acessar o repositório no GitHub clicando aqui.

Você se interessa por Angular e não vê a hora de mergulhar no Angular 17? Então não perca as novidades da Alura, muito em breve teremos uma formação fresquinha abordando essas novidades e te ajudando a evoluir ainda mais! 🚀

Rafaela Petelin Silvério
Rafaela Petelin Silvério

Graduanda em Sistemas de Informação e Técnica em Desenvolvimento de Sistemas pela ETEC, atua como Scuba, na área de Front-end. Apaixonada por tecnologia, está sempre buscando aprender coisas novas. Seus hobbies favoritos são ler, programar, estudar novos idiomas e assistir séries de comédia.

Veja outros artigos sobre Front-end