Wróć do strony głównej

Angular Tree Shaking

Czym jest tree shaking?

Jednym z większych problemów Angulara jest wielkość zbudowanej aplikacji. W dzisiejszych czasach rozmiar naszej aplikacji oznacza ilość JavaScript jaką użytkownik musi pobrać na swoje urządzenie. Team odpowiedzialny za rozwój Angulara otrzymał cel związany z optymalizacją rozmiaru bundle.
Cake Threshold. Nazwa była bardzo wymowna, ponieważ całemu teamowi obiecano ciasta jeżeli uda im się zejść poniżej 10 Kb dla aplikacji typu ‘Hello, World”. 

source: https://twitter.com/kuncevic/status/1050165141625483264

Jedną z optymalizacji którą wykorzystali przy okazji nowego silnika renderującego Ivy było wprowadzenie techniki tree shaking. Tree shaking nazywany też eliminacją martwego kodu jest to proces podczas którego usuwany jest nieużywany kod z naszego builda. Technika ta pozwala na zmniejszenie finalnej wielkości naszej aplikacji.

Tree shaking w Angular Ivy

Wprowadzenie tree shaking do nowego silnika renderującego Ivy było dużym krokiem w stronę optymalizacji aplikacji. Odpowiedzmy sobie teraz na pytanie w jaki sposób Angular rozpoznaje fragmenty kodu których nie potrzebuje. Na początek musimy zrozumieć czym jest incremental DOM na którym bazuje silnik renderujący Ivy. 

Team Google zdecydował się wprowadzić Incremental DOM aby osiągnąć dwa cele:

  • Mniejszy rozmiar bundle
  • Zmniejszyć zapotrzebowanie na pamięć RAM silnika renderującego

Incremental DOM polega na przekompilowaniu każdego komponentu w zestaw instrukcji. Instrukcje pozwalają na utworzenie drzewa DOM, a następnie uaktualnianie go w miejscach gdzie nastąpiła mutacja danych. Takie podejście pozwala nam na pozbycie się interpretera Angulara z finalnego bundle. Dodatkowo, sposób w jaki wykorzystywane są instrukcje do uaktualniania DOM potrzebują mniej pamięci w stosunku do Virtual DOM wykorzystywanych między innymi przez React, który generuje pełne nowe wersje drzew DOM.

Co sprawia, że Incremental DOM pozwala na wykorzystanie Tree Shaking? 

Dzięki wprowadzeniu instrukcji, podczas kompilacji możemy sprawdzić czy występuje referencja z naszego komponentu do konkretnej instrukcji. W przypadku braku referencji jesteśmy w stanie przeprowadzić Tree Shake. Dla porównania, Virtual DOM opiera się na interpreterze, który nie jest w stanie sprawdzić, czy dany fragment kodu zostanie wykorzystany w aplikacji, czy też nie.

Znając działanie techniki Tree Shaking zobaczmy ją w akcji.

Na początek utworzymy prostą aplikację Angularową z jednym komponentem, w którym wykorzystamy interpolację oraz date pipe:

Następnie wykorzystamy Angular Compiler CLI do wygenerowania plików Js które będą zawierać nasz komponent w formie instrukcji. Po wykonaniu komendy ngc -p tsconfig.json komponent wygląda teraz tak:

 

Skupmy się na funkcji AppComponent_Template. Pierwszy argument funkcji rf oznacza renderFlags, tutaj możemy wyróżnić dwa tryby 1 dla RenderFlags.Create oraz 2 dla RenderFlags.Update. Przy tworzeniu komponentu przechodzimy od linijki 18 do 24 dopisując nasze elementy do tablicy nazywanej Logical View (LView), w której trzymamy elementy DOM, wartości bindowane oraz instancje dyrektyw.
Taka tablica jest tworzona per komponent, a następnie jest wykorzystywana przy Change Detection. Kiedy przechodzimy do trybu update, instrukcje advance pozwalają nam odnaleźć pozycję aktualizowanego elementu w tablicy LView. Wartości są zapisywane w cache, a process change detection polega na porównaniu nowej wartości z obecną. Jeżeli chcesz dokładniej zgłębić temat polecam ten filmik: https://www.youtube.com/watch?v=S0o-4yc2n-8

Teraz skupmy się na tym co wykorzystujemy w naszej aplikacji, czyli Interpolacji oraz Pipe. W kodzie możemy zaobserwować funkcje ɵɵtextInterpolate1 oraz ɵɵpipeBind1. Zbudujmy naszą aplikację do wersji produkcyjnej i spróbujmy odnaleźć te instrukcje. Aby nasz kod pozostał czytelny zbudujemy naszą aplikację za pomocą komendy 

env NG_BUILD_MANGLE=false NG_BUILD_MINIFY=false NG_BUILD_BEAUTIFY=true ng build –prod

I faktycznie jesteśmy w stanie znaleźć takie deklaracje: 

Oraz

Teraz zobaczmy czy po wyeliminowaniu data Pipe pozbędziemy się również ɵɵpipeBind1 z naszego zbudowanego kodu. Rzeczywiście po usunięciu daty z naszej aplikacji, nie mamy już nigdzie odwołania do Pipe. Dlatego też nie znajdziemy tego kodu w naszym finalnym bundle:

Dodatkowo porównując wielkości naszych buildów produkcyjnych otrzymaliśmy następujące wyniki (warto podkreślić, że aplikacje były zbudowane w sposób który lepiej obrazuję różnicę w ich rozmiarze):

Z Date pipe: 172,4 kB
Bez Date pipe: 160,6 kB

Dla porównania Angular 6: 258,2 kB

Jak możemy zauważyć, kod został wyeliminowany przez Tree Shaking. 🙂

Podsumowywując, nowy silnik renderujący wprowadził Incremental DOM który opiera się na instrukcjach. Wprowadzenie instrukcji umożliwia nam prześledzenie całego projektu, zliczając referencje do konkretnych fragmentów API Angulara oraz naszego kodu. Po zakończeniu tego procesu wiemy dokładnie które fragmenty nie są wykorzystywane w aplikacji, a więc możemy przeprowadzić Tree Shaking na tych fragmentach kodu.  

Bonus

Jako dodatkową ciekawostkę odsyłam do artykułu który opisuje jak przeprowadzić symulowanie Tree-shakable komponentów za pomocą Single Component Angular Modules (SCAMs): https://dev.to/this-is-angular/emulating-tree-shakable-components-using-single-component-angular-modules-13do

 

O autorze

Artur Haczek

Ambitious and motivated developer interested in new technologies. It is important for him that his code is of the highest quality. Living in a complicated relationship with CSS.

Chcesz razem z nami tworzyć treści na bloga? Dołącz do nas i twórz wartościowe treści dla sympatyków Angulara z Angular.love!

0 komentarzy

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *