Usuwanie nieużywanego kodu

W tym ćwiczeniu z programowania poprawisz wydajność aplikacji, usuwając nieużywane i niepotrzebne zależności.

Zrzut ekranu aplikacji

Zmierz odległość

Zawsze warto najpierw zmierzyć skuteczność witryny, zanim dodasz optymalizacje.

  • Aby wyświetlić podgląd witryny, naciśnij Wyświetl aplikację, a następnie Pełny ekran pełny ekran.

Kliknij swojego ulubionego kotka. W tej aplikacji używana jest Baza danych czasu rzeczywistego Firebase, dlatego wynik jest aktualizowany w czasie rzeczywistym i synchronizowany z każdą inną osobą korzystającą z aplikacji. 🐈

  1. Naciśnij `Control+Shift+J` (lub `Command+Option+J` na Macu), aby otworzyć Narzędzia deweloperskie.
  2. Kliknij kartę Sieć.
  3. Zaznacz pole wyboru Wyłącz pamięć podręczną.
  4. Ponownie uruchom aplikację.

Pierwotny rozmiar pakietu: 992 KB

Do wczytania tej prostej aplikacji jest przesyłany prawie 1 MB kodu JavaScript.

Zapoznaj się z ostrzeżeniami dotyczącymi projektu w Narzędziach deweloperskich.

  • Kliknij kartę Konsola.
  • Upewnij się, że opcja Warnings jest włączona w menu poziomów obok pola Filter.

Filtr ostrzeżeń

  • Zapoznaj się z wyświetlonym ostrzeżeniem.

Ostrzeżenie w konsoli

Firebase, jedna z bibliotek używanych w tej aplikacji, ostrzega deweloperów, aby nie importowali całego pakietu, ale tylko te komponenty, które są używane. Innymi słowy, w tej aplikacji są nieużywane biblioteki, które można usunąć, aby przyspieszyć jej wczytywanie.

Zdarzają się też przypadki, gdy używana jest konkretna biblioteka, ale może istnieć prostsza alternatywa. Koncepcję usuwania niepotrzebnych bibliotek omówimy w dalszej części tego samouczka.

Analizowanie pakietu

W aplikacji występują 2 główne zależności:

  • Firebase: platforma, która udostępnia szereg przydatnych usług dla aplikacji na iOS, Androida i aplikacji internetowych. W tym przypadku baza danych czasu rzeczywistego służy do przechowywania i synchronizowania informacji o każdym kociaku w czasie rzeczywistym.
  • Moment.js: biblioteka narzędziowa, która ułatwia obsługę dat w języku JavaScript. Data urodzenia każdego kociaka jest przechowywana w bazie danych Firebase, a moment służy do obliczania jego wieku w tygodniach.

Jak to możliwe, że tylko 2 zależności mogą zwiększyć rozmiar pakietu do prawie 1 MB? Jednym z powodów jest to, że każda zależność może mieć własne zależności, więc jest ich znacznie więcej niż tylko 2, jeśli weźmiemy pod uwagę każdą głębokość/gałąź „drzewa” zależności. Aplikacja może łatwo i szybko stać się duża, jeśli zawiera wiele zależności.

Przeanalizuj bundler, aby lepiej zrozumieć, co się dzieje. Istnieje wiele różnych narzędzi stworzonych przez społeczność, które mogą w tym pomóc, np. webpack-bundle-analyzer.

Pakiet tego narzędzia jest już uwzględniony w aplikacji jako devDependency.

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

Oznacza to, że można go używać bezpośrednio w pliku konfiguracji webpack. Zaimportuj go na samym początku pliku webpack.config.js:

const path = require("path");

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

Teraz dodaj go jako wtyczkę na samym końcu pliku w tablicy plugins:

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

Po ponownym załadowaniu aplikacji powinna pojawić się wizualizacja całego pakietu, a nie samej aplikacji.

Webpack Bundle Analyzer

Może nie jest to tak urocze jak oglądanie kociąt 🐱, ale na pewno bardzo przydatne. Po najechaniu kursorem na dowolny pakiet zobaczysz jego rozmiar przedstawiony na 3 różne sposoby:

Stat size Rozmiar przed minifikacją lub kompresją.
Przeanalizowany rozmiar Rozmiar rzeczywistego pakietu w pakiecie po skompilowaniu. Wersja 4 webpacka (która jest używana w tej aplikacji) automatycznie minifikuje skompilowane pliki, dlatego ten rozmiar jest mniejszy niż rozmiar statyczny.
Rozmiar po skompresowaniu gzip Rozmiar pakietu po skompresowaniu za pomocą kodowania gzip. Ten temat został omówiony w osobnym przewodniku.

Za pomocą narzędzia webpack-bundle-analyzer łatwiej jest zidentyfikować nieużywane lub niepotrzebne pakiety, które stanowią duży procent pakietu.

Usuwanie nieużywanych pakietów

Wizualizacja pokazuje, że pakiet firebase zawiera znacznie więcej niż tylko bazę danych. Obejmuje dodatkowe pakiety, takie jak:

  • firestore
  • auth
  • storage
  • messaging
  • functions

Są to świetne usługi Firebase (więcej informacji znajdziesz w dokumentacji), ale żadna z nich nie jest używana w aplikacji, więc nie ma powodu, aby je wszystkie importować.

Cofnij zmiany w aplikacji webpack.config.js, aby znów ją zobaczyć:

  • Usuń BundleAnalyzerPlugin z listy wtyczek:
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • Teraz usuń nieużywany import z góry pliku:
const path = require("path");

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

Aplikacja powinna się teraz normalnie wczytać. Zmodyfikuj src/index.js, aby zaktualizować importy Firebase.

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

Teraz po ponownym załadowaniu aplikacji ostrzeżenie w Narzędziach deweloperskich nie będzie się wyświetlać. Otwarcie panelu Sieć w Narzędziach deweloperskich również pokazuje znaczną redukcję rozmiaru pakietu:

Rozmiar pakietu został zmniejszony do 480 KB

Usunięto ponad połowę rozmiaru pakietu. Firebase oferuje wiele różnych usług i daje deweloperom możliwość uwzględnienia tylko tych, które są im rzeczywiście potrzebne. W tej aplikacji do przechowywania i synchronizowania wszystkich danych użyto tylko firebase/database. Import firebase/app, który konfiguruje interfejs API dla każdej z różnych usług, jest zawsze wymagany.

Wiele innych popularnych bibliotek, takich jak lodash, również umożliwia deweloperom selektywne importowanie różnych części pakietów. Bez większego wysiłku, aktualizując importy bibliotek w aplikacji, aby zawierały tylko to, co jest używane, można znacznie poprawić wydajność.

Chociaż rozmiar pakietu został znacznie zmniejszony, nadal jest jeszcze sporo do zrobienia. 😈

Usuwam niepotrzebne pakiety

W przeciwieństwie do Firebase importowanie części biblioteki moment nie jest takie proste, ale może da się ją całkowicie usunąć?

Data urodzenia każdego uroczego kociaka jest przechowywana w bazie danych Firebase w formacie Unix (milisekundy).

Daty urodzenia zapisane w formacie Unix

Jest to sygnatura czasowa konkretnej daty i godziny reprezentowana przez liczbę milisekund, które upłynęły od 1 stycznia 1970 r. o godzinie 00:00 czasu UTC. Jeśli bieżącą datę i godzinę można obliczyć w tym samym formacie, prawdopodobnie można utworzyć niewielką funkcję, która pozwoli określić wiek każdego kociaka w tygodniach.

Jak zawsze, staraj się nie kopiować i nie wklejać kodu, gdy wykonujesz instrukcje. Zacznij od usunięcia moment z importów w src/index.js.

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

Detektor zdarzeń Firebase obsługuje zmiany wartości w naszej bazie danych:

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

Powyżej dodaj małą funkcję, która oblicza liczbę tygodni od danej daty:

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);
}

W tej funkcji różnica w milisekundach między bieżącą datą i godziną (new Date).getTime() a datą urodzenia (argument birthDate, już w milisekundach) jest obliczana i dzielona przez liczbę milisekund w jednym tygodniu.

Na koniec wszystkie wystąpienia moment można usunąć w detektorze zdarzeń, korzystając z tej funkcji:

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>
    `})
});

Teraz ponownie załaduj aplikację i jeszcze raz spójrz na panel Sieć.

Rozmiar pakietu został zmniejszony do 225 KB

Rozmiar naszego pakietu został zmniejszony o ponad połowę.

Podsumowanie

Dzięki temu ćwiczeniu wiesz już, jak analizować konkretny pakiet i dlaczego usuwanie nieużywanych lub niepotrzebnych pakietów może być tak przydatne. Zanim zaczniesz optymalizować aplikację za pomocą tej techniki, pamiętaj, że w przypadku większych aplikacji może to być znacznie bardziej skomplikowane.

Jeśli chodzi o usuwanie nieużywanych bibliotek, spróbuj dowiedzieć się, które części pakietu są używane, a które nie. Jeśli widzisz tajemniczy pakiet, który nie wydaje się być nigdzie używany, cofnij się o krok i sprawdź, które zależności najwyższego poziomu mogą go potrzebować. Spróbuj znaleźć sposób na ich rozdzielenie.

Usuwanie niepotrzebnych bibliotek może być nieco bardziej skomplikowane. Warto ściśle współpracować z zespołem i sprawdzić, czy można uprościć niektóre części bazy kodu. Usunięcie moment w tej aplikacji może wydawać się właściwym rozwiązaniem, ale co, jeśli trzeba będzie obsłużyć strefy czasowe i różne ustawienia regionalne? A co, jeśli trzeba wykonać bardziej skomplikowane operacje na datach? Przetwarzanie i analizowanie dat i godzin może być bardzo skomplikowane, a biblioteki takie jak moment i date-fns znacznie to upraszczają.

Wszystko ma swoje plusy i minusy, dlatego warto ocenić, czy złożoność i wysiłek związany z wdrożeniem niestandardowego rozwiązania zamiast korzystania z biblioteki innej firmy są w ogóle tego warte.