Upgrade da API com namespace para a API modular

Os apps que usam qualquer API da Web com namespace do Firebase, desde as bibliotecas compat até a versão 8 ou anteriores, devem considerar a migração para a API modular usando as instruções deste guia.

Ao seguir estas instruções, supomos que você já conhece a API com namespace e que vai aplicar um bundler de módulos, como webpack ou Rollup (links em inglês), para upgrade e desenvolvimento modular contínuo de apps.

É altamente recomendável usar um bundler de módulos no ambiente de desenvolvimento. Se você não usar um, não poderá aplicar os principais benefícios da API modular em tamanho de app reduzido. Você vai precisar de npm ou yarn para instalar o SDK.

As etapas de upgrade deste guia serão baseadas em um app da Web imaginário que usa os SDKs do Authentication e do Cloud Firestore. Ao seguir os exemplos, é possível dominar os conceitos e as etapas práticas necessárias para fazer upgrade de todos os SDKs da Web do Firebase com suporte.

Sobre as bibliotecas com namespace (compat)

Dois tipos de bibliotecas estão disponíveis para o SDK da Web do Firebase:

  • Modular: uma nova plataforma de API criada para facilitar o tree shaking (remoção de código não utilizado), o que torna o app da Web menor e o mais rápido possível.
  • Com namespace (compat): uma plataforma de API conhecida que é totalmente compatível com as versões anteriores do SDK, o que permite que você faça upgrade sem mudar todo o código do Firebase de uma vez. Quase não há diferenças de tamanho ou desempenho entre as bibliotecas de compatibilidade e as versões com namespace.

Ao seguir estas instruções, supomos que você vai usar as bibliotecas de compatibilidade para facilitar o upgrade. Essas bibliotecas permitem que você continue usando o código com namespace e o código refatorado para a API modular. Isso significa que é possível compilar e depurar o app mais facilmente enquanto trabalha no processo de upgrade.

No caso de apps com uma exposição muito pequena ao SDK da Web do Firebase (por exemplo, um app que faz apenas uma chamada simples para as APIs do Authentication), pode ser prático refatorar um código com namespace antigo sem usar as bibliotecas de compatibilidade. Se você estiver fazendo o upgrade desse aplicativo, siga as instruções deste guia para "a API modular" sem usar as bibliotecas de compatibilidade.

Sobre o processo de upgrade

O escopo de cada etapa do upgrade é definido para que você possa terminar de editar o código-fonte do app e, em seguida, compilar e executá-lo sem interrupções. Confira um resumo das etapas do upgrade de um app:

  1. Adicionar as bibliotecas modulares e de compatibilidade ao app.
  2. Atualizar as instruções de importação do código para ser compatível.
  3. Refatorar o código de um único produto (por exemplo, o Authentication) para o estilo modular.
  4. Opcional: remover a biblioteca e o código de compatibilidade do Authentication para reduzir o tamanho do app antes de continuar.
  5. Refatorar funções para cada produto (por exemplo, Cloud Firestore, FCM etc.) para o estilo modular, compilar e testar até que todas as áreas estejam concluídas.
  6. Atualizar o código de inicialização para o estilo modular.
  7. Remover todas as instruções e o código de compatibilidade restantes do app.

Instalação da versão mais recente do SDK

Para começar, instale as bibliotecas modulares e de compatibilidade usando o npm:

npm i firebase@10.4.0

# OR

yarn add firebase@10.4.0

Atualização de importações para serem compatíveis

Para que o código continue funcionando depois de atualizar as dependências, altere as instruções de importação para usar a versão de compatibilidade de cada importação. Exemplo:

Antes: versão 8 ou anterior

import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

Depois: compatibilidade

// compat packages are API compatible with namespaced code
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';

Refatoração para o estilo modular

As APIs com namespace são baseadas em um namespace e padrão de serviço encadeados por pontos. Já com a abordagem modular, seu código será organizado principalmente com base em funções. Na API modular, o pacote firebase/app e outros pacotes não retornam uma exportação abrangente que contém todos os métodos do pacote. Os pacotes exportam funções individuais.

Na API modular, os serviços são transmitidos como o primeiro argumento e, em seguida, a função usa os detalhes do serviço para fazer o resto. Confira dois exemplos de como isso funciona. Neles, as chamadas para as APIs do Authentication e do Cloud Firestore são refatoradas.

Exemplo 1: refatoração de uma função do Authentication

Antes: compatibilidade

O código de compatibilidade é idêntico ao código com namespace, mas as importações foram alteradas.

import firebase from "firebase/compat/app";
import "firebase/compat/auth";

const auth = firebase.auth();
auth.onAuthStateChanged(user => { 
  // Check for user status
});

Depois: modular

A função getAuth usa firebaseApp como o primeiro parâmetro. A função onAuthStateChanged não é encadeada na instância auth como seria na API com namespace. Em vez disso, é uma função livre que usa auth como o primeiro parâmetro.

import { getAuth, onAuthStateChanged } from "firebase/auth";

const auth = getAuth(firebaseApp);
onAuthStateChanged(auth, user => {
  // Check for user status
});

Atualização do tratamento do método getRedirectResult do Auth

A API modular introduz uma mudança interruptiva em getRedirectResult. Quando nenhuma operação de redirecionamento é chamada, a API modular retorna null, diferente da API com namespace, que retornou um UserCredential com um usuário de null.

Antes: compatibilidade

const result = await auth.getRedirectResult()
if (result.user === null && result.credential === null) {
  return null;
}
return result;

Depois: modular

const result = await getRedirectResult(auth);
// Provider of the access token could be Facebook, Github, etc.
if (result === null || provider.credentialFromResult(result) === null) {
  return null;
}
return result;

Exemplo 2: refatoração de uma função do Cloud Firestore

Antes: compatibilidade

import "firebase/compat/firestore"

const db = firebase.firestore();
db.collection("cities").where("capital", "==", true)
    .get()
    .then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });

Depois: modular

A função getFirestore usa firebaseApp como o primeiro parâmetro, que foi retornado de initializeApp em um exemplo anterior. Observe como o código para formar uma consulta é muito diferente na API modular. Não há encadeamentos, e os métodos como query ou where ficam expostos como funções livres.

import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";

const db = getFirestore(firebaseApp);

const q = query(collection(db, "cities"), where("capital", "==", true));

const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
  // doc.data() is never undefined for query doc snapshots
  console.log(doc.id, " => ", doc.data());
});

Atualização das referências para DocumentSnapshot.exists do Firestore

A API modular introduz uma alteração interruptiva. A propriedade firestore.DocumentSnapshot.exists mudou para um método. A funcionalidade é essencialmente a mesma (verificar se um documento existe), mas é necessário refatorar seu código para usar o novo método conforme mostrado:

Antes: compatibilidade

if (snapshot.exists) {
  console.log("the document exists");
}

Depois: modular

if (snapshot.exists()) {
  console.log("the document exists");
}

Exemplo 3: combinação de estilos de código modulares e com namespace

Usar as bibliotecas de compatibilidade durante o upgrade permite que você continue usando o código com namespace e o código refatorado para a API modular. Isso significa que é possível manter o código com namespace atual do Cloud Firestore enquanto você refatora o código do Authentication ou outro código do SDK do Firebase para o estilo modular e ainda compilar o app com os dois estilos de código. Isso vale para códigos de API modulares e com namespace dentro de um produto, como o Cloud Firestore. Estilos de código novos e antigos podem coexistir, desde que você importe os pacotes de compatibilidade:

import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import { getDoc } from 'firebase/firestore'

const docRef = firebase.firestore().doc();
getDoc(docRef);

Embora o app faça a compilação, você não terá acesso aos benefícios do tamanho do app do código modular até que remova todas as instruções e o código de compatibilidade do app.

Atualização o código de inicialização

Atualize o código de inicialização do app para usar a sintaxe modular. É importante atualizar esse código depois de concluir a refatoração de todo o código do app, porque firebase.initializeApp() inicializa o estado global das APIs modulares e de compatibilidade, enquanto a função initializeApp() modular inicializa somente o estado para modular.

Antes: compatibilidade

import firebase from "firebase/compat/app"

firebase.initializeApp({ /* config */ });

Depois: modular

import { initializeApp } from "firebase/app"

const firebaseApp = initializeApp({ /* config */ });

Remoção do código de compatibilidade

Para obter os benefícios de tamanho da API modular, é preciso converter todas as invocações para o estilo modular mostrado acima e remover todas as instruções import "firebase/compat/* do código. Quando terminar, não haverá mais referências ao namespace global firebase.* ou a qualquer outro código no estilo de API com namespace.

Como usar a biblioteca de compatibilidade pela janela

A API modular é otimizada para trabalhar com módulos em vez do objeto window do navegador. As versões anteriores da biblioteca permitiam o carregamento e o gerenciamento do Firebase usando o namespace window.firebase. Isso não é recomendado a partir de agora, porque não permite a eliminação de códigos não utilizados. No entanto, a versão de compatibilidade do SDK para JavaScript funciona com o window para desenvolvedores que preferem não iniciar imediatamente o caminho de upgrade modular.

<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-firestore-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-auth-compat.js"></script>
<script>
   const firebaseApp = firebase.initializeApp({ /* Firebase config */ });
   const db = firebaseApp.firestore();
   const auth = firebaseApp.auth();
</script>

A biblioteca de compatibilidade usa código modular em segundo plano e fornece a mesma API que a API com namespace. Isso significa que é possível consultar a referência da API com namespace e os snippets de código com namespace para obter detalhes. Esse método não é recomendado para uso por longo prazo, apenas como início do upgrade para a biblioteca totalmente modular.

Benefícios e limitações do SDK modular

O SDK totalmente modular tem estas vantagens em relação às versões anteriores:

  • O SDK modular permite um tamanho de app significativamente reduzido. Ele adota o formato moderno de módulo JavaScript, o que permite práticas de tree shaking em que você importa apenas os artefatos necessários para o app. Dependendo do app, o tree shaking com o SDK modular pode resultar em 80% menos kilobytes do que um app comparável criado com a API com namespace.
  • O SDK modular continuará a se beneficiar do desenvolvimento contínuo de recursos, enquanto a API com namespace não.