Удалите неиспользуемый код

В этой практической работе вы улучшите производительность приложения, удалив неиспользуемые и ненужные зависимости.

Скриншот из приложения

Измерить

Прежде чем оптимизировать сайт, всегда полезно оценить его эффективность.

  • Чтобы посмотреть сайт, нажмите View App (Посмотреть приложение), а затем – Fullscreen (Полноэкранный режим) полноэкранный режим.

Нажмите на понравившегося котенка. В этом приложении используется Realtime Database Firebase, поэтому счет обновляется в реальном времени и синхронизируется со всеми пользователями приложения. 🐈

  1. Нажмите Ctrl + Shift + J (или Command + Option + J на Mac), чтобы открыть Инструменты разработчика.
  2. Откройте вкладку Сеть.
  3. Установите флажок Отключить кеш.
  4. Перезапустите приложение.

Исходный размер пакета: 992 КБ.

Для загрузки этого простого приложения отправляется почти 1 МБ JavaScript!

Посмотрите предупреждения в инструментах разработчика.

  • Перейдите на вкладку Консоль.
  • Убедитесь, что в раскрывающемся списке уровней рядом с полем ввода Filter включен параметр Warnings.

Фильтр предупреждений

  • Посмотрите на предупреждение.

Предупреждение в консоли

Firebase – одна из библиотек, используемых в этом приложении. Она предупреждает разработчиков о том, что не нужно импортировать весь пакет, а только используемые компоненты. Другими словами, в этом приложении есть неиспользуемые библиотеки, которые можно удалить, чтобы оно загружалось быстрее.

В некоторых случаях вместо используемой библиотеки можно выбрать более простую альтернативу. О том, как удалять ненужные библиотеки, рассказывается далее в этом руководстве.

Анализ набора

В приложении есть две основные зависимости:

  • Firebase. Платформа, которая предоставляет ряд полезных сервисов для приложений iOS, Android и веб-приложений. В этом примере база данных реального времени используется для хранения и синхронизации информации о каждом котенке в реальном времени.
  • Moment.js – библиотека, которая упрощает работу с датами в JavaScript. Дата рождения каждого котенка хранится в базе данных Firebase, а moment используется для расчета его возраста в неделях.

Как всего две зависимости могут увеличить размер пакета почти на 1 МБ? Что ж, одна из причин заключается в том, что любая зависимость, в свою очередь, может иметь свои собственные зависимости, поэтому их гораздо больше, чем просто две, если учитывать каждую глубину/ветвь "дерева" зависимостей. Приложение может быстро стать слишком большим, если в него включено много зависимостей.

Проанализируйте сборщик, чтобы лучше понять, что происходит. Для этого можно использовать различные инструменты, созданные сообществом, например webpack-bundle-analyzer.

Пакет для этого инструмента уже включен в приложение как devDependency.

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

Это означает, что его можно использовать непосредственно в файле конфигурации webpack. Импортируйте его в самом начале файла webpack.config.js:

const path = require("path");

//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

Теперь добавьте его в качестве плагина в самом конце файла в массиве plugins:

module.exports = {
  //...
  plugins: [
    //...
    new BundleAnalyzerPlugin()
  ]
};

После перезагрузки приложения вы увидите визуализацию всего пакета, а не само приложение.

Webpack Bundle Analyzer

Не так мило, как котики 🐱, но невероятно полезно. При наведении указателя мыши на любой из пакетов отображается его размер, представленный тремя разными способами:

Размер статистики Размер до минификации или сжатия.
Размер после обработки Размер пакета в наборе после компиляции. Версия 4 webpack (которая используется в этом приложении) автоматически минимизирует скомпилированные файлы, поэтому этот размер меньше, чем размер stat.
Размер в формате GZIP Размер пакета после сжатия с помощью кодировки gzip. Эта тема рассматривается в отдельном руководстве.

С помощью инструмента webpack-bundle-analyzer можно легко определить неиспользуемые или ненужные пакеты, которые составляют большой процент пакета.

Удаление неиспользуемых пакетов

На визуализации видно, что пакет firebase состоит из гораздо большего, чем просто база данных. В него входят дополнительные пакеты, такие как:

  • firestore
  • auth
  • storage
  • messaging
  • functions

Все это отличные сервисы, предоставляемые Firebase (подробнее о них можно узнать из документации), но ни один из них не используется в приложении, поэтому нет причин импортировать их все.

Чтобы приложение снова появилось, отмените изменения в webpack.config.js:

  • Удалите BundleAnalyzerPlugin из списка плагинов:
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • Теперь удалите неиспользуемый импорт в верхней части файла:
const path = require("path");

//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

Теперь приложение должно загружаться нормально. Измените src/index.js, чтобы обновить импорт из Firebase.

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

Теперь при перезагрузке приложения предупреждение DevTools не появляется. Если открыть панель Сеть в Инструментах разработчика, то можно увидеть, что размер пакета значительно уменьшился.

Размер пакета уменьшен до 480 КБ.

Удалено больше половины размера пакета. Firebase предоставляет множество различных сервисов и дает разработчикам возможность включать только те, которые действительно необходимы. В этом приложении для хранения и синхронизации всех данных использовался только firebase/database. Импорт firebase/app, который настраивает поверхность API для каждого из сервисов, требуется всегда.

Многие другие популярные библиотеки, например lodash, также позволяют разработчикам выборочно импортировать разные части пакетов. Не прилагая особых усилий, обновление импорта библиотек в приложении, чтобы включить только то, что используется может привести к значительному повышению производительности.

Хотя размер пакета значительно уменьшился, предстоит ещё много работы. 😈

Удаление ненужных пакетов

В отличие от Firebase, импортировать части библиотеки moment не так просто, но, возможно, ее можно удалить полностью?

Дата рождения каждого котенка хранится в базе данных Firebase в формате Unix (в миллисекундах).

Даты рождения, сохраненные в формате Unix

Это временная метка, обозначающая определенную дату и время. Она представляет собой количество миллисекунд, прошедших с 1 января 1970 г. 00:00 UTC. Если текущую дату и время можно рассчитать в том же формате, то, вероятно, можно создать небольшую функцию для определения возраста каждого котенка в неделях.

Как всегда, старайтесь не копировать и не вставлять, когда вы следуете за мной. Для начала удалите moment из импортированных файлов в src/index.js.

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

Прослушиватель событий Firebase обрабатывает изменения значений в нашей базе данных:

favoritesRef.on("value", (snapshot) => { ... })

Добавьте небольшую функцию, которая будет рассчитывать количество недель, прошедших с определенной даты:

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

В этой функции разница в миллисекундах между текущей датой и временем (new Date).getTime() и датой рождения (аргумент birthDate, уже в миллисекундах) вычисляется и делится на количество миллисекунд в одной неделе.

Наконец, все экземпляры moment можно удалить в прослушивателе событий, используя вместо этого следующую функцию:

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

Перезагрузите приложение и снова откройте панель Сеть.

Размер пакета уменьшен до 225 КБ.

Размер нашего пакета снова уменьшился более чем наполовину!

Заключение

После выполнения этой практической работы вы должны хорошо понимать, как анализировать определенный пакет и почему может быть полезно удалять неиспользуемые или ненужные пакеты. Прежде чем начать оптимизацию приложения с помощью этого метода, важно знать, что в больших приложениях это может быть значительно сложнее.

Чтобы удалить неиспользуемые библиотеки, выясните, какие части пакета используются, а какие нет. Если вы видите подозрительный пакет, который, похоже, нигде не используется, сделайте шаг назад и проверьте, каким зависимостям верхнего уровня он может понадобиться. Попробуйте найти способ отделить их друг от друга.

Удалить ненужные библиотеки может быть сложнее. Важно тесно сотрудничать со своей командой и посмотреть, есть ли возможность упростить части базы кода. Удаление moment в этом приложении может показаться правильным решением в любой ситуации, но что если нужно будет обрабатывать часовые пояса и разные региональные настройки? Или что, если бы были более сложные манипуляции с датами? При обработке и синтаксическом анализе дат и времени могут возникнуть сложности, а библиотеки moment и date-fns значительно упрощают эти процессы.

Всегда приходится идти на компромисс, и важно оценить, стоит ли вообще тратить силы на создание собственного решения вместо того, чтобы использовать стороннюю библиотеку.