Merhaba,


Bir Angular projesi geliştiriyoruz diyelim. Projeyi oluşturduğumuzda src/app altında app.module.ts dosyası vardır. Her component'i her servisi bu module içerisinde import ediyoruz. Projemize yeni sayfalar, özellikler ekledikçe app.module içerisinde yüzlerce, hatta binlerce satır kod olacak. Peki bu kadar çok satır kodu eklememiz veya tüm bileşenlerimizin aynı modül içinde olması projemizin açılış hızını etkiler mi?


Tabiki etkiler. Şöyle ki;

Yaptığımız projenin kullanıcıların göreceği sayfalar var birde giriş yaptıktan sonra yönetim panelimiz var diyelim. Yani aslında 2 ayrı proje gibi ama bir çatı altında. Biz bu sayfalar için oluşturduğumuz her component'i, hatta her pipe'ı aynı modül içinde çağırıyoruz. Bu işin sonunda kullanıcı site anasayfasını açıyor ama bütün componentler yükleniyor. Bir başka deyişle; kullanıcı anasayfayı açıyor ama ürünler sayfası, ürün detayı sayfası, sepet ekranı, sipariş tamamlama sayfası, kayıt olma, giriş yapma, şifre sıfırlama sayfaları, hesabım sayfası, siparişlerim sayfası gibi gibi gibi projede kullanılan bütün componentler yükleniyor. Haliyle açılış hızı yavaş olacak.


Peki bunun çözümü yok mu?


Lazy Loading Feature Modules


Türkçeye tembel yükleme olarak çevirilen Lazy Loading, uygulamamızdaki paketleri farklı modüllere ayırarak uygulamızın açılış süresini hızlandırır ve kaynak tüketimini azaltır. Peki bunu nasıl yapıyor?

Basit bir mantıkla, app-routing.module.ts dosyasında tanımlanan rotalarda loadChildren özelliği ile açılması gereken componenti tembelleştiriyor ve ilk yüklemede değil de o rotaya gidildiği zaman modül yükleniyor. Eğer projemizde az component varsa bu yöntem çok fark yaratmayacaktır o yüzden küçük projelerde kullanıp kullanmamak size kalmış ancak büyük projelerde mutlaka kullanılmalıdır.


Tanım olarak karışık gelmiş olabilir ancak gelin biz bunu bir proje üzerinde deneyelim. Ve projemizde önce her şeyi hazırlayalım sonra lazy loading'i bu projenin üstüne ekleyelim. Yani direkt lazy loading yapısına uygun olarak yapmayalım ki sizlerde mevcut projelerinizde bunu nasıl kullanacağınızı daha iyi anlayın.


Projemizde Anasayfa, Hakkımızda, Giriş yap, Kayıt ol sayfaları olsun.


İlk olarak projemizi oluşturuyoruz ve çalıştırıyoruz. Bu arada şuan için Angular'ın güncel versiyonu 7 ile bu işlemleri yapıyorum.

ng new angularLazyLoading --routing
cd angularLazyLoading
ng serve


Ardından componentlerimizi oluşturuyoruz.

ng g c components/header
ng g c components/footer
ng g c components/home
ng g c components/about
ng g c components/account/login
ng g c components/account/register


app.component.html dosyamızdaki gereksiz kodları siliyoruz ve aşağıdaki hale getiriyoruz;

<app-header></app-header>
<router-outlet></router-outlet>
<app-footer></app-footer>


Daha sonra header.component.html dosyamızdaki header works! bölümünü silelim ve hızlıca menü oluşturalım.

<ul>
  <li><a routerLink="/">Home</a></li>
  <li><a routerLink="about">About</a></li>
  <li><a routerLink="account/login">Login</a></li>
  <li><a routerLink="account/register">Register</a></li>
</ul>

menü elemanlarını yan yana getirelim bu yüzden header.component.scss dosyasına aşağıdaki satırları ekleyelim.

ul {
  margin: 0;
  padding: 15px;
  list-style: none;
  background-color: #f7e7f2;
  li {
    display: inline-block;
    a {
      padding: 5px 10px;
    }
  }
}


Evet şuana kadar projemizdeki görüntü şu şekilde olacak;


Hızlıca app-routing.module.ts dosyamızda routing kısmını ayarlayarak devam edelim.

const routes: Routes = [
  { path: '', component: HomeComponent, pathMatch: 'full' },
  { path: 'about', component: AboutComponent },
  {
    path: 'account',
    children: [
      { path: 'login', component: LoginComponent },
      { path: 'register', component: RegisterComponent }
    ]
  },
];


Bu değişikliğide yaptıktan sonra artık menüden sayfalar arası geçiş yapabiliyoruz.


Evet projemiz hazır! Şimdi lazy loading yok diye ne oluyor bir görelim.


Tarayıcınızda developer tools'u açıp (Opera için Ctrl + Shift + C Chrome için F12) Network menüsüne gelin. Sayfanızı bir kez yenileyin (Ctrl+F5). Şimdi burada projemizdeki dosyalar yükleniyor biz sayfa değiştiriyoruz ve hiçbir değişiklik olmuyor. Yani biz "about" bağlantısına tıkladığımızda about.component karşımıza geliyor evet ancak bu o anda yüklenmiyor sayfayı yenilediğimizde tüm componentler yükleniyor bu yüzden biz anasayfada da olsak giriş yapma sayfasında da olsak o componentler çoktaaan yüklenmiş oluyor.


Şimdi gelelim lazy loading olayına.


İlk olarak componentlerimizin bulunduğu dizinlere modüllerimizi oluşturalım.

Modül oluşturmak için ng generate module modulAdi komutu kullanılıyor. Bunun kısaltmasıda tahmin edeceğiniz gibi ng g m modulAdi şeklinde.


ng g m components/home --routing
ng g m components/about --routing
ng g m components/account --routing

Burada login ve register componentleri için ayrı modüller oluşturabilir ancak bu iki componenti tek modül içinde kullanmayı göstereyim ki çeşitlilik olsun.


Modüllerimizi oluşturduk şimdi geldik can alıcı kısıma.


app-routing.module.ts dosyamızı açıyoruz, burada küçük değişiklikler yapmamız gerekiyor. Burada hangi rotada hangi componentin yüklenmesi gerektiği bölümleri oluşturduğumuz modüllere yönlendireceğiz. Yani component: xxxx olan özelliği silip loadChildren özelliğini kullanacağız.


Örnek: { path: 'about', component: HomeComponent } olan bölümünü { path: 'about', loadChildren: './components/about/about.module#AboutModule' } bununla değiştireceğiz. Burada about.component'in bulunduğu dizine oluşturduğumuz about.module'nin yolunu belirtiyoruz. Yani src/app/components/about/about.module.ts yi belirtiyoruz ve #AboutModule yazan ise about.module.ts dosyasının içindeki export class AboutModule { } kısmından geliyor.


Şimdi ilk önce anasayfa için bunu bir düzenleyelim sonra hızlıca diğerlerini düzenleyeceğiz.

{ path: '', component: HomeComponent, pathMatch: 'full' }

bu satırı aşaıdakiyle değiştirin.

{ path: '', loadChildren: './components/home/home.module#HomeModule', pathMatch: 'full' }

Aşağıdaki satırıda silebilirsiniz. Çünkü artık app-routing.module.ts içerisinde HomeComponent'i kullandığımız bir yer kalmadı dolayısıyla bu import işlemide gereksiz.

import {HomeComponent} from './components/home/home.component';


Bu değişikliği yaptıktan sonra uygulamamızda herhangi bir hata oluşmayacak ancak anasayfayı açtığımızda home works! yazısıda gelmeyecek. Çünkü biz home.module.ts içerisinde HomeComponent'i çağırmadık ve home-routing.module.ts içerisinde de bunu tanımlamadık. Hemen bunuda yapalım;


home.module.ts dosyamızda HomeComponent'i import ediyoruz ve declarations kısmında tanımlıyoruz, yani;

declarations: [
  HomeComponent
]


home-routing.module.ts dosyasında routes değişkenini aşağıdaki gibi ayarlıyoruz;

const routes: Routes = [
  { path: '', component: HomeComponent }
];


Bu değişiklikleride yapınca uygulamamıza döndüğümüzde console'da

"ERROR Error: Uncaught (in promise): Error: Type HomeComponent is part of the declarations of 2 modules: AppModule and HomeModule! Please consider moving HomeComponent to a higher module that imports AppModule and HomeModule."


gibi bir hata göreceksiniz. Burada diyor ki HomeComponent'i hem AppModule hemde HomeModule içinde kullandın, böyle olmaz.


Ne yapıyoruz?

Tabiki app.module.ts içinden HomeComponent'i siliyoruz. Çünkü artık app.module içinde onu kullanmıyoruz HomeComponent'e sadece HomeModule yüklendiği zaman ihtiyacımız olacak.


app.module.ts içerisinde declarations: [...] kısmından HomeComponent'i silin. Bunu silince import { HomeComponent } from './components/home/home.component' kısmınıda silebilirsiniz.


Şuanda HomeComponent için lazy loading işlemlerimiz bitti. Ne oldu gelin bir bakalım isterseniz;


about, login veya register sayfalarından birine girin (anasayfaya değil) sonra tarayıcınızın developer tools'unu açın Network sekmesine tıklayın ve bir kez sayfayı yenileyin. Daha sonra aşağıda yüklenen dosyalar kısmına dikkat edin ve Anasayfa'ya yani "Home" menüsüne tıklayın.


Evet yeni bir dosya dahil oldu sayfamıza: components-home-home-module.js. Hadi kalan işimizide halledelim ve bitsin artık bu lazy loading :)



Küçük bir not: Şuana kadar yapılanlar uzun bir işlem gibi görünebilir, ben olabildiğince açıklayabilmek için bu yüzden böyle oldu ancak aşağıda about ve account kısımlarını yaparken aslında ne kadar hızlı ve basit olduğunu göreceksiniz.



app-routing.module.ts dosyasını açıp değişikliklerimizi yapıyoruz;

{ path: 'about', component: AboutComponent },
{
  path: 'account',
  children: [
    { path: 'login', component: LoginComponent },
    { path: 'register', component: RegisterComponent }
  ]
}


bunları aşağıdakiyle değiştiriyoruz;

{ path: 'about', loadChildren: './components/about/about.module#AboutModule' },
{ path: 'account', loadChildren: './components/account/account.module#AccountModule' }


Aşağıdakileri siliyoruz;

import {AboutComponent} from './components/about/about.component';
import {LoginComponent} from './components/account/login/login.component';
import {RegisterComponent} from './components/account/register/register.component';


Bu işlemlerin sonunda app-routing.module.ts dosyası aşağıdaki gibi olacak;

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: '', loadChildren: './components/home/home.module#HomeModule', pathMatch: 'full' },
  { path: 'about', loadChildren: './components/about/about.module#AboutModule' },
  { path: 'account', loadChildren: './components/account/account.module#AccountModule' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }



app.module.ts dosyasını açıyoruz ve declarations bölümünden AboutComponent, LoginComponent, RegisterComponent'i siliyoruz. Bu işlem sonunda app.module.ts aşağıdaki gibi olacak;


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeaderComponent } from './components/header/header.component';
import { FooterComponent } from './components/footer/footer.component';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    FooterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


about.module.ts dosyasını açıp declarations kısmında AboutComponent'i tanımlıyoruz.

about-routing.module.ts dosyasını açıp rotamızı tanımlıyoruz;

const routes: Routes = [
  { path: '', component: AboutComponent }
];


About kısmıda bitti.


Geldik account modülüne, bu modülde 2 component olacaktı (login ve register).


account.module.ts dosyasını açıp declarations kısmında LoginComponent ve RegisterComponent'i tanımlıyoruz.

account-routing.module.ts dosyasını açıp rotalarımızı tanımlıyoruz;

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'register', component: RegisterComponent }
];


Account kısmıda bitti.


O zaman test edelim;


Evet görüldüğü gibi About'a giriyoruz about.module yükleniyor login veya register'a giriyoruz account.module yükleniyor. Home'da ise home.module yükleniyor. Yani amacımıza ulaştık.


Umarım açıklamam gereken tüm bölümleri sade bir şekilde açıklayabilmişimdir.

Ayrıca oluşturduğumuz bu projenin bitmiş haline de buradan ulaşabilirsiniz: https://github.com/MehmetSert/angularLazyLoading


Teşekkürler.