1. Antes de começar
O SDK modular do Firebase para JavaScript é uma reescrita do SDK para JS existente e será lançado como a próxima versão principal. Ele permite que os desenvolvedores excluam o código não utilizado do SDK do Firebase para JavaScript para criar pacotes menores e ter um desempenho melhor.
A diferença mais notável no SDK modular do JS é que os recursos agora são organizados em funções flutuantes livres que você vai importar, em vez de um único namespace firebase
que inclui tudo. Essa nova forma de organização do código é o que permite o desligamento de árvores, e você vai aprender a atualizar qualquer app que esteja usando o SDK do Firebase para JavaScript v8 para o novo modular.
Para facilitar o processo de upgrade, um conjunto de pacotes de compatibilidade é fornecido. Neste codelab, você vai aprender a usar os pacotes de compatibilidade para portar o app peça por peça.
O que você vai criar
Neste codelab, você vai migrar gradualmente um app da Web de lista de observação de ações que usa o SDK v8 JS para o novo SDK modular JS em três etapas:
- Fazer upgrade do app para usar os pacotes de compatibilidade
- Fazer upgrade do app dos pacotes de compatibilidade para a API modular peça por peça
- Use o Firestore Lite, uma implementação leve do SDK do Firestore, para melhorar ainda mais o desempenho do app.
Este codelab é focado no upgrade do SDK do Firebase. Outros conceitos e blocos de código são citados rapidamente e fornecidos para que você simplesmente os copie e cole.
O que é necessário
- Um navegador de sua escolha, como o Chrome
- O ambiente de desenvolvimento integrado/editor de texto de sua escolha, como WebStorm, Atom, Sublime ou VS Code
- O gerenciador de pacotes npm, que geralmente vem com o Node.js
- O exemplo de código do codelab (confira a próxima etapa do codelab para saber como acessar o código).
2. Começar a configuração
Buscar o código
Tudo o que você precisa para este projeto está em um repositório Git. Para começar, abra o código no ambiente de desenvolvimento de sua preferência.
Clone o repositório do GitHub (link em inglês) do codelab na linha de comando:
git clone https://github.com/FirebaseExtended/codelab-modular-sdk.git
Se o git não estiver instalado, faça o download do repositório como um arquivo ZIP e descompacte o arquivo ZIP transferido por download.
Importar o app
- Usando o ambiente de desenvolvimento integrado, abra ou importe o diretório
codelab-modular-sdk
. - Execute
npm install
para instalar as dependências necessárias para criar e executar o app localmente. - Execute
npm run build
para criar o app. - Execute
npm run serve
para iniciar o servidor da Web. - Abra uma guia do navegador e acesse http://localhost:8080.
3. Estabelecer um valor de referência
Qual é seu ponto de partida?
Seu ponto de partida é um app de lista de observação de ações projetado para este codelab. O código foi simplificado para ilustrar os conceitos deste codelab e tem pouco tratamento de erros. Se você optar por reutilizar algum desses códigos em um app de produção, não se esqueça de corrigir erros e testar completamente todo o código.
Verifique se tudo funciona no app:
- Faça login anonimamente usando o botão login no canto superior direito.
- Depois de fazer login, pesquise e adicione "NFLX", "SBUX" e "T" à lista de observação clicando no botão Adicionar, digitando as letras e clicando na linha de resultados da pesquisa que aparece abaixo.
- Para remover uma ação da lista de observação, clique na x no final da linha.
- Confira as atualizações em tempo real do preço das ações.
- Abra o Chrome DevTools, acesse a guia Rede e marque Desativar cache e Usar linhas de solicitação grandes. A opção Desativar cache garante que sempre recebamos as mudanças mais recentes após uma atualização, e Usar linhas de solicitação grandes faz com que a linha mostre o tamanho transmitido e o tamanho do recurso. Neste codelab, nosso interesse principal é o tamanho de
main.js
.
- Carregue o app em diferentes condições de rede usando a limitação simulada. Você vai usar a conexão 3G lenta para medir o tempo de carregamento neste codelab, porque é onde um tamanho de pacote menor ajuda mais.
Agora, comece a migrar o app para a nova API modular.
4. Usar os pacotes de compatibilidade
Os pacotes de compatibilidade permitem que você faça upgrade para a nova versão do SDK sem alterar todo o código do Firebase de uma só vez. Você pode fazer upgrade para a API modular gradualmente.
Nesta etapa, você vai fazer upgrade da biblioteca do Firebase da v8 para a nova versão e mudar o código para usar os pacotes de compatibilidade. Nas próximas etapas, você vai aprender a fazer upgrade apenas do código do Firebase Auth para usar a API modular primeiro e, em seguida, fazer upgrade do código do Firestore.
Ao final de cada etapa, você poderá compilar e executar o app sem falhas e observar uma diminuição no tamanho do pacote à medida que migramos cada produto.
Acessar o novo SDK
Encontre a seção de dependências no package.json
e substitua-a pelo seguinte:
package.json
"dependencies": {
"firebase": "^9.0.0"
}
Reinstalar as dependências
Como mudamos a versão da dependência, precisamos executar npm install
novamente para acessar a nova versão dela.
Mudar as rotas de importação
Os pacotes de compatibilidade são expostos no submódulo firebase/compat
. Por isso, vamos atualizar os caminhos de importação:
- Acesse o arquivo
src/firebase.ts
- Substitua as importações atuais pelas seguintes:
src/firebase.ts
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
Verificar se o app funciona
- Execute
npm run build
para recriar o app. - Abra uma guia do navegador em http://localhost:8080 ou atualize a guia atual.
- Teste o app. Tudo deve continuar funcionando.
5. Fazer upgrade da Auth para usar a API modular
É possível fazer upgrade dos produtos do Firebase em qualquer ordem. Neste codelab, você vai atualizar a Auth primeiro para aprender os conceitos básicos, porque a API Auth é relativamente simples. Atualizar o Firestore é um pouco mais complicado, e você vai aprender a fazer isso a seguir.
Atualização da inicialização da autenticação
- Acesse o arquivo
src/firebase.ts
- Adicione a seguinte importação:
src/firebase.ts
import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
- Excluir
import ‘firebase/compat/auth'.
- Substitua
export const firebaseAuth = app.auth();
por:
src/firebase.ts
export const firebaseAuth = initializeAuth(app, { persistence: [indexedDBLocalPersistence] });
- Remova
export type User = firebase.User;
no final do arquivo. OUser
será exportado diretamente emsrc/auth.ts
, que você vai mudar em seguida.
Atualizar código de autenticação
- Acesse o arquivo
src/auth.ts
- Adicione as seguintes importações à parte de cima do arquivo:
src/auth.ts
import {
signInAnonymously,
signOut,
onAuthStateChanged,
User
} from 'firebase/auth';
- Remova
User
deimport { firebaseAuth, User } from './firebase';
, já que você já importouUser
de‘firebase/auth'.
- Atualize as funções para usar a API modular.
Como você já viu anteriormente, quando atualizamos a instrução de importação, os pacotes na versão 9 são organizados em torno de funções que podem ser importadas, em contraste com as APIs da versão 8, que são baseadas em um namespace e padrão de serviço encadeados por pontos. É essa nova organização de código que permite o uso do tree shaking em código não utilizado, porque permite que as ferramentas de build analisem qual código é usado e qual não é.
Na versão 9, os serviços são transmitidos como o primeiro argumento para as funções. Os serviços são os objetos que você recebe ao inicializar um serviço do Firebase, por exemplo, o objeto retornado de getAuth()
ou initializeAuth()
. Eles contêm o estado de um serviço específico do Firebase, e a função usa o estado para realizar as tarefas. Vamos aplicar esse padrão para implementar as seguintes funções:
src/auth.ts
export function firebaseSignInAnonymously() {
return signInAnonymously(firebaseAuth);
}
export function firebaseSignOut() {
return signOut(firebaseAuth);
}
export function onUserChange(callback: (user: User | null) => void) {
return onAuthStateChanged(firebaseAuth, callback);
}
export { User } from 'firebase/auth';
Verificar se o app funciona
- Execute
npm run build
para recriar o app. - Abra uma guia do navegador em http://localhost:8080 ou atualize a guia atual.
- Teste o app. Tudo deve continuar funcionando.
Verificar o tamanho do pacote
- Abra o Chrome DevTools.
- Mude para a guia Rede.
- Atualize a página para capturar as solicitações de rede.
- Procure main.js e verifique o tamanho dele. Você reduziu o tamanho do pacote em 100 KB (36 KB compactados com Gzip), ou cerca de 22%, mudando apenas algumas linhas de código. O site também está carregando 0,75 segundo mais rápido em uma conexão 3G lenta.
6. Fazer upgrade do Firebase App e do Firestore para usar a API modular
Atualizar a inicialização do Firebase
- Acesse o arquivo
src/firebase.ts.
- Substitua
import firebase from ‘firebase/compat/app';
por:
src/firebase.ts
import { initializeApp } from 'firebase/app';
- Substitua
const app = firebase.initializeApp({...});
por:
src/firebase.ts
const app = initializeApp({
apiKey: "AIzaSyBnRKitQGBX0u8k4COtDTILYxCJuMf7xzE",
authDomain: "exchange-rates-adcf6.firebaseapp.com",
databaseURL: "https://exchange-rates-adcf6.firebaseio.com",
projectId: "exchange-rates-adcf6",
storageBucket: "exchange-rates-adcf6.firebasestorage.app",
messagingSenderId: "875614679042",
appId: "1:875614679042:web:5813c3e70a33e91ba0371b"
});
Atualizar a inicialização do Firestore
- No mesmo arquivo
src/firebase.ts,
, substituaimport 'firebase/compat/firestore';
por
src/firebase.ts
import { getFirestore } from 'firebase/firestore';
- Substitua
export const firestore = app.firestore();
por:
src/firebase.ts
export const firestore = getFirestore();
- Remova todas as linhas após "
export const firestore = ...
"
Atualizar importações
- Abrir o arquivo
src/services.ts.
- Remova
FirestoreFieldPath
,FirestoreFieldValue
eQuerySnapshot
da importação. A importação de'./firebase'
vai ficar assim:
src/services.ts
import { firestore } from './firebase';
- Importe as funções e os tipos que você vai usar na parte de cima do arquivo:
**src/services.ts**
import {
collection,
getDocs,
doc,
setDoc,
arrayUnion,
arrayRemove,
onSnapshot,
query,
where,
documentId,
QuerySnapshot
} from 'firebase/firestore';
Atualizar search()
- Crie uma referência à coleção que contém todos os tickers:
src/services.ts
const tickersCollRef = collection(firestore, 'current');
- Use
getDocs()
para buscar todos os documentos da coleção:
src/services.ts
const tickers = await getDocs(tickersCollRef);
Consulte search()
para conferir o código concluído.
Atualização de addToWatchList()
Use doc()
para criar uma referência de documento à lista de observação do usuário e, em seguida, adicione um ticker usando setDoc()
com arrayUnion()
:
src/services.ts
export function addToWatchList(ticker: string, user: User) {
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
return setDoc(watchlistRef, {
tickers: arrayUnion(ticker)
}, { merge: true });
}
Atualização de deleteFromWatchList()
Da mesma forma, remova um ticker da lista de interesses do usuário usando setDoc()
com arrayRemove()
:
src/services.ts
export function deleteFromWatchList(ticker: string, user: User) {
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
return setDoc(watchlistRef, {
tickers: arrayRemove(ticker)
}, { merge: true });
}
Atualização de subscribeToTickerChanges()
- Use
doc()
para criar uma referência de documento à lista de observação do usuário primeiro e, em seguida, detecte as mudanças na lista de observação usandoonSnapshot()
:
src/services.ts
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
const unsubscribe = onSnapshot(watchlistRef, snapshot => {
/* subscribe to ticker price changes */
});
- Depois de adicionar os símbolos à lista de observação, use
query()
para criar uma consulta e buscar os preços deles eonSnapshot()
para detectar as mudanças de preço:
src/services.ts
const priceQuery = query(
collection(firestore, 'current'),
where(documentId(), 'in', tickers)
);
unsubscribePrevTickerChanges = onSnapshot(priceQuery, snapshot => {
if (firstload) {
performance && performance.measure("initial-data-load");
firstload = false;
logPerformance();
}
const stocks = formatSDKStocks(snapshot);
callback(stocks);
});
Consulte subscribeToTickerChanges() para conferir a implementação completa.
Atualização de subscribeToAllTickerChanges()
Primeiro, você vai usar collection()
para criar uma referência à coleção que contém os preços de todos os tickers. Depois, use onSnapshot()
para detectar mudanças de preço:
src/services.ts
export function subscribeToAllTickerChanges(callback: TickerChangesCallBack) {
const tickersCollRef = collection(firestore, 'current');
return onSnapshot(tickersCollRef, snapshot => {
if (firstload) {
performance && performance.measure("initial-data-load");
firstload = false;
logPerformance();
}
const stocks = formatSDKStocks(snapshot);
callback(stocks);
});
}
Verificar se o app funciona
- Execute
npm run build
para recriar o app. - Abra uma guia do navegador em http://localhost:8080 ou atualize a guia atual.
- Teste o app. Tudo deve continuar funcionando.
Verificar o tamanho do pacote
- Abra o Chrome DevTools.
- Mude para a guia Rede.
- Atualize a página para capturar as solicitações de rede.
- Procure
main.js
e verifique o tamanho dele. Compare com o tamanho original do pacote novamente.Reduzimos o tamanho do pacote em mais de 200 KB (63,8 KB compactado com Gzip) ou 50% menor, o que significa um tempo de carregamento 1,3s mais rápido.
7. Use o Firestore Lite para acelerar a renderização inicial da página
O que é o Firestore Lite?
O SDK do Firestore oferece armazenamento em cache complexo, streaming em tempo real, armazenamento persistente, sincronização off-line com várias guias, novas tentativas, simultaneidade otimista e muito mais. Por isso, ele é bastante grande. Mas talvez você queira apenas acessar os dados uma vez, sem precisar dos recursos avançados. Para esses casos, o Firestore criou uma solução simples e leve, um pacote totalmente novo: o Firestore Lite.
Um ótimo caso de uso para o Firestore Lite é otimizar o desempenho da renderização inicial da página, em que você só precisa saber se um usuário está ou não conectado e ler alguns dados do Firestore para exibição.
Nesta etapa, você vai aprender a usar o Firestore Lite para reduzir o tamanho do pacote e acelerar a renderização inicial da página. Em seguida, vai carregar o SDK principal do Firestore de forma dinâmica para se inscrever em atualizações em tempo real.
Você vai refatorar o código para:
- Mova os serviços em tempo real para um arquivo separado para que eles possam ser carregados dinamicamente usando a importação dinâmica.
- Crie novas funções para usar o Firestore Lite e extrair a lista de observação e os preços das ações.
- Use as novas funções do Firestore Lite para recuperar dados e fazer a renderização inicial da página. Em seguida, carregue dinamicamente os serviços em tempo real para ouvir atualizações em tempo real.
Mover serviços em tempo real para um novo arquivo
- Crie um novo arquivo chamado
src/services.realtime.ts.
- Mova as funções
subscribeToTickerChanges()
esubscribeToAllTickerChanges()
desrc/services.ts
para o novo arquivo. - Adicione as importações necessárias ao início do novo arquivo.
Você ainda precisa fazer algumas mudanças aqui:
- Primeiro, crie uma instância do Firestore usando o SDK principal do Firestore na parte de cima do arquivo que será usado nas funções. Não é possível importar a instância do Firestore de
firebase.ts
aqui porque você vai mudar para uma instância do Firestore Lite em algumas etapas, que será usada apenas para a renderização inicial da página. - Em segundo lugar, livre-se da variável
firstload
e do bloco if protegido por ela. As funcionalidades deles serão movidas para novas funções que você vai criar na próxima etapa.
src/services.realtime.ts
import { User } from './auth'
import { TickerChange } from './models';
import { collection, doc, onSnapshot, query, where, documentId, getFirestore } from 'firebase/firestore';
import { formatSDKStocks } from './services';
const firestore = getFirestore();
type TickerChangesCallBack = (changes: TickerChange[]) => void
export function subscribeToTickerChanges(user: User, callback: TickerChangesCallBack) {
let unsubscribePrevTickerChanges: () => void;
// Subscribe to watchlist changes. We will get an update whenever a ticker is added/deleted to the watchlist
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
const unsubscribe = onSnapshot(watchlistRef, snapshot => {
const doc = snapshot.data();
const tickers = doc ? doc.tickers : [];
if (unsubscribePrevTickerChanges) {
unsubscribePrevTickerChanges();
}
if (tickers.length === 0) {
callback([]);
} else {
// Query to get current price for tickers in the watchlist
const priceQuery = query(
collection(firestore, 'current'),
where(documentId(), 'in', tickers)
);
// Subscribe to price changes for tickers in the watchlist
unsubscribePrevTickerChanges = onSnapshot(priceQuery, snapshot => {
const stocks = formatSDKStocks(snapshot);
callback(stocks);
});
}
});
return () => {
if (unsubscribePrevTickerChanges) {
unsubscribePrevTickerChanges();
}
unsubscribe();
};
}
export function subscribeToAllTickerChanges(callback: TickerChangesCallBack) {
const tickersCollRef = collection(firestore, 'current');
return onSnapshot(tickersCollRef, snapshot => {
const stocks = formatSDKStocks(snapshot);
callback(stocks);
});
}
Usar o Firestore Lite para buscar dados
- Abrir
src/services.ts.
- Mude o caminho de importação de
‘firebase/firestore'
para‘firebase/firestore/lite',
, adicionegetDoc
e removaonSnapshot
da lista de importação:
src/services.ts
import {
collection,
getDocs,
doc,
setDoc,
arrayUnion,
arrayRemove,
// onSnapshot, // firestore lite doesn't support realtime updates
query,
where,
documentId,
QuerySnapshot,
getDoc // add this import
} from 'firebase/firestore/lite';
- Adicione funções para buscar os dados necessários para a renderização inicial da página usando o Firestore Lite:
src/services.ts
export async function getTickerChanges(tickers: string[]): Promise<TickerChange[]> {
if (tickers.length === 0) {
return [];
}
const priceQuery = query(
collection(firestore, 'current'),
where(documentId(), 'in', tickers)
);
const snapshot = await getDocs(priceQuery);
performance && performance.measure("initial-data-load");
logPerformance();
return formatSDKStocks(snapshot);
}
export async function getTickers(user: User): Promise<string[]> {
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
const data = (await getDoc(watchlistRef)).data();
return data ? data.tickers : [];
}
export async function getAllTickerChanges(): Promise<TickerChange[]> {
const tickersCollRef = collection(firestore, 'current');
const snapshot = await getDocs(tickersCollRef);
performance && performance.measure("initial-data-load");
logPerformance();
return formatSDKStocks(snapshot);
}
- Abra
src/firebase.ts
e mude o caminho de importação de‘firebase/firestore'
para‘firebase/firestore/lite':
.
src/firebase.ts
import { getFirestore } from 'firebase/firestore/lite';
Vincule tudo
- Abrir
src/main.ts.
- Você vai precisar das funções recém-criadas para buscar dados para a renderização inicial da página e algumas funções auxiliares para gerenciar o estado do app. Agora atualize as importações:
src/main.ts
import { renderLoginPage, renderUserPage } from './renderer';
import { getAllTickerChanges, getTickerChanges, getTickers } from './services';
import { onUserChange } from './auth';
import { getState, setRealtimeServicesLoaded, setUser } from './state';
import './styles.scss';
- Carregue
src/services.realtime
usando uma importação dinâmica na parte de cima do arquivo. A variávelloadRealtimeService
é uma promessa que será resolvida com os serviços em tempo real depois que o código for carregado. Ele será usado mais tarde para se inscrever em atualizações em tempo real.
src/main.ts
const loadRealtimeService = import('./services.realtime');
loadRealtimeService.then(() => {
setRealtimeServicesLoaded(true);
});
- Mude o callback de
onUserChange()
para uma funçãoasync
para que possamos usarawait
no corpo da função:
src/main.ts
onUserChange(async user => {
// callback body
});
- Agora, busque os dados para renderizar a página inicial usando as novas funções que criamos na etapa anterior.
No callback onUserChange()
, encontre a condição "if" em que um usuário está conectado e copie e cole o código dentro da instrução "if":
src/main.ts
onUserChange(async user => {
// LEAVE THE EXISTING CODE UNCHANGED HERE
...
if (user) {
// REPLACE THESE LINES
// user page
setUser(user);
// show loading screen in 500ms
const timeoutId = setTimeout(() => {
renderUserPage(user, {
loading: true,
tableData: []
});
}, 500);
// get data once if realtime services haven't been loaded
if (!getState().realtimeServicesLoaded) {
const tickers = await getTickers(user);
const tickerData = await getTickerChanges(tickers);
clearTimeout(timeoutId);
renderUserPage(user, { tableData: tickerData });
}
// subscribe to realtime updates once realtime services are loaded
loadRealtimeService.then(({ subscribeToTickerChanges }) => {
unsubscribeTickerChanges = subscribeToTickerChanges(user, stockData => {
clearTimeout(timeoutId);
renderUserPage(user, { tableData: stockData })
});
});
} else {
// DON'T EDIT THIS PART, YET
}
}
- No bloco else em que nenhum usuário está conectado, busque informações de preço para todas as ações usando o Firestore Lite, renderize a página e detecte mudanças de preço quando os serviços em tempo real forem carregados:
src/main.ts
if (user) {
// DON'T EDIT THIS PART, WHICH WE JUST CHANGED ABOVE
...
} else {
// REPLACE THESE LINES
// login page
setUser(null);
// show loading screen in 500ms
const timeoutId = setTimeout(() => {
renderLoginPage('Landing page', {
loading: true,
tableData: []
});
}, 500);
// get data once if realtime services haven't been loaded
if (!getState().realtimeServicesLoaded) {
const tickerData = await getAllTickerChanges();
clearTimeout(timeoutId);
renderLoginPage('Landing page', { tableData: tickerData });
}
// subscribe to realtime updates once realtime services are loaded
loadRealtimeService.then(({ subscribeToAllTickerChanges }) => {
unsubscribeAllTickerChanges = subscribeToAllTickerChanges(stockData => {
clearTimeout(timeoutId);
renderLoginPage('Landing page', { tableData: stockData })
});
});
}
Consulte src/main.ts para conferir o código concluído.
Verificar se o app funciona
- Execute
npm run build
para recriar o app. - Abra uma guia do navegador em http://localhost:8080 ou atualize a guia atual.
Verificar o tamanho do pacote
- Abra o Chrome DevTools.
- Mude para a guia Rede.
- Atualize a página para capturar solicitações de rede
- Procure
main.js
e verifique o tamanho dele. - Agora, ele tem apenas 115 KB (34,5 KB compactado com Gzip). Isso é 75% menor que o tamanho original do pacote, que era de 446 KB(138 KB compactado com Gzip). Como resultado, o site está carregando mais de 2 segundos mais rápido em uma conexão 3G, uma grande melhoria na performance e na experiência do usuário.
8. Parabéns
Parabéns! Você fez o upgrade do app e o deixou menor e mais rápido.
Você usou os pacotes de compatibilidade para fazer upgrade do app peça por peça e usou o Firestore Lite para acelerar a renderização inicial da página e, em seguida, carregou dinamicamente o Firestore principal para transmitir mudanças de preço.
Você também reduziu o tamanho do pacote e melhorou o tempo de carregamento ao longo deste codelab:
main.js | tamanho do recurso (KB) | Tamanho compactado (KB) | tempo de carregamento (s) (em 3G lento) |
v8 | 446 | 138 | 4,92 |
Compatibilidade com v9 | 429 | 124 | 4,65 |
Auth modular somente na v9 | 348 | 102 | 4.2 |
v9 totalmente modular | 244 | 74,6 | 3.66 |
v9 totalmente modular + Firestore Lite | 117 | 34,9 | 2,88 |
Agora você conhece as principais etapas necessárias para fazer upgrade de um app da Web que usa o SDK do Firebase para JavaScript v8 e usar o novo SDK modular para JavaScript.