1. Antes de comenzar
El SDK modular de Firebase JS es una reescritura del SDK de JS existente y se lanzará como la próxima versión principal. Permite a los desarrolladores excluir el código no utilizado del SDK de Firebase JS para crear paquetes más pequeños y lograr un mejor rendimiento.
La diferencia más notable en el SDK JS modular es que las funciones ahora están organizadas en funciones flotantes libres que usted importará, en lugar de en un único espacio de nombres firebase
que incluye todo. Esta nueva forma de organización del código es lo que permite la agitación de árboles, y aprenderá cómo actualizar cualquier aplicación que actualmente utilice el SDK v8 de Firebase JS al nuevo modular.
Para proporcionar un proceso de actualización fluido, se proporciona un conjunto de paquetes de compatibilidad. En este codelab, aprenderá cómo usar los paquetes de compatibilidad para migrar la aplicación pieza por pieza.
lo que construirás
En este codelab, migrará gradualmente una aplicación web de lista de seguimiento de existencias existente que utiliza el SDK de JS v8 al nuevo SDK de JS modular en tres etapas:
- Actualice la aplicación para usar los paquetes de compatibilidad
- Actualice la aplicación desde los paquetes de compatibilidad a la API modular pieza por pieza
- Utilice Firestore Lite, una implementación ligera del SDK de Firestore, para mejorar aún más el rendimiento de la aplicación.
Este codelab se centra en actualizar el SDK de Firebase. Otros conceptos y bloques de código se pasan por alto y se proporcionan para que usted simplemente los copie y pegue.
Lo que necesitarás
2. Prepárate
Obtener el código
Todo lo que necesitas para este proyecto reside en un repositorio de Git. Para comenzar, deberás tomar el código y abrirlo en tu entorno de desarrollo favorito.
Clona el repositorio Github del codelab desde la línea de comando:
git clone https://github.com/FirebaseExtended/codelab-modular-sdk.git
Alternativamente, si no tiene git instalado, puede descargar el repositorio como un archivo ZIP y descomprimir el archivo zip descargado.
Importar la aplicación
- Usando su IDE, abra o importe el directorio
codelab-modular-sdk
. - Ejecute
npm install
para instalar las dependencias necesarias para compilar y ejecutar la aplicación localmente. - Ejecute
npm run build
para compilar la aplicación. - Ejecute
npm run serve
para iniciar el servidor web - Abra una pestaña del navegador en http://localhost:8080
3. Establecer una línea de base
¿Cuál es tu punto de partida?
Su punto de partida es una aplicación de lista de seguimiento de acciones diseñada para este codelab. El código se ha simplificado para ilustrar los conceptos de este codelab y tiene poco manejo de errores. Si elige reutilizar parte de este código en una aplicación de producción, asegúrese de manejar cualquier error y probar completamente todo el código.
Asegúrate de que todo funcione en la aplicación:
- Inicie sesión de forma anónima utilizando el botón de inicio de sesión en la esquina superior derecha.
- Después de iniciar sesión, busque y agregue "NFLX", "SBUX" y "T" a la lista de seguimiento haciendo clic en el botón Agregar , escribiendo las letras y haciendo clic en la fila de resultados de búsqueda que aparece a continuación.
- Elimine una acción de la lista de seguimiento haciendo clic en la x al final de la fila.
- Vea las actualizaciones en tiempo real del precio de las acciones.
- Abra Chrome DevTools, vaya a la pestaña Red y marque Deshabilitar caché y Usar filas de solicitud grandes . Deshabilitar el caché garantiza que siempre obtengamos los últimos cambios después de una actualización y Usar filas de solicitud grandes hace que la fila muestre tanto el tamaño transmitido como el tamaño del recurso para un recurso. En este codelab, lo que más nos interesa es el tamaño de
main.js
- Cargue la aplicación en diferentes condiciones de red mediante aceleración simulada. Utilizará Slow 3G para medir el tiempo de carga en este codelab porque es donde un tamaño de paquete más pequeño ayuda más.
Ahora ingrese y comience a migrar la aplicación a la nueva API modular.
4. Utilice los paquetes de compatibilidad
Los paquetes de compatibilidad le permiten actualizar a la nueva versión del SDK sin cambiar todo el código de Firebase a la vez. Puede actualizarlos a la API modular gradualmente.
En este paso, actualizará la biblioteca de Firebase de v8 a la nueva versión y cambiará el código para usar los paquetes de compatibilidad. En los siguientes pasos, aprenderá cómo actualizar solo el código de Firebase Auth para usar primero la API modular y luego actualizar el código de Firestore.
Al final de cada paso, debería poder compilar y ejecutar la aplicación sin interrupciones y ver una disminución en el tamaño del paquete a medida que migramos cada producto.
Obtenga el nuevo SDK
Busque la sección de dependencias en package.json
y reemplácela con lo siguiente:
paquete.json
"dependencies": {
"firebase": "^9.0.0"
}
Reinstale las dependencias
Como cambiamos la versión de la dependencia, debemos volver a ejecutar npm install
para obtener la nueva versión de la dependencia.
Cambiar rutas de importación
Los paquetes de compatibilidad están expuestos en el submódulo firebase/compat
, por lo que actualizaremos las rutas de importación en consecuencia:
- Ir al archivo
src/firebase.ts
- Reemplace las importaciones existentes con las siguientes importaciones:
src/firebase.ts
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
Verificar que la aplicación funcione
- Ejecute
npm run build
para reconstruir la aplicación. - Abra una pestaña del navegador en http://localhost:8080 o actualice la pestaña existente.
- Juega con la aplicación. Todo debería seguir funcionando.
5. Actualice Auth para usar la API modular
Puedes actualizar los productos de Firebase en cualquier orden. En este codelab, primero actualizará Auth para aprender los conceptos básicos porque la API de Auth es relativamente simple. Actualizar Firestore es un poco más complicado y aprenderá cómo hacerlo a continuación.
Actualizar inicialización de autenticación
- Ir al archivo
src/firebase.ts
- Agregue la siguiente importación:
src/firebase.ts
import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
- Eliminar
import 'firebase/compat/auth'.
- Reemplace
export const firebaseAuth = app.auth();
con:
src/firebase.ts
export const firebaseAuth = initializeAuth(app, { persistence: [indexedDBLocalPersistence] });
- Eliminar
export type User = firebase.User;
al final del archivo.User
se exportará directamente asrc/auth.ts
, que cambiará a continuación.
Actualizar código de autenticación
- Ir al archivo
src/auth.ts
- Agregue las siguientes importaciones a la parte superior del archivo:
src/auth.ts
import {
signInAnonymously,
signOut,
onAuthStateChanged,
User
} from 'firebase/auth';
- Eliminar
User
deimport { firebaseAuth, User } from './firebase';
ya que ya importóUser
desde'firebase/auth'.
- Actualice funciones para usar la API modular.
Como ya vio anteriormente cuando actualizamos la declaración de importación, los paquetes en la versión 9 están organizados alrededor de funciones que puede importar, en contraste con las API de la versión 8 que se basan en un espacio de nombres y un patrón de servicio encadenados por puntos. Es esta nueva organización del código la que permite sacudir el código no utilizado, porque permite crear herramientas para analizar qué código se usa y cuál no.
En la versión 9, los servicios se pasan como primer argumento de las funciones. Los servicios son los objetos que se obtienen al inicializar un servicio de Firebase, por ejemplo, el objeto devuelto por getAuth()
o initializeAuth()
. Mantienen el estado de un servicio de Firebase en particular y la función usa el estado para realizar sus tareas. Apliquemos este patrón para implementar las siguientes funciones:
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 que la aplicación funcione
- Ejecute
npm run build
para reconstruir la aplicación. - Abra una pestaña del navegador en http://localhost:8080 o actualice la pestaña existente
- Juega con la aplicación. Todo debería seguir funcionando.
Verifique el tamaño del paquete
- Abra Chrome DevTools.
- Cambie a la pestaña Red .
- Actualice la página para capturar solicitudes de red.
- Busque main.js y verifique su tamaño. ¡Ha reducido el tamaño del paquete en 100 KB (36 KB comprimidos), o aproximadamente un 22 % más pequeño al cambiar solo unas pocas líneas de código! El sitio también se carga 0,75 segundos más rápido con una conexión 3G lenta.
6. Actualice la aplicación Firebase y Firestore para usar la API modular
Actualizar la inicialización de Firebase
- Vaya al archivo
src/firebase.ts.
- Reemplace
import firebase from 'firebase/compat/app';
con:
src/firebase.ts
import { initializeApp } from 'firebase/app';
- Reemplace
const app = firebase.initializeApp({...});
con:
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.appspot.com",
messagingSenderId: "875614679042",
appId: "1:875614679042:web:5813c3e70a33e91ba0371b"
});
Actualizar la inicialización de Firestore
- En el mismo archivo
src/firebase.ts,
reemplaceimport 'firebase/compat/firestore';
con
src/firebase.ts
import { getFirestore } from 'firebase/firestore';
- Reemplace
export const firestore = app.firestore();
con:
src/firebase.ts
export const firestore = getFirestore();
- Elimine todas las líneas después de "
export const firestore = ...
"
Actualizar importaciones
- Abra el archivo
src/services.ts.
- Elimine
FirestoreFieldPath
,FirestoreFieldValue
yQuerySnapshot
de la importación. La importación desde'./firebase'
ahora debería tener el siguiente aspecto:
src/servicios.ts
import { firestore } from './firebase';
- Importa las funciones y tipos que vas a utilizar en la parte superior del archivo:
**src/services.ts**
import {
collection,
getDocs,
doc,
setDoc,
arrayUnion,
arrayRemove,
onSnapshot,
query,
where,
documentId,
QuerySnapshot
} from 'firebase/firestore';
Actualizar búsqueda()
- Cree una referencia a la colección que contiene todos los tickers:
src/servicios.ts
const tickersCollRef = collection(firestore, 'current');
- Utilice
getDocs()
para recuperar todos los documentos de la colección:
src/servicios.ts
const tickers = await getDocs(tickersCollRef);
Consulte search()
para ver el código terminado.
Actualizar addToWatchList()
Use doc()
para crear una referencia de documento a la lista de seguimiento del usuario, luego agréguele un ticker usando setDoc()
con arrayUnion()
:
src/servicios.ts
export function addToWatchList(ticker: string, user: User) {
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
return setDoc(watchlistRef, {
tickers: arrayUnion(ticker)
}, { merge: true });
}
Actualizar eliminarFromWatchList()
De manera similar, elimine un ticker de la lista de seguimiento del usuario usando setDoc()
con arrayRemove()
:
src/servicios.ts
export function deleteFromWatchList(ticker: string, user: User) {
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
return setDoc(watchlistRef, {
tickers: arrayRemove(ticker)
}, { merge: true });
}
Actualizar subscribeToTickerChanges()
- Use
doc()
para crear una referencia de documento a la lista de seguimiento del usuario primero, luego escuche los cambios en la lista de seguimiento usandoonSnapshot()
:
src/servicios.ts
const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
const unsubscribe = onSnapshot(watchlistRef, snapshot => {
/* subscribe to ticker price changes */
});
- Una vez que tenga los tickers en la lista de seguimiento, use
query()
para crear una consulta para obtener sus precios y useonSnapshot()
para escuchar sus cambios de precios:
src/servicios.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 ver la implementación completa.
Actualizar subscribeToAllTickerChanges()
Primero usará collection()
para crear una referencia a la colección que contiene los precios de todos los tickers primero, luego usará onSnapshot()
para escuchar los cambios de precios:
src/servicios.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 que la aplicación funcione
- Ejecute
npm run build
para reconstruir la aplicación. - Abra una pestaña del navegador en http://localhost:8080 o actualice la pestaña existente
- Juega con la aplicación. Todo debería seguir funcionando.
Verifique el tamaño del paquete
- Abra Chrome DevTools.
- Cambie a la pestaña Red .
- Actualice la página para capturar solicitudes de red.
- Busque
main.js
y verifique su tamaño. Compárelo nuevamente con el tamaño del paquete original: hemos reducido el tamaño del paquete en más de 200 KB (63,8 KB comprimidos), o un 50 % más pequeño, lo que se traduce en un tiempo de carga 1,3 s más rápido.
7. Utilice Firestore Lite para acelerar la representación de la página inicial
¿Qué es Firestore Lite?
El SDK de Firestore ofrece almacenamiento en caché complejo, transmisión en tiempo real, almacenamiento persistente, sincronización sin conexión de múltiples pestañas, reintentos, simultaneidad optimista y mucho más y, por lo tanto, tiene un tamaño bastante grande. Pero es posible que simplemente quieras obtener los datos una vez, sin necesidad de ninguna de las funciones avanzadas. Para esos casos, Firestore creó una solución simple y liviana, un paquete completamente nuevo: Firestore Lite.
Un gran caso de uso para Firestore Lite es optimizar el rendimiento de la representación de la página inicial, donde solo necesita saber si un usuario ha iniciado sesión o no, y luego leer algunos datos de Firetore para mostrarlos.
En este paso, aprenderá cómo usar Firestore lite para reducir el tamaño del paquete y acelerar la representación de la página inicial, luego cargará dinámicamente el SDK principal de Firestore para suscribirse a actualizaciones en tiempo real.
Refactorizarás el código para:
- Mueva los servicios en tiempo real a un archivo separado, para que se puedan cargar dinámicamente mediante la importación dinámica.
- Cree nuevas funciones para usar Firestore Lite para recuperar la lista de seguimiento y los precios de las acciones.
- Utilice las nuevas funciones de Firestore Lite para recuperar datos para realizar la representación de la página inicial y luego cargar dinámicamente los servicios en tiempo real para escuchar las actualizaciones en tiempo real.
Mover servicios en tiempo real a un nuevo archivo
- Cree un nuevo archivo llamado
src/services.realtime.ts.
- Mueva las funciones
subscribeToTickerChanges()
ysubscribeToAllTickerChanges()
desrc/services.ts
al nuevo archivo. - Agregue las importaciones necesarias en la parte superior del nuevo archivo.
Aún necesitas hacer un par de cambios aquí:
- Primero, cree una instancia de Firestore desde el SDK principal de Firestore en la parte superior del archivo que se usará en las funciones. No puedes importar la instancia de Firestore desde
firebase.ts
aquí porque vas a cambiarla a una instancia de Firestore Lite en unos pocos pasos, que se usará solo para la representación de la página inicial. - En segundo lugar, elimine la variable
firstload
y el bloque if protegido por ella. Sus funcionalidades se trasladarán a nuevas funciones que creará en el siguiente paso.
src/servicios.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);
});
}
Utilice Firestore lite para recuperar datos
- Abra
src/services.ts.
- Cambie la ruta de importación de
'firebase/firestore'
a'firebase/firestore/lite',
agreguegetDoc
y elimineonSnapshot
de la lista de importación:
src/servicios.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';
- Agregue funciones para recuperar los datos necesarios para la representación de la página inicial usando Firestore Lite:
src/servicios.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
y cambie la ruta de importación de'firebase/firestore'
a'firebase/firestore/lite':
src/firebase.ts
import { getFirestore } from 'firebase/firestore/lite';
Átalos todos juntos
- Abra
src/main.ts.
- Necesitará las funciones recién creadas para recuperar datos para la representación de la página inicial y un par de funciones auxiliares para administrar el estado de la aplicación. Ahora actualice las importaciones:
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';
- Cargue
src/services.realtime
usando una importación dinámica en la parte superior del archivo. La variableloadRealtimeService
es una promesa que se resolverá con los servicios en tiempo real una vez que se cargue el código. Lo usará más adelante para suscribirse a actualizaciones en tiempo real.
src/main.ts
const loadRealtimeService = import('./services.realtime');
loadRealtimeService.then(() => {
setRealtimeServicesLoaded(true);
});
- Cambie la devolución de llamada de
onUserChange()
a una funciónasync
, para que podamos usarawait
en el cuerpo de la función:
src/main.ts
onUserChange(async user => {
// callback body
});
- Ahora obtenga los datos para realizar la representación de la página inicial utilizando las nuevas funciones que creamos en el paso anterior.
En la devolución de llamada onUserChange()
, busque la condición if en la que un usuario inicia sesión y copie y pegue el código dentro de la declaración 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
}
}
- En el bloque else donde ningún usuario ha iniciado sesión, obtenga información de precios para todas las acciones usando firestore lite, renderice la página y luego escuche los cambios de precios una vez que se carguen los servicios en tiempo real:
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 ver el código terminado.
Verificar que la aplicación funcione
- Ejecute
npm run build
para reconstruir la aplicación. - Abra una pestaña del navegador en http://localhost:8080 o actualice la pestaña existente.
Verifique el tamaño del paquete
- Abra las herramientas de desarrollo de Chrome.
- Cambie a la pestaña Red .
- Actualizar la página para capturar solicitudes de red
- Busque
main.js
y verifique su tamaño. - Ahora son sólo 115 KB (34,5 KB comprimidos). ¡Eso es un 75% más pequeño que el tamaño del paquete original, que era de 446 KB (138 KB comprimidos con gzip)! Como resultado, el sitio se carga dos segundos más rápido con una conexión 3G: ¡una gran mejora en el rendimiento y la experiencia del usuario!
8. Felicitaciones
¡Felicitaciones, actualizó exitosamente la aplicación y la hizo más pequeña y más rápida!
Usó los paquetes de compatibilidad para actualizar la aplicación pieza por pieza y usó Firestore Lite para acelerar la representación de la página inicial y luego cargó dinámicamente el Firestore principal para transmitir los cambios de precios.
También redujo el tamaño del paquete y mejoró su tiempo de carga en el transcurso de este codelab:
principal.js | tamaño del recurso (kb) | tamaño comprimido (kb) | tiempo de carga (s) (más lento 3g) |
v8 | 446 | 138 | 4.92 |
compatibilidad v9 | 429 | 124 | 4.65 |
v9 solo autenticación modular | 348 | 102 | 4.2 |
v9 totalmente modular | 244 | 74,6 | 3.66 |
v9 totalmente modular + Firestore lite | 117 | 34,9 | 2,88 |
Ahora conoce los pasos clave necesarios para actualizar una aplicación web que utiliza la versión 8 del SDK Firebase JS para usar el nuevo SDK JS modular.