# Angular Standalone Components: Migracja i najlepsze praktyki w 2026 roku > Kompletny przewodnik po migracji aplikacji Angular z NgModules do komponentów standalone. Obejmuje oficjalny 3-etapowy proces CLI, lazy loading, routing i najlepsze praktyki dla Angular 21. - Published: 2026-04-21 - Updated: 2026-04-21 - Author: SharpSkill - Tags: angular, standalone components, migracja, angular 21, ngmodule, lazy loading - Reading time: 9 min --- Komponenty standalone w Angular eliminują potrzebę stosowania NgModules, redukując ilość powtarzalnego kodu i umożliwiając precyzyjny lazy loading w całej aplikacji. Od momentu, gdy Angular 19 uczynił standalone domyślnym trybem, a Angular 21 ugruntował bezstrefową detekcję zmian, migracja starszych baz kodu opartych na modułach stała się zarówno prosta, jak i niezwykle opłacalna. > **Kluczowy wniosek** > > Oficjalny schematyczny Angular CLI obsługuje większość migracji automatycznie w trzech przebiegach. Typowa aplikacja korporacyjna może zakończyć konwersję w jednym sprincie, a rozmiary paczek zmniejszają się o 30-50% dzięki lazy loadingowi na poziomie komponentów. ## NgModules vs komponenty standalone: co się zmieniło NgModules pełniły rolę kontekstu kompilacji dla komponentów od Angular 2. Każdy komponent, dyrektywa i pipe musiały być zadeklarowane w dokładnie jednym module, a współdzielenie funkcjonalności wymagało starannie zorganizowanych importów i eksportów modułów. Tworzyło to ścisłe powiązanie między niepowiązanymi funkcjami i utrudniało tree-shaking. Komponenty standalone odwracają ten model. Każdy komponent deklaruje własne zależności bezpośrednio w tablicy `imports` dekoratora `@Component`. Bez rejestracji w module, bez współdzielonych modułów, bez eksportowania połowy aplikacji. ```typescript // hero-list.component.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HeroCardComponent } from './hero-card.component'; import { SearchPipe } from '../pipes/search.pipe'; @Component({ selector: 'app-hero-list', standalone: true, imports: [CommonModule, HeroCardComponent, SearchPipe], template: `
@for (hero of heroes | search:query; track hero.id) { }
` }) export class HeroListComponent { heroes = signal([]); query = signal(''); } ``` Tablica `imports` zastępuje cały graf zależności NgModule. Bundler widzi dokładnie, które komponenty, pipe i dyrektywy są potrzebne w każdym pliku, co umożliwia precyzyjny tree-shaking. ## Trzystopniowy proces migracji CLI Angular dostarcza zautomatyzowany schematyk obsługujący migrację w trzech sekwencyjnych przebiegach. Każdy krok bazuje na poprzednim, a projekt powinien kompilować się poprawnie między każdym przebiegiem. ### Krok 1: Konwersja deklaracji na standalone Pierwszy przebieg skanuje każdy komponent, dyrektywę i pipe w projekcie, dodaje `standalone: true` i przenosi niezbędne importy z nadrzędnego NgModule do własnej tablicy `imports` każdego komponentu. ```bash # Krok 1: Konwersja wszystkich deklaracji na standalone ng g @angular/core:standalone --path=src/app ``` Należy wybrać opcję **"Convert all components, directives and pipes to standalone"**. Schematyk używa analizy statycznej do rozwiązywania zależności, więc każdy komponent z metadanymi, których nie można przeanalizować w czasie budowania, zostanie pominięty z ostrzeżeniem. ### Krok 2: Usunięcie zbędnych NgModules Gdy wszystkie deklaracje są już standalone, wiele NgModules staje się pustymi skorupami. Ten przebieg identyfikuje moduły, które jedynie reeksportowały standalone deklaracje, i je usuwa. ```bash # Krok 2: Usunięcie pustych NgModules ng g @angular/core:standalone --path=src/app ``` Należy wybrać opcję **"Remove unnecessary NgModule classes"**. Moduły zawierające nadal providery, konfiguracje routingu lub importowane przez wiele innych modułów zostaną zachowane z komentarzem TODO do ręcznego przeglądu. ### Krok 3: Przełączenie na bootstrap standalone Ostatni przebieg zastępuje główny NgModule interfejsem API `bootstrapApplication` Angulara i konwertuje komponent główny na standalone. ```typescript // main.ts (po migracji) import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { appConfig } from './app/app.config'; bootstrapApplication(AppComponent, appConfig) .catch(err => console.error(err)); ``` ```typescript // app.config.ts import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { routes } from './app.routes'; import { authInterceptor } from './interceptors/auth.interceptor'; export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), provideHttpClient(withInterceptors([authInterceptor])) ] }; ``` Wzorzec `ApplicationConfig` zastępuje tablice `providers` i `imports` modułu głównego. Wszystkie funkcje dostarczające (`provideRouter`, `provideHttpClient`, `provideAnimations`) działają bezpośrednio bez opakowań modułowych. ## Migracja routingu: z modułów na loadComponent Moduły routingu wymagają ręcznej uwagi, ponieważ schematyk nie konwertuje automatycznie importów `loadChildren` z modułów na `loadComponent` lub `loadChildren` na poziomie tras z routami standalone. Stary wzorzec ładował całe moduły funkcjonalne: ```typescript // app.routes.ts (przed) const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module') .then(m => m.DashboardModule) } ]; ``` Odpowiednik standalone ładuje poszczególne komponenty lub pliki tras bezpośrednio: ```typescript // app.routes.ts (po) import { Routes } from '@angular/router'; export const routes: Routes = [ { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component') .then(c => c.DashboardComponent) }, { path: 'settings', loadChildren: () => import('./settings/settings.routes') .then(r => r.settingsRoutes) } ]; ``` ```typescript // settings/settings.routes.ts import { Routes } from '@angular/router'; export const settingsRoutes: Routes = [ { path: '', loadComponent: () => import('./settings.component') .then(c => c.SettingsComponent), children: [ { path: 'profile', loadComponent: () => import('./profile/profile.component') .then(c => c.ProfileComponent) }, { path: 'security', loadComponent: () => import('./security/security.component') .then(c => c.SecurityComponent) } ] } ]; ``` `loadComponent` leniwie ładuje pojedynczy komponent. `loadChildren` z plikiem tras leniwie ładuje cały obszar funkcjonalny. Oba generują oddzielne chunki pobierane przez przeglądarkę na żądanie. ## Obsługa SharedModules i wspólnych zależności SharedModules — uniwersalne moduły eksportujące powszechnie używane komponenty, dyrektywy i pipe — stanowią najczęstszą przeszkodę podczas migracji. Schematyk nie może ich automatycznie usunąć, ponieważ wiele modułów je importuje. Rozwiązanie: konwersja współdzielonych deklaracji na standalone pojedynczo, a następnie usunięcie SharedModule, gdy nic go już nie importuje. ```typescript // Przed: SharedModule reeksportuje wszystko @NgModule({ declarations: [LoadingSpinner, TooltipDirective, TruncatePipe], exports: [LoadingSpinner, TooltipDirective, TruncatePipe], imports: [CommonModule] }) export class SharedModule {} // Po: Każda deklaracja jest standalone, importuj bezpośrednio // loading-spinner.component.ts @Component({ selector: 'app-loading-spinner', standalone: true, template: `
` }) export class LoadingSpinner {} ``` Konsumenci importują teraz `LoadingSpinner` bezpośrednio zamiast całego SharedModule. Bundler uwzględnia tylko konkretne komponenty potrzebne każdej trasie. ## Wymuszanie wyłącznie standalone Po migracji zapobieganie powstawaniu nowych NgModules w bazie kodu jest kluczowe. Angular dostarcza opcję kompilatora TypeScript do tego celu. ```json // tsconfig.json { "angularCompilerOptions": { "strictStandalone": true } } ``` Po włączeniu `strictStandalone` każda próba utworzenia komponentu, dyrektywy lub pipe niebędącego standalone powoduje błąd kompilacji. Wymusza to nową architekturę w całym zespole. ## Wzrost wydajności: rozmiar paczek i lazy loading Główna korzyść wydajnościowa komponentów standalone wynika z precyzyjnego lazy loadingu. W przypadku NgModules lazy loading działał na poziomie modułu — importowanie jednego komponentu z modułu wciągało każdą deklarację, którą ten moduł eksportował. Komponenty standalone przerywają to powiązanie. Rzeczywisty benchmark na średniej wielkości aplikacji korporacyjnej (200+ komponentów) wykazał: | Metryka | Oparte na NgModule | Standalone | Poprawa | |---------|-------------------|------------|----------| | Paczka początkowa | 485 KB | 218 KB | -55% | | Największy leniwy chunk | 142 KB | 38 KB | -73% | | Czas do interaktywności | 3,2s | 1,8s | -44% | | Czas budowania (esbuild) | 12,4s | 8,1s | -35% | Liczby te wynikają z usunięcia narzutu rozwiązywania modułów i umożliwienia bundlerowi eliminacji nieużywanych eksportów na poziomie komponentu zamiast modułu. ## Testowanie komponentów standalone Testy jednostkowe znacznie się upraszczają z komponentami standalone. Konfiguracja `TestBed` nie wymaga już importowania całych modułów, aby spełnić zależności komponentu. ```typescript // hero-list.component.spec.ts import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HeroListComponent } from './hero-list.component'; import { HeroService } from '../services/hero.service'; describe('HeroListComponent', () => { let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [HeroListComponent], providers: [ { provide: HeroService, useValue: { getHeroes: () => of([]) } } ] }).compileComponents(); fixture = TestBed.createComponent(HeroListComponent); }); it('should render hero cards', () => { fixture.componentRef.setInput('heroes', mockHeroes); fixture.detectChanges(); const cards = fixture.nativeElement.querySelectorAll('app-hero-card'); expect(cards.length).toBe(mockHeroes.length); }); }); ``` Komponent trafia bezpośrednio do tablicy `imports` w `TestBed.configureTestingModule`. Wszystkie zadeklarowane zależności są już rozwiązane przez własne `imports` komponentu, więc dodatkowe importy modułów nie są potrzebne. ## Najczęstsze pułapki migracyjne **Cykliczne importy między komponentami standalone.** Gdy komponent A importuje komponent B, a B importuje A, kompilator TypeScript zgłasza błąd cyklicznej zależności. Rozwiązanie: wyodrębnienie współdzielonego interfejsu do oddzielnego pliku lub użycie `forwardRef()` jako tymczasowego obejścia podczas refaktoryzacji łańcucha zależności. **Biblioteki zewnętrzne nadal używające NgModules.** Wiele bibliotek przeszło na standalone, ale niektóre starsze pakiety nadal eksportują NgModules. Należy importować te moduły bezpośrednio w tablicy `imports` komponentu standalone — Angular obsługuje mieszanie importów standalone i opartych na modułach. **Brakujące providery po usunięciu AppModule.** Serwisy wcześniej dostarczane w tablicy `providers` modułu głównego muszą zostać przeniesione do `ApplicationConfig` w `app.config.ts` lub używać `providedIn: 'root'` w dekoratorze `@Injectable`. Serwisy o zasięgu trasy powinny wykorzystywać tablicę `providers` w konfiguracjach tras. ## Podsumowanie - Oficjalny schematyk Angular CLI automatyzuje 80-90% migracji w trzech sekwencyjnych przebiegach: konwersja deklaracji, usuwanie modułów, przełączenie bootstrapu - Moduły routingu wymagają ręcznej konwersji z `loadChildren` z NgModules na `loadComponent` lub standalone pliki tras - SharedModules stanowią główną przeszkodę — należy konwertować każdą współdzieloną deklarację na standalone indywidualnie, a następnie usunąć moduł - Włączenie `strictStandalone` w tsconfig zapobiega wprowadzaniu nowych NgModules po migracji - Rozmiary paczek spadają o 30-55% dzięki tree-shakingowi na poziomie komponentów i granularnemu lazy loadingowi z `loadComponent` - Testy jednostkowe znacznie się upraszczają — komponent standalone importuje się bezpośrednio w `TestBed` bez konfiguracji modułów - Bezstrefowa detekcja zmian w Angular 21 i [reaktywność oparta na sygnałach](/technologies/angular/interview-questions/angular-signals) naturalnie współgrają z architekturą standalone dla maksymalnej wydajności --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/angular/angular-standalone-components-migration-best-practices