firebase.js ПРОСТО ОГРОМНЫЙ (и что мы можем с этим сделать)
- суббота, 9 декабря 2017 г. в 03:14:29
 
Он действительно огромный — просто посмотрите на него:
Эта штука весит 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 в сжатом виде и они работают не покладая рук.
Но раз невозможно уменьшить размер этого куска — мы просто отложим его загрузку до того момента, когда он будет реально нужен.
И сделаем это так что никто и не заметит :)
Для сервера это предельно просто:
// 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() ко всем промисам (ну и зарепортить их);<link rel="preload" href="/assets/firebase.js" as="script"> в head;hot-loader webpack'a, нам помгло использование default приложения а не именованного (https://firebase.google.com/docs/web/setup);