Eliminacja martwego kodu w Angularze
Zdarzają się przypadki, kiedy chcemy uruchomić kod jedynie w trybie produkcyjnym, lub developerskim. Może chodzić o jakąś drobnostkę (np. logowanie danych na konsoli), ale możemy też chcieć ładować całą bibliotekę, a to już znacznie większa operacja. Pokażę Wam tutaj, jak sprawić, aby do końcowego pakietu nie trafił niepożądany kod. Zacznijmy od rozwiązania, które zna każdy:
import { environment } from './environments/environment';
if (environment.production) {
console.log('PROD MODE');
} else {
console.log('DEV MODE');
}
Możemy wykorzystać pliki środowiskowe - Angular CLI załaduje odpowiedni plik w zależności od konfiguracji. Usunie on też niechciany kod kiedy przenosimy projekt na produkcję. Więcej o tej funkcji możecie przeczytać tutaj.
Rozwiązanie to będzie działało w większości przypadków, ale co z zewnętrznymi bibliotekami, które nie mogą korzystać ze zmiennej środowiskowej? Jak mogą pozbyć się kodu, którego należy używać jedynie w trybie developerskim?
Jeśli spojrzymy na kod źródłowy Angulara CLI, to zobaczymy, że używa on Terser, aby zmniejszyć nasz kod. Jedną z funkcji Terser jest conditional compilation API. Pozwala ona na stworzenie globalnej stałej, którą można konfigurować w czasie kompilacji. Na przykład:
await minify(`if(DEBUG) { console.log('DEV') }`, {
compress: {
dead_code: true,
global_defs: {
DEBUG: false
}
}
}).code;
// returns: ''
W powyższym przykładzie Terser najpierw zastępuje każde wystąpienie zmiennej DEBUG
wartością false. Następnie, ponieważ włączyliśmy opcję dead_code
, Terser zdaje sobie sprawę, że linijka if (false) {…}
jest bezużyteczna, więc ją usuwa:
Przykład
Jeśli teraz spojrzymy na kod źródłowy Angulara CLI, to zobaczymy, że zmienna ngDevMode jest teraz stałą globalną w Terser global_defs. Oznacza to, że możemy jej używać zarówno w naszej aplikacji, jak i w bibliotekach zewnętrznych:
declare const ngDevMode: boolean;
@Component({
selector: 'lib-my-lib',
template: ` <p>my-lib works!</p> `
})
export class MyLibComponent {
ngOnInit() {
if (ngDevMode) {
console.log('DEV MODE');
}
}
}
Kiedy konsumujemy komponent w naszej aplikacji i przenosimy go na produkcję, to Terser usunie kod wewnątrz instrukcji if
z naszego pakietu. Pamiętaj, że jeśli chcesz obsługiwać kompilację po stronie serwera, to musisz dodać jeszcze jeden check:
declare const ngDevMode: boolean;
@Component({
selector: 'lib-my-lib',
template: ` <p>my-lib works!</p> `
})
export class MyLibComponent {
ngOnInit() {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
console.log('DEV MODE');
}
}
}
Moje projekty
Oto kilka z moich projektów open source:
- Akita:zarządzanie stanem przeznaczone dla aplikacji JS
- Spectator: narzędzie do uproszczenia testów Angulara
- Transcolo: biblioteka Angulara do internacjonalizacji
- Error-Tailor- do walki z błędami w apkach Angulara
- Forms Manager:fundament pod właściwe zarządzanie formami w Angularze
- Cashew:elastyczna biblioteka, która wyłapuje żądania HTTP
I wiele więcej!
Oryginał tekstu w języku angielskim możesz przeczytać tutaj.