Angular 9 dan Ivy: pemuatan komponen yang malas

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 Ivy

Di 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 Kuis

Pertanyaan-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 QuizCardComponent

Setelah 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 malas

Setelah 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 Dimodifikasi

Hebat! 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 aplikasi

Ukuran 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 komponen

Ini 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?

Source: https://habr.com/ru/post/id484618/


All Articles