Nível 3Unidade 1 · Fundamentos de front-end com Vue.js3 aulas de 50 min + 1 h EAD

Aula 01 — Apresentação da disciplina e revisão de JavaScript

Nível 3 — Frameworks Modernos · FACET-SNP-310 · WebLab · Prof. Ivan Luiz Pedroso Pires

🎯 Objetivos de aprendizagem

Ao final desta aula você será capaz de:

📋 Pré-requisitos desta aula

Esta é a primeira aula — não há pré-requisito de conteúdo da disciplina. Você precisa apenas de:

⚠️ Atenção Se você nunca escreveu uma linha de JavaScript, não entre em pânico — a Seção 3 desta aula é uma revisão completa. Mas reserve um tempo extra para os laboratórios em casa.

🗺️ Roteiro

Bloco Tempo Atividade
1 50 min Apresentação da disciplina, cronograma, avaliações, instalação do ambiente
2 50 min Por que frameworks existem: DOM manual vs. paradigma declarativo
3 50 min Revisão de JavaScript moderno (ES2015+) com foco no domínio de dados do curso

1. Apresentação da disciplina

1.1 Quem sou eu e como essa disciplina funciona

Meu nome é Ivan Luiz Pedroso Pires, professor da FACET/UNEMAT — Campus Sinop. Esta disciplina, FACET-SNP-310 — Frameworks Modernos para Desenvolvimento de Sistemas, tem carga horária de 60 horas: 45h de aulas síncronas presenciais (15 encontros de 3 aulas de 50 minutos, às noites, horário 4N234) e 15h de atividades assíncronas — uma hora de trabalho fora de sala a cada encontro.

A ementa oficial do curso é:

"Desenvolvimento com uso de frameworks; padrões: criacionais, estruturais e comportamentais; aplicação conjunta das abordagens de frameworks e componentes no desenvolvimento de software."

Isso significa duas coisas na prática: (1) você vai aprender a construir aplicações reais com um framework front-end moderno e depois integrá-las a um back-end; (2) ao longo do caminho, vamos identificar padrões de projeto clássicos (GoF) escondidos dentro das ferramentas que usamos — Vue, Vuetify, Pinia, Express — porque a ementa exige isso explicitamente, e porque entender o padrão por trás da ferramenta é o que separa quem usa framework de quem entende framework.

1.2 As três unidades

Unidade Conteúdo Aulas
U1 Fundamentos de front-end com Vue.js 01–04
U2 Vue.js avançado: Vuetify, Axios, Vue Router, Pinia 04–06
U3 Integração front-end/back-end: Firebase, Supabase, Express, autenticação, banco de dados, deploy 07–15

1.3 Cronograma completo

Guarde esta tabela — ela é o mapa do semestre inteiro.

# Data Unidade Tema
01 12/08/2026 U1 Apresentação da disciplina e revisão de JavaScript
02 19/08/2026 U1 Introdução ao Vue: lifecycle, instância, data e methods, diretivas básicas
03 26/08/2026 U1 Vue: v-if, v-else, v-for, computed e onMounted
04 02/09/2026 U1 Introdução a Vuetify e Vue Router — entrega da Avaliação 1
05 16/09/2026 U2 Componentes, Vue Router e Vuetify
06 23/09/2026 U2 Axios e Pinia
07 30/09/2026 U3 Firebase, Node.js e Express
08 07/10/2026 U3 Endpoints e middlewares — entrega da Avaliação 2
09 21/10/2026 U3 Integração com MySQL
10 28/10/2026 U3 Requisições autenticadas com Firebase
11 11/11/2026 U3 CRUD front-end + back-end
12 18/11/2026 U3 CRUD com banco de dados em nuvem (Supabase)
13 25/11/2026 U3 Desenvolvimento do back-end em camadas
14 09/12/2026 U3 Documentação com Swagger
15 16/12/2026 U3 Apresentação dos resultados — entrega da Avaliação 3

Todas as aulas 01 a 15 constroem, em sala, uma aplicação de referência chamada UniEventos — uma plataforma de divulgação e inscrição em eventos acadêmicos. Você vai acompanhar essa construção passo a passo, mas seu projeto autoral (o que você entrega nas avaliações) terá a mesma arquitetura aplicada a um domínio diferente, escolhido por você. Falamos disso na Seção 1.6.

1.4 Avaliações

A nota final é a média aritmética simples das três avaliações.

Avaliação Escopo Entrega
Avaliação 1 Vue 3 com CLI: estrutura de projeto, componentes, diretivas até 02/09/2026, 23h59
Avaliação 2 Vue avançado: Vuetify + Axios + Vue Router + Pinia até 07/10/2026, 23h59
Avaliação 3 Back-end: Firebase/Express/Supabase, banco de dados, autenticação até 16/12/2026, 23h59

Todas as entregas são feitas via SIGAA, dentro do prazo. As instruções detalhadas de cada avaliação (escopo exato, rubrica, formato de entrega) serão publicadas na aula correspondente — a Avaliação 1 será detalhada na Aula 04.

📌 Na prova Existe também um exame final: prova teórica, presencial e individual, cobrindo as três unidades. Ele é aplicado a quem não atinge a média mínima de aprovação direta pelas três avaliações, conforme o regimento da UNEMAT. Estude a teoria, não só a prática — o exame não é sobre "rodar o código", é sobre entender os conceitos.

1.5 Frequência e comunicação

A frequência é obrigatória e verificada em cada um dos 15 encontros presenciais — o mínimo de 75% de presença é exigência regimental para aprovação, independente da nota. Encontros faltados não podem ser compensados só com a atividade assíncrona: a atividade assíncrona é conteúdo adicional, não substituto de presença.

Canais oficiais de comunicação:

💡 Dica Comite no seu repositório do projeto autoral toda semana, mesmo que pouco. Um histórico de commits ao longo do semestre vale mais, na avaliação de processo, do que um único commit gigante na véspera da entrega.

1.6 O projeto autoral

Cada estudante escolhe, ainda nesta primeira semana, um domínio de aplicação diferente do UniEventos (o projeto que construiremos em sala), mas segue exatamente a mesma arquitetura técnica: Vue 3 → Vuetify + Vue Router → Axios + Pinia → back-end Express → banco de dados → autenticação → deploy.

Exemplos de temas válidos:

Regras para o tema:

  1. Precisa ter pelo menos duas entidades relacionadas (ex.: "Evento" e "Inscrição", "Quadra" e "Reserva") — um cadastro único sem relacionamento não sustenta as três unidades.
  2. Precisa ter uma tela de listagem com filtro, uma tela de detalhe e uma área que exija autenticação — isso espelha as telas do UniEventos (Home, Detalhe, Minhas inscrições, Login, Área administrativa).
  3. Não pode ser o próprio UniEventos copiado — o domínio precisa ser outro.

1.7 Ambiente de desenvolvimento

Vamos instalar, nesta aula, tudo que será usado até o fim do semestre. As versões abaixo foram testadas em 12/08/2026 no ambiente real da disciplina — use exatamente estas.

Ferramenta Versão usada na disciplina
Node.js 22.22.2 LTS
npm 10.9.7 (vem com o Node)
VS Code versão estável mais recente
Git versão estável mais recente

Passo a passo:

  1. Node.js 22 LTS. Baixe em nodejs.org a versão "LTS" (não a "Current"). No Linux, você também pode usar o gerenciador de versões nvm:
Terminal
# instalar nvm (se ainda não tiver)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# instalar e usar o Node 22 LTS
nvm install 22
nvm use 22
  1. Verifique a instalação:
Terminal
node -v
# esperado: v22.22.2 (ou outra 22.x LTS)

npm -v
# esperado: 10.9.7 (ou próxima)

⚠️ Atenção Se node -v mostrar uma versão 16, 18 ou 20, desinstale-a ou troque com o nvm antes de continuar. Ferramentas que usaremos mais à frente, como o create-vue, exigem Node ^22.18.0 ou >=24.12.0 — versões antigas simplesmente falham na instalação.

  1. VS Code. Baixe em code.visualstudio.com. Instale estas extensões (aba Extensions, Ctrl+Shift+X):
  1. Navegador com DevTools. Use Chrome, Edge ou Firefox — qualquer um com um bom painel de DevTools (F12). Vamos usar a aba Elements (inspecionar DOM), Console e Network (ver requisições) o semestre inteiro.

  2. Git e GitHub.

Terminal
git --version
# se não tiver, instale: sudo apt install git (Linux) ou baixe em git-scm.com

git config --global user.name "Seu Nome"
git config --global user.email "seu-email@exemplo.com"

Crie uma conta em github.com se ainda não tiver. Vamos usar o GitHub para hospedar o código do projeto autoral e, mais adiante no semestre, para o deploy.

💡 Dica Depois de instalar tudo, rode node -v, npm -v e git --version e tire um print. Cole no seu README como evidência de ambiente pronto — é o primeiro item do checkpoint desta aula.

2. Por que frameworks existem

2.1 Um problema concreto: lista de eventos com filtro

Imagine que você precisa mostrar uma lista de eventos acadêmicos na tela, com um campo de busca por texto e um filtro por categoria. Sem framework nenhum, em JavaScript puro manipulando o DOM diretamente, o código fica assim:

HTML
<!-- index.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8" />
  <title>Eventos — DOM manual</title>
</head>
<body>
  <input id="busca" type="text" placeholder="Buscar evento..." />
  <select id="categoria">
    <option value="">Todas as categorias</option>
    <option value="palestra">Palestra</option>
    <option value="minicurso">Minicurso</option>
    <option value="workshop">Workshop</option>
  </select>

  <ul id="lista-eventos"></ul>

  <script src="app-dom-manual.js"></script>
</body>
</html>
JavaScript
// app-dom-manual.js
const eventos = [
  { id: 1, titulo: 'Semana da Computação', categoria: 'palestra', vagas: 40 },
  { id: 2, titulo: 'Oficina de Vue.js', categoria: 'minicurso', vagas: 25 },
  { id: 3, titulo: 'Hackathon FACET', categoria: 'workshop', vagas: 60 },
  { id: 4, titulo: 'Introdução a IA', categoria: 'palestra', vagas: 80 },
]

const inputBusca = document.getElementById('busca')
const selectCategoria = document.getElementById('categoria')
const listaEl = document.getElementById('lista-eventos')

// Função que decide QUAIS eventos mostrar e depois
// PRECISA, na mão, apagar o DOM antigo e reconstruir tudo.
function renderizar() {
  const termo = inputBusca.value.toLowerCase()
  const categoria = selectCategoria.value

  const filtrados = eventos.filter((evento) => {
    const bateTexto = evento.titulo.toLowerCase().includes(termo)
    const bateCategoria = categoria === '' || evento.categoria === categoria
    return bateTexto && bateCategoria
  })

  // Passo manual 1: limpar o que já estava na tela.
  listaEl.innerHTML = ''

  // Passo manual 2: recriar cada item, um por um.
  if (filtrados.length === 0) {
    const li = document.createElement('li')
    li.textContent = 'Nenhum evento encontrado.'
    listaEl.appendChild(li)
    return
  }

  filtrados.forEach((evento) => {
    const li = document.createElement('li')
    li.textContent = `${evento.titulo} (${evento.categoria}) — ${evento.vagas} vagas`
    listaEl.appendChild(li)
  })
}

// Passo manual 3: escutar cada evento de interação e chamar renderizar() de novo.
inputBusca.addEventListener('input', renderizar)
selectCategoria.addEventListener('change', renderizar)

renderizar()

Funciona. Mas repare no que você, programador, teve que fazer manualmente:

  1. Escutar cada evento de UI (input, change) e lembrar de chamar renderizar().
  2. Dentro de renderizar(), apagar o HTML antigo (innerHTML = '') e reconstruir do zero.
  3. Manter sincronizado, na sua cabeça, o estado (eventos, o termo de busca, a categoria) com o que está na tela.

Em uma lista de 4 eventos isso é trivial. Em uma aplicação real — com dezenas de componentes, cada um reagindo a mudanças de estado de outros — esse sincronismo manual vira a maior fonte de bugs: tela desatualizada, elementos duplicados, listeners que vazam memória.

2.2 A mesma ideia, em paradigma declarativo

Compare com o que faremos a partir da Aula 02, em Vue:

Vue SFC
<!-- Antecipação — ainda NÃO é o que vamos escrever hoje, é só para criar contraste -->
<template>
  <input v-model="busca" type="text" placeholder="Buscar evento..." />
  <select v-model="categoria">
    <option value="">Todas as categorias</option>
    <option value="palestra">Palestra</option>
    <option value="minicurso">Minicurso</option>
    <option value="workshop">Workshop</option>
  </select>

  <ul>
    <li v-for="evento in eventosFiltrados" :key="evento.id">
      {{ evento.titulo }} ({{ evento.categoria }}) — {{ evento.vagas }} vagas
    </li>
    <li v-if="eventosFiltrados.length === 0">Nenhum evento encontrado.</li>
  </ul>
</template>

Note a diferença de raciocínio: no código Vue você descreve o resultado desejado ("a lista deve mostrar eventosFiltrados") e o framework decide, sozinho, quando e como atualizar o DOM. Você não escreve innerHTML = '', não escreve addEventListener, não gerencia manualmente qual elemento criar ou remover. Isso é programação declarativa: você declara o "o quê", o framework resolve o "como".

Essa é a promessa central de um framework front-end reativo como o Vue — e é o fio condutor do semestre inteiro. Ainda não vamos escrever Vue hoje (isso começa na Aula 02); hoje construímos a base de JavaScript que sustenta tudo isso.

2.3 Conceitos que você vai ouvir o semestre inteiro

3. Revisão de JavaScript moderno (ES2015+)

Esta é a espinha dorsal da aula de hoje. O Vue é, por baixo, "só" JavaScript — cada recurso que revisamos aqui reaparece dentro de um <script setup> já na próxima aula.

3.1 let, const e escopo de bloco

JavaScript
// Antes do ES2015 só existia `var`, com escopo de função (confuso).
// Hoje: use `const` por padrão, `let` só quando o valor precisa mudar.

const nomeEvento = 'Semana da Computação' // não pode ser reatribuída
let vagasDisponiveis = 40                 // pode ser reatribuída

vagasDisponiveis = vagasDisponiveis - 1
console.log(vagasDisponiveis) // 39

// `let` e `const` respeitam escopo de bloco { }
if (vagasDisponiveis > 0) {
  const mensagem = 'ainda há vagas'
  console.log(mensagem)
}
// console.log(mensagem) aqui daria ReferenceError: mensagem não existe fora do bloco

⚠️ Atenção Nunca use var em código novo. var "vaza" para fora de blocos if/for, o que gera bugs difíceis de rastrear. A disciplina inteira usa apenas let e const.

3.2 Template literals

JavaScript
const evento = { titulo: 'Oficina de Vue.js', vagas: 25 }

// Antes: concatenação com +
const antigo = 'Evento: ' + evento.titulo + ' (' + evento.vagas + ' vagas)'

// Agora: template literal, com crase e ${}
const moderno = `Evento: ${evento.titulo} (${evento.vagas} vagas)`

// Suporta múltiplas linhas sem precisar de \n
const bloco = `
  Título: ${evento.titulo}
  Vagas: ${evento.vagas}
`
console.log(moderno)

3.3 Arrow functions e this

JavaScript
// Função tradicional
function dobrar(numero) {
  return numero * 2
}

// Arrow function equivalente
const dobrarArrow = (numero) => numero * 2

// Com múltiplos parâmetros e corpo de bloco
const somarVagas = (a, b) => {
  const total = a + b
  return total
}

// Sem parâmetros, precisa dos parênteses vazios
const gerarId = () => Math.floor(Math.random() * 1000)

A diferença mais importante não é a sintaxe curta — é o comportamento do this.

JavaScript
const contador = {
  vagas: 10,
  // `function` tradicional: `this` é o objeto que CHAMA o método (contador).
  reduzirComFunction: function () {
    setTimeout(function () {
      // aqui `this` NÃO é mais `contador` — em modo estrito, é `undefined`.
      console.log(this?.vagas) // undefined
    }, 100)
  },
  // arrow function: `this` é herdado do escopo onde a arrow foi DEFINIDA.
  reduzirComArrow: function () {
    setTimeout(() => {
      // aqui `this` continua sendo `contador`, porque a arrow "pega emprestado"
      // o `this` do método externo.
      console.log(this.vagas) // 10
    }, 100)
  },
}

contador.reduzirComFunction()
contador.reduzirComArrow()

🔎 Por baixo do capô Arrow functions não têm seu próprio this — em vez de criar um novo, elas capturam o this do escopo léxico onde foram escritas. É exatamente por isso que, dentro de um <script setup> do Vue (Aula 02), quase sempre usamos arrow functions ou funções normais no nível do módulo: o comportamento de this deixa de ser um problema porque a Composition API não depende dele.

3.4 Desestruturação de objetos e arrays

JavaScript
const evento = {
  id: 2,
  titulo: 'Oficina de Vue.js',
  categoria: 'minicurso',
  vagas: 25,
  local: 'Bloco B, sala 12',
}

// Desestruturação de objeto: extrai propriedades para variáveis
const { titulo, vagas } = evento
console.log(titulo, vagas) // Oficina de Vue.js 25

// Renomear ao desestruturar
const { titulo: nomeDoEvento } = evento
console.log(nomeDoEvento) // Oficina de Vue.js

// Valor default se a propriedade não existir
const { imagemUrl = '/img/padrao.png' } = evento
console.log(imagemUrl) // /img/padrao.png

// Desestruturação de array: extrai por posição
const coordenadas = [-16.0736, -57.6789]
const [latitude, longitude] = coordenadas
console.log(latitude, longitude) // -16.0736 -57.6789

// Desestruturação em parâmetros de função — muito comum no Vue com props
function exibirEvento({ titulo, vagas }) {
  return `${titulo}: ${vagas} vagas`
}
console.log(exibirEvento(evento)) // Oficina de Vue.js: 25 vagas

3.5 Spread e rest

JavaScript
// Spread (...) em array: "espalha" os elementos
const categoriasBase = ['palestra', 'minicurso']
const todasCategorias = [...categoriasBase, 'workshop']
console.log(todasCategorias) // ['palestra', 'minicurso', 'workshop']

// Spread em objeto: cria uma CÓPIA com propriedades sobrescritas
// (importante: nunca mutar o objeto original em Vue)
const eventoOriginal = { id: 1, titulo: 'Semana da Computação', vagas: 40 }
const eventoAtualizado = { ...eventoOriginal, vagas: 39 }
console.log(eventoOriginal.vagas)   // 40 — original intocado
console.log(eventoAtualizado.vagas) // 39 — cópia com a mudança

// Rest (...) em parâmetros: agrupa "o resto" dos argumentos em array
function somarTodasAsVagas(...quantidades) {
  return quantidades.reduce((total, atual) => total + atual, 0)
}
console.log(somarTodasAsVagas(10, 20, 30)) // 60

// Rest em desestruturação: agrupa "o resto" das propriedades
const { id, ...detalhesDoEvento } = eventoOriginal
console.log(id)              // 1
console.log(detalhesDoEvento) // { titulo: 'Semana da Computação', vagas: 40 }

3.6 Parâmetros default

JavaScript
function criarEvento(titulo, categoria = 'palestra', vagas = 30) {
  return { titulo, categoria, vagas }
}

console.log(criarEvento('Minicurso de Git'))
// { titulo: 'Minicurso de Git', categoria: 'palestra', vagas: 30 }

console.log(criarEvento('Workshop de Testes', 'workshop', 15))
// { titulo: 'Workshop de Testes', categoria: 'workshop', vagas: 15 }

3.7 Optional chaining (?.) e nullish coalescing (??)

JavaScript
const evento = {
  titulo: 'Semana da Computação',
  local: {
    predio: 'Bloco A',
    // sala não foi informada
  },
}

// Sem optional chaining, acessar uma propriedade aninhada ausente quebra:
// console.log(evento.organizador.nome) // TypeError: Cannot read properties of undefined

// Com optional chaining: retorna `undefined` em vez de lançar erro
console.log(evento.organizador?.nome) // undefined
console.log(evento.local?.sala)       // undefined
console.log(evento.local?.predio)     // Bloco A

// Funciona também para chamar métodos que podem não existir
const relatorio = null
console.log(relatorio?.gerar?.()) // undefined, sem quebrar

// Nullish coalescing (??): fornece um valor padrão SOMENTE quando o
// valor à esquerda é null ou undefined (diferente do || , que também
// cai no padrão para 0, '' ou false — o que costuma ser um bug).
const vagasInformadas = 0
console.log(vagasInformadas || 10) // 10 — ERRADO: 0 é um valor válido de vagas!
console.log(vagasInformadas ?? 10) // 0  — CORRETO: só usa o padrão se for null/undefined

const sala = evento.local?.sala ?? 'a definir'
console.log(sala) // a definir

⚠️ Atenção || e ?? parecem intercambiáveis, mas não são. Use ?? sempre que 0, '' ou false forem valores legítimos que você não quer substituir pelo padrão. É um erro comum em formulários (campo numérico zerado sendo tratado como "vazio").

3.8 Métodos de array: map, filter, reduce, find, some, every, sort

Vamos usar o mesmo array de eventos em todos os exemplos — é o dado que sustentará o UniEventos a partir da Aula 02.

JavaScript
const eventos = [
  { id: 1, titulo: 'Semana da Computação', categoria: 'palestra', dataHora: '2026-09-10T19:00:00', vagas: 40, inscritos: 12 },
  { id: 2, titulo: 'Oficina de Vue.js', categoria: 'minicurso', dataHora: '2026-08-20T14:00:00', vagas: 25, inscritos: 25 },
  { id: 3, titulo: 'Hackathon FACET', categoria: 'workshop', dataHora: '2026-10-05T08:00:00', vagas: 60, inscritos: 18 },
  { id: 4, titulo: 'Introdução a IA', categoria: 'palestra', dataHora: '2026-08-18T19:30:00', vagas: 80, inscritos: 55 },
]

map — transforma cada item em outra coisa, sem mudar o tamanho do array:

JavaScript
const titulos = eventos.map((evento) => evento.titulo)
console.log(titulos)
// ['Semana da Computação', 'Oficina de Vue.js', 'Hackathon FACET', 'Introdução a IA']

// map devolvendo objetos NOVOS (sem mutar os originais) — padrão que
// vamos repetir sempre que precisarmos "decorar" dados para a tela
const eventosComVagasRestantes = eventos.map((evento) => ({
  ...evento,
  vagasRestantes: evento.vagas - evento.inscritos,
}))
console.log(eventosComVagasRestantes[0].vagasRestantes) // 28

filter — seleciona um subconjunto:

JavaScript
const palestras = eventos.filter((evento) => evento.categoria === 'palestra')
console.log(palestras.length) // 2

const comVagas = eventos.filter((evento) => evento.inscritos < evento.vagas)
console.log(comVagas.map((e) => e.titulo)) // exclui a "Oficina de Vue.js" (lotada)

reduce — acumula os itens em um único valor:

JavaScript
const totalDeVagas = eventos.reduce((acumulado, evento) => acumulado + evento.vagas, 0)
console.log(totalDeVagas) // 205

// reduce também serve para agrupar por categoria
const porCategoria = eventos.reduce((grupos, evento) => {
  const chave = evento.categoria
  if (!grupos[chave]) {
    grupos[chave] = []
  }
  grupos[chave].push(evento)
  return grupos
}, {})
console.log(Object.keys(porCategoria)) // ['palestra', 'minicurso', 'workshop']

find — retorna o primeiro item que bate na condição (ou undefined):

JavaScript
const eventoBuscado = eventos.find((evento) => evento.id === 3)
console.log(eventoBuscado.titulo) // Hackathon FACET

const inexistente = eventos.find((evento) => evento.id === 999)
console.log(inexistente) // undefined

some e every — testam a coleção e retornam boolean:

JavaScript
const existeEventoLotado = eventos.some((evento) => evento.inscritos >= evento.vagas)
console.log(existeEventoLotado) // true (a Oficina de Vue.js está lotada)

const todosTemVagas = eventos.every((evento) => evento.inscritos < evento.vagas)
console.log(todosTemVagas) // false

sort — ordena o array (⚠️ muta o array original):

JavaScript
// sort() muda o array ORIGINAL. Para não afetar `eventos`, copie antes com spread.
const eventosPorData = [...eventos].sort(
  (a, b) => new Date(a.dataHora) - new Date(b.dataHora),
)
console.log(eventosPorData.map((e) => e.titulo))
// ['Oficina de Vue.js', 'Introdução a IA', 'Semana da Computação', 'Hackathon FACET']

console.log(eventos.map((e) => e.titulo))
// ainda na ordem original — porque ordenamos a CÓPIA, não `eventos`

Encadeando métodos — o padrão mais comum no dia a dia:

JavaScript
const resumoDePalestrasComVaga = eventos
  .filter((evento) => evento.categoria === 'palestra')
  .filter((evento) => evento.inscritos < evento.vagas)
  .map((evento) => `${evento.titulo} (${evento.vagas - evento.inscritos} vagas livres)`)

console.log(resumoDePalestrasComVaga)
// ['Semana da Computação (28 vagas livres)', 'Introdução a IA (25 vagas livres)']

3.9 Objetos e shorthand

JavaScript
const titulo = 'Semana da Computação'
const vagas = 40

// Antes: repetir a chave e o valor
const eventoAntigo = { titulo: titulo, vagas: vagas }

// Shorthand: quando o nome da variável é igual ao nome da propriedade
const eventoModerno = { titulo, vagas }
console.log(eventoModerno) // { titulo: 'Semana da Computação', vagas: 40 }

// Shorthand também funciona para métodos
const gerenciadorDeEventos = {
  eventos: [],
  adicionar(evento) {          // em vez de: adicionar: function (evento) { ... }
    this.eventos.push(evento)
  },
  contar() {
    return this.eventos.length
  },
}
gerenciadorDeEventos.adicionar({ titulo: 'Novo evento' })
console.log(gerenciadorDeEventos.contar()) // 1

// Nomes de propriedade computados
const chave = 'categoria'
const filtro = { [chave]: 'palestra' }
console.log(filtro) // { categoria: 'palestra' }

3.10 Módulos ES: import e export

Organizar código em módulos é essencial — é assim que um projeto Vue inteiro é estruturado, um arquivo por responsabilidade.

JavaScript
// arquivo: eventos.js
// export nomeado: pode haver vários por arquivo
export const CATEGORIAS = ['palestra', 'minicurso', 'workshop']

export function filtrarPorCategoria(eventos, categoria) {
  if (!categoria) return eventos
  return eventos.filter((evento) => evento.categoria === categoria)
}

export function calcularVagasRestantes(evento) {
  return evento.vagas - evento.inscritos
}

// export default: no máximo um por arquivo — geralmente a "coisa principal"
export default class GerenciadorDeEventos {
  constructor(eventosIniciais = []) {
    this.eventos = eventosIniciais
  }

  adicionar(evento) {
    this.eventos.push(evento)
  }
}
JavaScript
// arquivo: main.js
// import nomeado: usa chaves { } e o mesmo nome do export
import { CATEGORIAS, filtrarPorCategoria } from './eventos.js'

// import default: sem chaves, você escolhe o nome
import GerenciadorDeEventos from './eventos.js'

// import combinando os dois
import GerenciadorDeEventos2, { calcularVagasRestantes } from './eventos.js'

console.log(CATEGORIAS) // ['palestra', 'minicurso', 'workshop']

const gerenciador = new GerenciadorDeEventos()
gerenciador.adicionar({ titulo: 'Minicurso de Git', categoria: 'minicurso', vagas: 20, inscritos: 5 })
console.log(calcularVagasRestantes(gerenciador.eventos[0])) // 15

Para rodar módulos ES direto no navegador (sem bundler ainda), o HTML precisa declarar type="module":

HTML
<script type="module" src="main.js"></script>

📌 Na prova A partir da Aula 02, todo componente .vue é, por baixo, um módulo ES: ele importa outros componentes com import e é importado por quem o usa. Entender import/export agora evita confusão depois.

3.11 Classes

JavaScript
class Evento {
  // Campos de instância (sintaxe moderna, sem precisar declarar no constructor)
  inscritos = 0

  constructor(titulo, categoria, vagas) {
    this.titulo = titulo
    this.categoria = categoria
    this.vagas = vagas
  }

  // Método de instância
  inscrever() {
    if (this.inscritos >= this.vagas) {
      throw new Error('Evento lotado')
    }
    this.inscritos += 1
  }

  // Getter: parece uma propriedade, mas é calculado
  get vagasRestantes() {
    return this.vagas - this.inscritos
  }
}

// Herança com extends
class Minicurso extends Evento {
  constructor(titulo, vagas, cargaHoraria) {
    super(titulo, 'minicurso', vagas) // chama o constructor da classe-mãe
    this.cargaHoraria = cargaHoraria
  }
}

const oficina = new Minicurso('Oficina de Vue.js', 25, 4)
oficina.inscrever()
oficina.inscrever()
console.log(oficina.vagasRestantes) // 23
console.log(oficina.cargaHoraria)   // 4
console.log(oficina instanceof Evento) // true

3.12 JSON: parse e stringify

JavaScript
const evento = { id: 1, titulo: 'Semana da Computação', vagas: 40 }

// Objeto JavaScript → texto JSON (para enviar em uma requisição, por exemplo)
const textoJson = JSON.stringify(evento)
console.log(textoJson) // '{"id":1,"titulo":"Semana da Computação","vagas":40}'

// Com indentação, útil para debug/log
console.log(JSON.stringify(evento, null, 2))

// Texto JSON → objeto JavaScript (o inverso — comum ao ler resposta de API)
const textoRecebido = '{"id":2,"titulo":"Oficina de Vue.js","vagas":25}'
const objetoRecebido = JSON.parse(textoRecebido)
console.log(objetoRecebido.titulo) // Oficina de Vue.js

3.13 Assíncrono: de callback a async/await

O JavaScript é de thread única, então operações demoradas (rede, temporizadores) precisam de um jeito de "avisar quando terminar" sem travar tudo. A linguagem evoluiu em três estágios.

Estágio 1 — callback (o jeito antigo, difícil de encadear):

JavaScript
function buscarEventoComCallback(id, aoTerminar) {
  setTimeout(() => {
    aoTerminar({ id, titulo: 'Semana da Computação' })
  }, 500)
}

buscarEventoComCallback(1, (evento) => {
  console.log('recebido:', evento.titulo)
  // se precisasse buscar outra coisa depois, teria que aninhar
  // outro callback aqui dentro — o famoso "callback hell"
})

Estágio 2 — Promise (representa um valor que existirá no futuro):

JavaScript
function buscarEventoComPromise(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, titulo: 'Semana da Computação' })
      } else {
        reject(new Error('id inválido'))
      }
    }, 500)
  })
}

buscarEventoComPromise(1)
  .then((evento) => console.log('recebido:', evento.titulo))
  .catch((erro) => console.error('deu erro:', erro.message))

Estágio 3 — async/await (mesma Promise por baixo, sintaxe que lê como código síncrono):

JavaScript
async function carregarEvento() {
  try {
    const evento = await buscarEventoComPromise(1)
    console.log('recebido:', evento.titulo)
  } catch (erro) {
    console.error('deu erro:', erro.message)
  }
}

carregarEvento()

🔎 Por baixo do capô async/await não é uma tecnologia nova e diferente de Promise — é açúcar sintático sobre Promise. await pausa a execução da função async até a Promise resolver ou rejeitar, sem bloquear o restante do programa. Todo await precisa estar dentro de uma função marcada async.

fetch com async/await e try/catch — o padrão que vamos usar o semestre inteiro:

JavaScript
async function buscarEventosDaApi() {
  try {
    const resposta = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5')

    if (!resposta.ok) {
      throw new Error(`Erro HTTP: ${resposta.status}`)
    }

    const dados = await resposta.json()
    console.log('eventos recebidos:', dados.length)
    return dados
  } catch (erro) {
    console.error('falha ao buscar eventos:', erro.message)
    return []
  }
}

buscarEventosDaApi()

Promise.all — disparar várias requisições em paralelo e esperar todas:

JavaScript
async function carregarDadosDaHome() {
  try {
    const [respostaEventos, respostaCategorias] = await Promise.all([
      fetch('https://jsonplaceholder.typicode.com/posts?_limit=5'),
      fetch('https://jsonplaceholder.typicode.com/users?_limit=3'),
    ])

    const eventos = await respostaEventos.json()
    const categorias = await respostaCategorias.json()

    console.log('eventos:', eventos.length, 'categorias:', categorias.length)
  } catch (erro) {
    // Promise.all rejeita assim que QUALQUER uma das promises falhar
    console.error('alguma requisição falhou:', erro.message)
  }
}

carregarDadosDaHome()

⚠️ Atenção Promise.all falha rápido: se uma das promises rejeitar, todas as outras são "abandonadas" do ponto de vista do .catch/try-catch, mesmo que já estivessem resolvidas. Quando precisar do resultado de todas independentemente de falha, use Promise.allSettled (não obrigatório nesta disciplina, mas bom saber que existe).

🧩 Padrão de projeto em uso

🧩 Padrão de projeto em uso — Module / Revealing Module

O padrão Module organiza código relacionado (estado + comportamento) dentro de um único bloco, escondendo detalhes internos e expondo apenas uma interface pública. Antes dos módulos ES nativos, isso era feito com uma IIFE (função invocada imediatamente) que retornava um objeto com as partes públicas — o Revealing Module Pattern:

```js // Revealing Module Pattern — jeito pré-ES2015 de encapsular const GerenciadorDeEventos = (function () { // "privado": só existe dentro deste escopo de função let eventos = []

function adicionar(evento) { eventos.push(evento) }

function contarVagas() { return eventos.reduce((total, e) => total + e.vagas, 0) }

// "revela" (expõe) só o que deve ser público return { adicionar, contarVagas, } })()

GerenciadorDeEventos.adicionar({ titulo: 'Semana da Computação', vagas: 40 }) console.log(GerenciadorDeEventos.contarVagas()) // 40 // GerenciadorDeEventos.eventos não existe aqui fora — está encapsulado ```

Os módulos ES (import/export, Seção 3.10) resolvem o mesmo problema de forma nativa e sem a necessidade da IIFE: tudo que não é exportado com export é automaticamente privado ao arquivo. É o mesmo padrão de projeto, com sintaxe de linguagem em vez de truque de engenharia. Todo componente .vue que você vai escrever a partir da Aula 02 é, conceitualmente, um Module: estado interno + funções, expondo ao <template> só o que for necessário.

💻 Mão na massa — configurando o primeiro arquivo de revisão

Vamos consolidar tudo em um único exercício guiado, rodado no navegador.

Passo 1 — crie a pasta e os arquivos.

Terminal
mkdir -p ~/fds-aula01 && cd ~/fds-aula01
touch index.html eventos.js main.js

Passo 2 — o HTML que carrega o módulo:

HTML
<!-- index.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8" />
  <title>Aula 01 — Revisão de JavaScript</title>
</head>
<body>
  <h1>Abra o Console do navegador (F12) para ver os resultados</h1>
  <ul id="saida"></ul>

  <script type="module" src="main.js"></script>
</body>
</html>

Passo 3 — o módulo com os dados e as funções:

JavaScript
// eventos.js
export const eventos = [
  { id: 1, titulo: 'Semana da Computação', categoria: 'palestra', dataHora: '2026-09-10T19:00:00', vagas: 40, inscritos: 12 },
  { id: 2, titulo: 'Oficina de Vue.js', categoria: 'minicurso', dataHora: '2026-08-20T14:00:00', vagas: 25, inscritos: 25 },
  { id: 3, titulo: 'Hackathon FACET', categoria: 'workshop', dataHora: '2026-10-05T08:00:00', vagas: 60, inscritos: 18 },
  { id: 4, titulo: 'Introdução a IA', categoria: 'palestra', dataHora: '2026-08-18T19:30:00', vagas: 80, inscritos: 55 },
]

export function filtrarPorCategoria(lista, categoria) {
  if (!categoria) return lista
  return lista.filter((evento) => evento.categoria === categoria)
}

export function ordenarPorData(lista) {
  return [...lista].sort((a, b) => new Date(a.dataHora) - new Date(b.dataHora))
}

export function totalDeVagas(lista) {
  return lista.reduce((total, evento) => total + evento.vagas, 0)
}

export function formatarData(dataIso) {
  const data = new Date(dataIso)
  return new Intl.DateTimeFormat('pt-BR', {
    day: '2-digit',
    month: 'long',
    hour: '2-digit',
    minute: '2-digit',
  }).format(data)
}

Passo 4 — consumir o módulo, renderizar no DOM e buscar dados de uma API:

JavaScript
// main.js
import { eventos, filtrarPorCategoria, ordenarPorData, totalDeVagas, formatarData } from './eventos.js'

const listaEl = document.getElementById('saida')

function renderizarLista(lista) {
  listaEl.innerHTML = ''
  lista.forEach((evento) => {
    const item = document.createElement('li')
    item.textContent = `${evento.titulo}${formatarData(evento.dataHora)} (${evento.vagas} vagas)`
    listaEl.appendChild(item)
  })
}

const palestras = filtrarPorCategoria(eventos, 'palestra')
const ordenados = ordenarPorData(eventos)

console.log('total de palestras:', palestras.length)
console.log('total de vagas em todos os eventos:', totalDeVagas(eventos))
renderizarLista(ordenados)

// buscando dados externos de verdade
async function buscarPostsDeExemplo() {
  try {
    const resposta = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=3')
    if (!resposta.ok) throw new Error(`HTTP ${resposta.status}`)
    const posts = await resposta.json()
    console.log('posts de exemplo recebidos da API:', posts)
  } catch (erro) {
    console.error('não foi possível buscar os posts:', erro.message)
  }
}

buscarPostsDeExemplo()

Passo 5 — abra index.html com a extensão Live Server do VS Code (ou qualquer servidor local — módulos ES não funcionam abrindo o arquivo direto com file:// por causa de CORS). Abra o DevTools (F12) e confira o Console.

🧪 Laboratório

Use o array eventos do Passo 3 acima para os exercícios. Crie um arquivo lab.js, importe o que precisar de eventos.js e teste cada exercício no console.

1. Filtrar por categoria — escreva uma função apenasWorkshops(lista) que retorne só os eventos de categoria 'workshop', usando filter.

Resultado esperado: array com 1 item (Hackathon FACET).

Dica

lista.filter((evento) => evento.categoria === 'workshop')

2. Ordenar por vagas restantes — escreva ordenarPorVagasRestantes(lista) que devolva uma cópia do array ordenada da maior para a menor quantidade de vagas restantes (vagas - inscritos), sem mutar o array original.

Resultado esperado: o array original (eventos) mantém a mesma ordem depois de chamar a função.

Dica

Copie primeiro com [...lista], depois use .sort((a, b) => (b.vagas - b.inscritos) - (a.vagas - a.inscritos)).

3. Calcular vagas totais com reduce — escreva vagasRestantesTotais(lista) que retorne a soma de vagas - inscritos de todos os eventos.

Resultado esperado: 55 (para o array de exemplo: 28 + 0 + 42 + 25).

Dica

lista.reduce((total, evento) => total + (evento.vagas - evento.inscritos), 0)

4. Formatar datas em pt-BR — usando Intl.DateTimeFormat('pt-BR', { dateStyle: 'long' }), formate a dataHora de cada evento e monte um array de strings como "10 de setembro de 2026".

Resultado esperado: 4 strings de data em português.

Dica
JavaScript
const formatador = new Intl.DateTimeFormat('pt-BR', { dateStyle: 'long' })
eventos.map((evento) => formatador.format(new Date(evento.dataHora)))

5. Buscar dados de uma API pública e renderizar — usando fetch e async/await, busque https://jsonplaceholder.typicode.com/users (sem parâmetro de limite), pegue apenas os 3 primeiros com .slice(0, 3), e renderize o name de cada um em uma lista <ul> no HTML.

Resultado esperado: 3 nomes de usuários aparecendo na página.

Dica

const usuarios = await (await fetch(url)).json(), depois usuarios.slice(0, 3).forEach(...) criando <li> como no Passo 4.

🐛 Erros comuns e como resolver

Sintoma Causa Solução
this é undefined dentro de um callback Usou function tradicional dentro de um método, perdendo o this do objeto Troque por arrow function, que herda o this do escopo externo
Array original mudou de ordem sem eu querer .sort() muta o array original Copie antes: [...array].sort(...)
if (vagas == '40') deu true mas os tipos são diferentes Uso de == faz coerção de tipo Use sempre === e !==
TypeError: Cannot read properties of undefined Esqueceu de colocar await antes de uma Promise Confira se toda chamada assíncrona tem await dentro de uma função async
fetch falha com erro de CORS no console A API não libera requisições vindas do seu domínio local Use uma API que libere CORS publicamente (como JSONPlaceholder) ou rode via proxy/back-end nas aulas futuras

🏠 Atividade assíncrona (1 h)

  1. Escolha o tema do seu projeto autoral seguindo as regras da Seção 1.6.
  2. Crie um repositório público no GitHub chamado <seu-tema>-web (ex.: pantanal-plantas-web).
  3. Escreva um README.md na raiz do repositório contendo: - Uma descrição de 3 a 5 linhas do problema que o projeto resolve. - As entidades do domínio (no mínimo duas relacionadas) com seus campos, no mesmo estilo da Seção 3 do plano de curso (compare com Evento/Inscricao/Usuario do UniEventos). - As telas previstas: pelo menos listagem com filtro, detalhe, e uma área autenticada.
  4. Envie o link do repositório pelo canal indicado no SIGAA.

Critério de pronto: repositório público criado, README.md com descrição, modelo de dados (entidades + campos) e lista de telas.

✅ Checkpoint do projeto autoral

Ao final desta aula, seu repositório deve ter:

📚 Para aprofundar


Próxima aula (02, 19/08/2026): começamos o Vue de verdade — createApp, instância, data/methods (Options API) e Composition API com <script setup>, além das diretivas básicas (v-bind, v-on, v-model, v-if, v-for). Traga o ambiente instalado e o repositório do projeto autoral criado.

WebLab — Laboratório de Desenvolvimento Web · UNEMAT — Universidade do Estado de Mato Grosso · Campus Sinop · FACET
Prof. Ivan Luiz Pedroso Pires · Material didático de uso educacional; livre para consulta, estudo e reuso com atribuição.
Início · Banco de Desafios · Links úteis · Fontes no GitHub