Он действительно огромный — просто посмотрите на него:

Эта штука весит 103кб (в сжатом виде). Больше чем код приложения — интернет-магазин -(58kb) и сравнима со всем остальным кодом в vendor бандле (156kb) — включающем react, react-dom, react-router, moment.js, lodash и кучу других библиотек. Что еще хуже — firebase нужен не на всех страницах, и очень часто не нужен к моменту загрузку сайта.
Что можно с этим сделать?
Не слишком много, как оказалось. Включение отдельных модулей не работало (на тот момент в webpack@2) да и помогло бы не слишком сильно — все равно требовалось бы включить auth и database + модуль app(42kb + 40kb + 3kb) — что дало бы 83% исходного размера так или иначе. Кроме того, сами модули auth и database совершенно монолитны (наглядно видно на скриншоте выше) и уже сжаты лучше некуда.
К слову — сжаты они с помощьюClouse Compilerили чем там Google пользуется на текущий момент — удачи минифицировать банлд с помощьюuglifyи не сломать ничего.
Но что-то же нужно делать!
Конечно. 103kb кода валяющиющийся мертвым грузом в бандле — это очень неприятно. Подумайте — люди жалуются на размер react и переходят на inferno/preact — а react+react-dom весит всего 39kb в сжатом виде и они работают не покладая рук.
Но раз невозможно уменьшить размер этого куска — мы просто отложим его загрузку до того момента, когда он будет реально нужен.
И сделаем это так что никто и не заметит :)
Webpack и dynamic import спешит на помощь
Для сервера это предельно просто:
// firebaseImport.server.js import * as firebase from 'firebase/firebase-node' export default function importFirebase() { return Promise.resolve(firebase) }
Для клиента — чуть сложнее, но все равно просто:
// firebaseImport.browser.js export default function importFirebase() { // динамический import, вернет Promise // "магические" комментарии в импорте дадут возможность получить вменяемое имя // а не `0.js` return import(/* webpackChunkName: 'firebase' */ /* webpackMode: 'lazy' */ 'firebase/firebase-browser') }
Это все необходимо для универсального рендеринга на сервере. Если ваше приложение работает только на клиене — просто игнорируйте серверную часть
Далее нужно сделать полиморфный импорт в webpack-конфиге:
// browser webpack-config resolve: { alias: { 'firebaseImport$': path.join('path', 'to', 'your', 'firebaseImport.browser.js') } } // ... // server webpack-config resolve: { alias: { 'firebaseImport$': path.join('path', 'to', 'your', 'firebaseImport.server.js') } } // ...
Да, мы собираем сервер webpack-ом. Постарайтесь нас не осуждать, нам нужны работающие importы в универсальном коде, а node.js не понимает их нативно.Теперь require(firebaseImport$) вернет Promise который сразу выполнен на сервере и который лениво загрузит firebase на клиенте. После первой загрузки на клиенте этот импорт тоже станет 'выполненым', и последующие обращения к firebase будут уже почти мгновенными.
Далее нужно инициализировать клиент firebase:
export default function firebase() { return importFirebase().then((firebase) => { // Собственно инициализция firebase. Обычно что-то вроде этого: const app = firebase.initializeApp({ apiKey: '<your-api-key>', authDomain: '<your-auth-domain>', databaseURL: '<your-database-url>', storageBucket: '<your-storage-bucket>', messagingSenderId: '<your-sender-id>' }) // возвращаем реально используемые интерфейсы: return { database: app.database() auth: app.auth() } }) }
И собственно всё. Конечно, теперь использование firebase стало более многословным:
// до: import firebase from 'what/ever/firebase' const {auth} = firebase export function signIn({email, password}) { auth.signInWithEmailAndPassword(email, password) .then((user) => { // ... }) } // --- // после: import firebase from 'what/ever/firebase' export function signIn({email, password}) { firebase().then(({auth}) => { auth.signInWithEmailAndPassword(email, password) .then((user) => { // ... }) }) }
Но результат того стоит:


Несколько дополнительных моментов
- Нужно не забыть добавить обработку ошибок через
catch()ко всем промисам (ну и зарепортить их); - firebase.js можно получить на клиенте к моменту загрузки основных бандлов в большинстве браузеров — простым добавлением
<link rel="preload" href="/assets/firebase.js" as="script">вhead; - Инициализация firebase может поругаться с
hot-loaderwebpack'a, нам помгло использованиеdefaultприложения а не именованного (https://firebase.google.com/docs/web/setup); - Крайне полезно время от времени запускать webpack-bundle-analyzer — размер некоторых пакетов может вас неприятно удивить (momen.js и его локали могут даже шокировать);
- Пост находится в react.js хабе потому что говорю webpack-подразумеваю react, но (очевидно) данный метод напрямую ни от реакта ни от firebase не зависит, может быть использован для любых библиотек которые нужны "лениво";
