Pemuatan komponen malas di Angular? Mungkin kita berbicara tentang modul memuat malas menggunakan router Angular? Tidak, kita berbicara tentang komponen. Versi Angular saat ini hanya mendukung pemuatan modul malas. Tapi Ivy memberi pengembang peluang baru dalam bekerja dengan komponen.

Beban malas yang kami gunakan sejauh ini: rute
Pemuatan malas adalah mekanisme yang bagus. Di Angular, Anda dapat menggunakan mekanisme ini tanpa harus melakukan hampir semua upaya dengan menyatakan rute yang mendukung pemuatan malas. Berikut adalah contoh dari
dokumentasi Angular yang menunjukkan ini:
const routes: Routes = [ { path: 'customer-list', loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) } ];
Berkat kode di atas, sebuah fragmen terpisah untuk
customers.module
Modul akan dibuat, yang akan dimuat saat pengguna menavigasi sepanjang rute
customer-list
.
Ini adalah cara yang sangat bagus untuk mengurangi ukuran bundel utama proyek dan mempercepat pemuatan awal aplikasi.
Namun, meskipun demikian, bukankah menyenangkan untuk dapat mengontrol pemuatan malas secara lebih akurat? Misalnya, bagaimana jika kita dapat mengatur pemuatan komponen individu yang malas?
Sampai sekarang, ini belum mungkin. Tapi itu semua berubah dengan kedatangan Ivy.
Ivy dan konsep "lokalitas"
Modul adalah konsep dasar dan blok bangunan dasar dari setiap aplikasi Angular. Modul menyatakan komponen, arahan, pipa, layanan.
Aplikasi Angular modern tidak dapat ada tanpa modul. Salah satu alasan untuk ini adalah kenyataan bahwa ViewEngine menambahkan semua metadata yang diperlukan ke modul.
Ivy, di sisi lain, mengambil pendekatan yang berbeda. Di Ivy, komponen bisa ada tanpa modul. Ini dimungkinkan berkat konsep lokalitas. Esensinya adalah bahwa semua metadata adalah lokal untuk komponen.
Mari kita jelaskan ini dengan menganalisis bundel es2015 yang dihasilkan menggunakan Ivy.
Es2015-bundle dihasilkan menggunakan IvyDi bagian
Component code
, Anda dapat melihat bahwa sistem Ivy telah menyimpan kode komponen. Tidak ada yang istimewa di sini. Tapi kemudian Ivy menambahkan beberapa metadata ke kode.
Bagian pertama metadata ditunjukkan pada gambar sebagai
Component factory
. Pabrik tahu bagaimana membuat instantiate komponen. Di bagian
Component metadata
, Ivy menempatkan atribut tambahan, seperti
type
dan
selectors
, yaitu, semua yang dibutuhkan komponen selama eksekusi program.
Perlu disebutkan bahwa Ivy menambahkan fungsi
template
sini. Itu ditampilkan di
Compiled version of your template
bagian
Compiled version of your template
. Mari kita membahas fakta menarik ini secara lebih rinci.
Fungsi
template
adalah versi kompilasi dari kode HTML kami. Dia mengikuti instruksi Ivy untuk membuat DOM. Ini berbeda dari cara kerja ViewEngine.
Sistem ViewEngine mengambil kode dan memotongnya. Angular kemudian akan mengeksekusi kode jika kita menggunakannya.
Dan dengan pendekatan yang digunakan oleh Ivy, komponen yang memanggil perintah Angular bertanggung jawab atas segalanya. Perubahan ini memungkinkan komponen ada secara mandiri, ini mengarah pada fakta bahwa algoritma tree-shake dapat diterapkan pada kode dasar Angular.
Contoh nyata dari pemuatan komponen yang malas
Sekarang kita tahu bahwa pemuatan komponen yang malas dimungkinkan, pertimbangkan dengan contoh nyata. Yaitu, kami akan membuat aplikasi Kuis yang menanyakan pertanyaan pengguna dengan opsi jawaban.
Aplikasi ini menampilkan gambar kota dan opsi dari mana Anda perlu memilih nama kota ini. Segera setelah pengguna memilih opsi dengan mengklik tombol yang sesuai, tombol ini akan segera berubah, menunjukkan apakah jawabannya benar atau salah. Jika latar belakang tombol berubah menjadi hijau, maka jawabannya benar. Jika latar belakang berubah merah, itu berarti jawabannya salah.
Setelah jawaban atas pertanyaan saat ini diterima, program akan menampilkan pertanyaan berikut. Ini tampilannya.
Demo KuisPertanyaan-pertanyaan yang ditanyakan program kepada pengguna diwakili oleh komponen
QuizCardComponent
.
Konsep pemuatan komponen malas
Pertama mari kita ilustrasikan ide umum pemuatan malas komponen
QuizCardComponent
.
Proses Komponen QuizCardComponentSetelah pengguna memulai kuis dengan mengklik tombol
Start quiz
, kami mulai memuat komponen dengan malas. Setelah komponen tersedia, kami menempatkannya dalam wadah.
Kami bereaksi terhadap peristiwa yang
questionAnsvered
dari komponen yang "malas" dengan cara yang sama seperti kami akan menanggapi acara dari komponen yang biasa. Yaitu, setelah terjadinya acara, kami menampilkan di layar kartu berikutnya dengan pertanyaan.
Analisis kode
Untuk memahami apa yang terjadi selama pemuatan komponen yang malas, kami mulai dengan versi
QuizCardComponent
disederhanakan, yang menampilkan properti pertanyaan.
Kemudian kami akan memperluas komponen ini menggunakan komponen Material Sudut di dalamnya. Dan pada akhirnya, kami akan menyesuaikan reaksi terhadap peristiwa yang dihasilkan oleh komponen.
Mari kita mengatur pemuatan malas dari komponen
QuizCardComponent
yang
QuizCardComponent
, yang memiliki templat berikut:
<h1>Here's the question</h1> <ul> <li><b>Image: </b> {{ question.image }}</li> <li><b>Possible selections: </b> {{ question.possibleSelections.toString() }}</li> <li><b>Correct answer: </b> {{ question.correctAnswer }}</li> </ul>
Langkah pertama adalah membuat elemen kontainer. Untuk melakukan ini, kita dapat menggunakan elemen nyata, seperti
<div>
, atau - menggunakan
ng-container
, yang memungkinkan kita melakukannya tanpa tambahan level kode-HTML. Beginilah deklarasi elemen kontainer di mana kita menempatkan komponen "malas":
<mat-toolbar color="primary"> <span>City quiz</span> </mat-toolbar> <button *ngIf="!quizStarted" mat-raised-button color="primary" class="start-quiz-button" (click)="startQuiz()">Start quiz</button> <ng-container #quizContainer class="quiz-card-container"></ng-container>
Dalam komponen yang Anda butuhkan untuk mengakses wadah. Untuk melakukan ini, kami menggunakan anotasi
@ViewChild
dan beri tahu kami bahwa kami ingin membaca
ViewContainerRef
.
Perhatikan bahwa dalam Angular 9, properti
static
di penjelasan
@ViewChild
disetel ke
false
secara default:
@ViewChild('quizContainer', {read: ViewContainerRef}) quizContainer: ViewContainerRef;
Sekarang kami memiliki wadah di mana kami ingin menambahkan komponen "malas". Selanjutnya, kita perlu
ComponentFactoryResolver
dan
Injector
. Keduanya dapat diperoleh dengan beralih ke metodologi injeksi ketergantungan.
Entitas
ComponentFactoryResolver
adalah registri sederhana yang menetapkan hubungan antara komponen dan kelas
ComponentFactory
dihasilkan secara otomatis yang dapat digunakan untuk instantiate komponen:
constructor(private quizservice: QuizService, private cfr: ComponentFactoryResolver, private injector: Injector) { }
Sekarang kami memiliki segala yang dibutuhkan untuk mencapai tujuan.
startQuiz
bekerja pada isi metode
startQuiz
dan mengatur pemuatan komponen yang malas:
const {QuizCardComponent} = await import('./quiz-card/quiz-card.component'); const quizCardFactory = this.cfr.resolveComponentFactory(QuizCardComponent); const {instance} = this.quizContainer.createComponent(quizCardFactory, null, this.injector); instance.question = this.quizservice.getNextQuestion();
Kita dapat menggunakan perintah
import
ECMAScript untuk mengatur pemuatan malas
QuizCardComponent
. Ekspresi impor mengembalikan janji. Anda dapat bekerja dengannya menggunakan
async/await
konstruk atau menggunakan
.then
handler. Setelah menyelesaikan janji, kami menggunakan destrukturisasi untuk membuat instance komponen.
Saat ini, Anda harus menggunakan
ComponentFactory
untuk memberikan kompatibilitas mundur. Di masa mendatang, garis yang sesuai tidak diperlukan, karena kami akan dapat bekerja dengan komponen secara langsung.
Pabrik
ComponentFactory
memberi kita
componentRef
. Kami melewati
componentRef
dan
Injector
ke metode
createComponent
wadah.
Metode
createComponent
mengembalikan
ComponentRef
mana instance komponen terkandung. Kami menggunakan
instance
untuk melewatkan properti komponen
@Input
.
Di masa depan, semua ini mungkin dapat dilakukan dengan menggunakan metode Angular
renderComponent
. Metode ini masih eksperimental. Namun, sangat mungkin bahwa itu akan berubah menjadi metode Angular biasa.
Berikut adalah materi yang bermanfaat tentang topik ini.
Ini semua yang diperlukan untuk mengatur pemuatan komponen yang malas.
Pemuatan komponen yang malasSetelah tombol
Start quiz
ditekan, pemuatan komponen yang malas dimulai. Jika Anda membuka tab
Network
alat pengembang, Anda dapat melihat proses pemuatan malas kode fragmen yang diwakili oleh file
quiz-card-quiz-card-component.js
. Setelah memuat dan memproses komponen ditampilkan, pengguna melihat kartu pertanyaan.
Perpanjangan Komponen
Kami sedang memuat komponen
QuizCardComponent
. Ini sangat bagus. Tetapi aplikasi kami belum berfungsi secara khusus.
Mari kita perbaiki ini dengan menambahkan fitur dan komponen tambahan dari Bahan Angular ke dalamnya:
<mat-card class="quiz-card"> <mat-card-header> <div mat-card-avatar class="quiz-header-image"></div> <mat-card-title>Which city is this?</mat-card-title> <mat-card-subtitle>Click on the correct answer below</mat-card-subtitle> </mat-card-header> <img class="image" mat-card-image [src]="'assets/' + question.image" alt="Photo of a Shiba Inu"> <mat-card-actions class="answer-section"> <button [disabled]="answeredCorrectly !== undefined" *ngFor="let selection of question.possibleSelections" mat-stroked-button color="primary" [ngClass]="{ 'correct': answeredCorrectly && selection === question.correctAnswer, 'wrong': answeredCorrectly === false && selection === question.correctAnswer }" (click)="answer(selection)"> {{selection}} </button> </mat-card-actions> </mat-card>
Kami telah memasukkan beberapa komponen Material yang indah ke dalam komponen. Bagaimana dengan modul yang sesuai?
Mereka tentu saja dapat ditambahkan ke
AppModule
. Tetapi ini berarti bahwa modul-modul ini akan dimuat dalam mode "serakah". Dan ini bukan ide yang bagus. Selain itu, perakitan proyek akan gagal, dengan pesan berikut:
ERROR in src/app/quiz-card/quiz-card.component.html:9:1 - error TS-998001: 'mat-card' is not a known element: 1. If 'mat-card' is an Angular component, then verify that it is part of this module. 2. If 'mat-card' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
Apa yang harus dilakukan Seperti yang mungkin sudah Anda pahami, masalah ini sepenuhnya bisa dipecahkan. Ini dapat dilakukan dengan menggunakan modul.
Tapi kali ini kita akan menggunakannya sedikit berbeda dari sebelumnya. Kami akan menambahkan modul kecil ke file yang sama dengan
QuizCardComponent
(dalam file
quizcard.component.ts
):
@NgModule({ declarations: [QuizCardComponent], imports: [CommonModule, MatCardModule, MatButtonModule] }) class QuizCardModule { }
Perhatikan bahwa tidak ada pernyataan
export
.
Spesifikasi modul ini dimiliki secara eksklusif oleh komponen kami, dimuat dalam mode malas. Akibatnya, satu-satunya komponen yang dideklarasikan dalam modul adalah
QuizCardComponent
. Bagian
import
hanya mengimpor modul yang dibutuhkan komponen kami.
Kami tidak mengekspor modul baru sehingga modul yang dimuat dalam mode serakah tidak dapat mengimpornya.
Mulai ulang aplikasi dan lihat bagaimana perilakunya ketika Anda mengklik tombol
Start quiz
.
Analisis Aplikasi yang DimodifikasiHebat! Komponen
QuizCardComponent
dimuat dalam mode malas dan ditambahkan ke
ViewContainer
. Semua dependensi yang diperlukan dimuat dengannya.
Kami akan menganalisis bundel aplikasi menggunakan alat
webpack-bundle-analyze
yang disediakan oleh modul npm yang sesuai. Ini memungkinkan Anda untuk memvisualisasikan konten file yang dihasilkan Webpack dan memeriksa skema yang dihasilkan.
Analisis bundel aplikasiUkuran bundel utama aplikasi adalah sekitar 260 Kb. Jika kita mengunduh komponen
QuizCardComponent
bersamaan dengan itu, maka ukuran data yang diunduh akan sekitar 270 Kb. Ternyata kami mampu mengurangi ukuran bundel utama sebesar 10 Kb, hanya memuat satu komponen dalam mode malas. Hasil yang bagus!
Kode
QuizCardComponent
setelah perakitan sampai ke file terpisah. Jika Anda menganalisis konten file ini, ternyata tidak hanya kode
QuizCardComponent
, tetapi juga modul Material yang digunakan dalam komponen ini.
Meskipun
QuizCardComponent
menggunakan
MatButtonModule
dan
MatCardModule
, hanya
MatCardModule
masuk ke
MatCardModule
kode yang sudah jadi. Alasan untuk ini adalah bahwa kami menggunakan
MatButtonModule
di
AppModule
, menampilkan tombol
Start quiz
. Akibatnya, kode yang sesuai jatuh ke fragmen lain dari bundel.
Sekarang kami telah mengatur pemuatan malas
QuizCardComponent
. Komponen ini menampilkan kartu, dirancang dengan gaya Material, berisi gambar, pertanyaan, dan tombol dengan opsi jawaban. Apa yang terjadi sekarang jika Anda mengklik salah satu tombol ini?
Tombol, tergantung pada apakah itu berisi jawaban benar atau salah, menjadi hijau atau merah saat ditekan. Apa lagi yang terjadi? Tidak ada Dan kita perlu bahwa setelah menjawab satu pertanyaan, kartu pertanyaan lain akan ditampilkan. Perbaiki
Penanganan acara
Aplikasi tidak menunjukkan kartu pertanyaan baru ketika Anda mengklik tombol jawab karena fakta bahwa kami belum menyiapkan mekanisme untuk menanggapi peristiwa komponen yang dimuat dalam mode malas. Kita sudah tahu bahwa komponen
QuizCardComponent
menghasilkan peristiwa menggunakan
EventEmitter
. Jika Anda melihat definisi kelas
EventEmitter
, Anda bisa mengetahui bahwa itu adalah turunan dari
Subject
:
export declare class EventEmitter<T extends any> extends Subject<T>
Ini berarti bahwa
EventEmitter
memiliki metode
subscribe
, yang memungkinkan Anda untuk mengonfigurasi respons sistem terhadap peristiwa yang terjadi:
instance.questionAnswered.pipe( takeUntil(instance.destroy$) ).subscribe(() => this.showNewQuestion());
Di sini kita berlangganan aliran
questionAnswered
dan memanggil metode
showNextQuestion
, yang mengeksekusi logika
lazyLoadQuizCard
:
async showNewQuestion() { this.lazyLoadQuizCard(); }
takeUntil(instance.destroy$)
diperlukan untuk menghapus langganan yang dieksekusi setelah komponen dihancurkan. Jika kait
ngOnDestroy
dari siklus hidup komponen
ngOnDestroy
dipanggil,
destroy$
disebut dengan
next
dan
complete
.
Karena komponen sudah dimuat, sistem tidak melakukan permintaan HTTP tambahan. Kami menggunakan konten dari fragmen kode yang sudah kami miliki, membuat komponen baru dan memasukkannya ke dalam wadah.
Kait siklus hidup komponen
Hampir semua kait siklus hidup komponen secara otomatis dipanggil ketika bekerja dengan komponen
QuizCardComponent
menggunakan teknik lazy loading. Tetapi satu kait saja tidak cukup. Bisakah kamu mengerti yang mana?
Kait siklus hidup komponenIni adalah
ngOnChanges
paling penting
ngOnChanges
. Karena kami sendiri memperbarui properti input instance komponen, kami juga bertanggung jawab untuk memanggil
ngOnChanges
siklus hidup
ngOnChanges
.
Untuk memanggil kait
ngOnChanges
dari instance
ngOnChanges
, Anda perlu membuat sendiri objek
SimpleChange
:
(instance as any).ngOnChanges({ question: new SimpleChange(null, instance.question, true) });
Kami secara manual memanggil
ngOnChanges
instance komponen dan memberikannya objek
SimpleChange
. Objek ini menunjukkan bahwa perubahan ini adalah yang pertama, bahwa nilai sebelumnya adalah
null
, dan bahwa nilai saat ini adalah pertanyaan.
Hebat! Kami memuat komponen dengan modul pihak ketiga, merespons peristiwa yang dihasilkannya dan mengatur panggilan kait yang diperlukan dari siklus hidup komponen.
Ini adalah kode sumber untuk proyek selesai yang sedang kami kerjakan di sini.
Ringkasan
Pemuatan komponen yang malas memberi pengembang Angular peluang besar untuk mengoptimalkan kinerja aplikasi. Yang dimilikinya adalah alat untuk menyempurnakan komposisi material yang dimuat dalam mode malas. Sebelumnya, ketika hanya bisa memuat rute dalam mode malas, kami tidak memiliki keakuratan seperti itu.
Sayangnya, saat menggunakan modul pihak ketiga dalam komponen, kita juga harus menjaga modul tersebut. Namun, perlu diingat bahwa di masa depan hal ini dapat berubah.
Mesin Ivy memperkenalkan konsep lokalitas, berkat komponen mana yang dapat eksis secara mandiri. Perubahan ini adalah fondasi masa depan Angular.
Pembaca yang budiman! Apakah Anda berencana untuk menggunakan teknik pemuatan komponen yang malas dalam proyek Angular Anda?
