Penulis artikel, bagian pertama dari terjemahan yang kami terbitkan, mengatakan bahwa ia telah mengerjakan aplikasi berskala besar di
Trade Me selama sekitar dua tahun sekarang. Selama beberapa tahun terakhir, tim pengembangan aplikasi terus meningkatkan proyek, baik dari segi kualitas kode dan kinerja.
Seri materi ini akan fokus pada pendekatan pengembangan yang digunakan oleh tim Trade Me, yang dinyatakan dalam bentuk lebih dari dua lusin rekomendasi mengenai teknologi seperti Angular, TypeScript, RxJS dan @ ngrx / store. Selain itu, beberapa perhatian akan diberikan pada teknik pemrograman universal yang bertujuan membuat kode aplikasi lebih bersih dan lebih akurat.
1. Tentang trackBy
Menggunakan
ngFor
untuk melintasi array dalam template, gunakan konstruksi ini dengan fungsi
trackBy
, yang mengembalikan pengidentifikasi unik untuk setiap elemen.
β Penjelasan
Ketika array berubah, Angular merender seluruh pohon DOM. Tetapi jika Anda menggunakan
trackBy
, sistem akan tahu elemen mana yang telah berubah dan akan membuat perubahan pada DOM yang hanya berlaku untuk elemen tertentu. Detail tentang ini dapat ditemukan di
sini .
βTo
<li *ngFor="let item of items;">{{ item }}</li>
FterSetelah itu
// <li *ngFor="let item of items; trackBy: trackByFn">{{ item }}</li> // trackByFn(index, item) { return item.id; // id, }
2. Kata kunci const dan let
Jika Anda akan mendeklarasikan variabel yang nilainya tidak Anda rencanakan untuk diubah, gunakan kata kunci
const
.
β Penjelasan
Penggunaan yang tepat dari kata kunci
let
dan
const
mengklarifikasi maksud mengenai penggunaan entitas yang dinyatakan menggunakannya. Selain itu, pendekatan ini membuatnya lebih mudah untuk mengenali masalah yang disebabkan oleh nilai konstan yang ditimpa secara tidak sengaja. Dalam situasi ini, kesalahan kompilasi dilemparkan. Selain itu, ini meningkatkan keterbacaan kode.
βTo
let car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}; if (iHaveMoreThanOneCar) { myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) { yourCar = `${youCar}s`; }
FterSetelah itu
// car , car const car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}; if (iHaveMoreThanOneCar) { myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) { yourCar = `${youCar}s`; }
3. operator konveyor
Saat bekerja dengan RxJS, gunakan operator pipelined.
β Penjelasan
Operator yang disampaikan mendukung algoritma pohon-goyang, yaitu, ketika mereka diimpor, hanya kode yang direncanakan akan dieksekusi akan dimasukkan dalam proyek. Ini juga memudahkan untuk mengidentifikasi pernyataan yang tidak digunakan dalam file.
Harap dicatat bahwa rekomendasi ini relevan untuk Angular versi 5.5 dan lebih tinggi.
βTo
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/take'; iAmAnObservable .map(value => value.item) .take(1);
FterSetelah itu
import { map, take } from 'rxjs/operators'; iAmAnObservable .pipe( map(value => value.item), take(1) );
4. Isolasi perbaikan API
Tidak semua API sepenuhnya stabil dan bebas kesalahan. Oleh karena itu, kadang-kadang perlu untuk memperkenalkan beberapa logika ke dalam kode yang ditujukan untuk memperbaiki masalah API. Alih-alih menempatkan logika ini dalam komponen di mana API yang ditambal digunakan, akan lebih baik untuk mengisolasinya di suatu tempat, misalnya, dalam layanan, dan merujuk dari komponen tidak lagi ke API yang bermasalah, tetapi ke layanan yang sesuai.
β Penjelasan
Pendekatan yang diusulkan memungkinkan menjaga koreksi "lebih dekat" ke API, yaitu, sedekat mungkin dengan kode dari mana permintaan jaringan dibuat mungkin. Akibatnya, jumlah kode aplikasi yang berinteraksi dengan API yang bermasalah berkurang. Selain itu, ternyata semua koreksi ada di satu tempat, akibatnya akan lebih mudah untuk bekerja dengannya. Jika Anda harus memperbaiki kesalahan di API, maka jauh lebih mudah untuk melakukan ini dalam satu file daripada menyebarkan koreksi ini di seluruh aplikasi. Ini memfasilitasi tidak hanya penciptaan koreksi, tetapi juga pencarian kode yang sesuai dalam proyek, dan dukungannya.
Selain itu, Anda dapat membuat tag Anda sendiri, seperti
API_FIX
(yang menyerupai tag
TODO
), dan menandai perbaikan dengan mereka. Ini membuatnya lebih mudah untuk menemukan perbaikan seperti itu.
5. Berlangganan dalam template
Hindari berlangganan yang dapat diobservasi dari komponen. Sebagai gantinya, berlanggananlah dalam templat.
β Penjelasan
Operator pipeline asinkron secara otomatis berhenti berlangganan sendiri, yang menyederhanakan kode, menghilangkan kebutuhan untuk manajemen berlangganan manual. Ini, di samping itu, mengurangi risiko bahwa pengembang akan lupa berhenti berlangganan dari komponen, yang dapat menyebabkan kebocoran memori. Dimungkinkan untuk mengurangi kemungkinan kebocoran memori dengan menggunakan aturan linter yang bertujuan mengidentifikasi objek yang dapat diamati yang tidak mereka hentikan berlangganan.
Selain itu, penerapan pendekatan ini mengarah pada fakta bahwa komponen berhenti menjadi komponen stateful, yang dapat menyebabkan kesalahan ketika data berubah di luar langganan.
βTo
// <p>{{ textToDisplay }}</p> // iAmAnObservable .pipe( map(value => value.item), takeUntil(this._destroyed$) ) .subscribe(item => this.textToDisplay = item);
FterSetelah itu
// <p>{{ textToDisplay$ | async }}</p> // this.textToDisplay$ = iAmAnObservable .pipe( map(value => value.item) );
6. Menghapus Langganan
Saat berlangganan ke objek yang dipantau, selalu pastikan bahwa langganan ke mereka dihapus dengan benar menggunakan operator seperti
take
,
takeUntil
dan sebagainya.
β Penjelasan
Jika Anda tidak berhenti berlangganan dari objek yang diamati, ini akan menyebabkan kebocoran memori, karena aliran objek yang diamati akan tetap terbuka, yang dimungkinkan bahkan setelah komponen dihancurkan atau setelah pengguna pergi ke halaman lain dari aplikasi.
Yang lebih baik lagi adalah membuat aturan linter untuk mendeteksi objek yang diamati dengan langganan yang valid.
βTo
iAmAnObservable .pipe( map(value => value.item) ) .subscribe(item => this.textToDisplay = item);
FterSetelah itu
Gunakan operator
takeUntil
jika Anda ingin mengamati perubahan beberapa objek hingga objek lain yang diamati menghasilkan nilai tertentu:
private destroyed$ = new Subject(); public ngOnInit (): void { iAmAnObservable .pipe( map(value => value.item)
Menggunakan sesuatu seperti
this
adalah pola yang digunakan untuk mengontrol penghapusan langganan ke banyak objek yang diamati dalam suatu komponen.
Gunakan
take
jika Anda hanya perlu nilai pertama yang dikembalikan oleh objek yang diamati:
iAmAnObservable .pipe( map(value => value.item), take(1), takeUntil(this._destroyed$) ) .subscribe(item => this.textToDisplay = item);
Harap perhatikan bahwa di sini kami menggunakan
takeUntil
dengan
take
. Hal ini dilakukan untuk menghindari kebocoran memori yang disebabkan oleh fakta bahwa langganan tidak mengarah pada memperoleh nilai sampai komponen dihancurkan. Jika fungsi
takeUntil
tidak digunakan di
takeUntil
, berlangganan akan ada sampai nilai pertama diterima, tetapi karena komponen sudah dihancurkan, nilai ini tidak akan pernah diterima, yang akan menyebabkan kebocoran memori.
7. Menggunakan operator yang sesuai
Menggunakan operator perataan dengan objek yang dapat diamati, terapkan yang sesuai dengan fitur dari masalah yang sedang dipecahkan.
- Gunakan
switchMap
ketika Anda perlu mengabaikan tindakan terjadwal sebelumnya ketika tindakan baru tiba. - Gunakan
mergeMap
jika Anda perlu memproses semua tindakan yang dikirim secara paralel. - Gunakan
concatMap
ketika tindakan perlu diproses satu demi satu, sesuai urutan diterima. - Gunakan
exhaustMap
dalam situasi di mana, dalam proses pemrosesan tindakan yang diterima sebelumnya, Anda perlu mengabaikan yang baru.
Detail tentang ini dapat ditemukan di
sini .
β Penjelasan
Jika mungkin, menggunakan satu operator, alih-alih mencapai efek yang sama dengan menggabungkan beberapa operator dalam satu rantai, mengurangi jumlah kode aplikasi yang perlu dikirim ke pengguna. Menggunakan operator yang dipilih secara tidak tepat dapat menyebabkan perilaku program yang salah, karena berbagai operator memproses objek secara berbeda.
8. Memuat malas
Kemudian, jika memungkinkan, cobalah untuk mengatur pemuatan modul-modul aplikasi Angular yang malas. Teknik ini bermuara pada fakta bahwa sesuatu dimuat hanya jika digunakan. Misalnya, komponen dimuat hanya ketika perlu ditampilkan.
β Penjelasan
Pemuatan malas mengurangi ukuran materi aplikasi yang harus diunduh pengguna. Ini dapat meningkatkan kecepatan pengunduhan aplikasi karena fakta bahwa modul yang tidak digunakan tidak ditransfer dari server ke klien.
βTo
// app.routing.ts { path: 'not-lazy-loaded', component: NotLazyLoadedComponent }
FterSetelah itu
// app.routing.ts { path: 'lazy-load', loadChildren: 'lazy-load.module#LazyLoadModule' } // lazy-load.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { LazyLoadComponent } from './lazy-load.component'; @NgModule({ imports: [ CommonModule, RouterModule.forChild([ { path: '', component: LazyLoadComponent } ]) ], declarations: [ LazyLoadComponent ] }) export class LazyModule {}
9. Tentang langganan dalam langganan lain
Terkadang, untuk melakukan beberapa tindakan, Anda mungkin memerlukan data dari beberapa objek yang dapat diamati. Dalam situasi seperti itu, hindari membuat langganan ke objek tersebut di dalam blok
subscribe
objek yang dapat diamati lainnya. Alih-alih, gunakan operator yang sesuai untuk memadukan perintah. Di antara operator tersebut, seseorang dapat mencatat dengan
withLatestFrom
dan
combineLatest
. Pertimbangkan contoh-contohnya, dan berikan komentar.
βTo
firstObservable$.pipe( take(1) ) .subscribe(firstValue => { secondObservable$.pipe( take(1) ) .subscribe(secondValue => { console.log(`Combined values are: ${firstValue} & ${secondValue}`); }); });
FterSetelah itu
firstObservable$.pipe( withLatestFrom(secondObservable$), first() ) .subscribe(([firstValue, secondValue]) => { console.log(`Combined values are: ${firstValue} & ${secondValue}`); });
β Penjelasan
Jika kita berbicara tentang keterbacaan, tentang kompleksitas kode, atau tentang tanda-tanda kode yang buruk, maka ketika program tidak menggunakan fitur RxJS secara penuh, ini berarti bahwa pengembang tidak terbiasa dengan API RxJS. Jika kita menyentuh topik kinerja, ternyata jika objek yang diamati memerlukan waktu untuk diinisialisasi, maka ia akan berlangganan
firstObservable
, maka sistem akan menunggu operasi selesai, dan hanya setelah itu pekerjaan dengan objek yang diamati kedua akan dimulai. Jika objek ini adalah permintaan jaringan, maka itu akan terlihat seperti eksekusi permintaan yang sinkron.
10. Tentang mengetik
Selalu berusaha mendeklarasikan variabel atau konstanta dengan tipe selain
any
.
β Penjelasan
Jika variabel atau konstanta dideklarasikan dalam TypeScript tanpa menentukan tipe, tipe akan disimpulkan berdasarkan nilai yang diberikan padanya. Ini bisa menimbulkan masalah. Pertimbangkan contoh klasik perilaku sistem dalam situasi yang serupa:
const x = 1; const y = 'a'; const z = x + y; console.log(`Value of z is: ${z}` // Value of z is 1a
Diasumsikan bahwa
y
adalah angka di sini, tetapi program kami tidak mengetahuinya, sehingga ia menampilkan sesuatu yang terlihat salah, tetapi tidak menghasilkan pesan kesalahan apa pun. Masalah serupa dapat dihindari dengan menetapkan tipe yang sesuai untuk variabel dan konstanta.
Kami menulis ulang contoh di atas:
const x: number = 1; const y: number = 'a'; const z: number = x + y; // : Type '"a"' is not assignable to type 'number'. const y:number
Ini membantu untuk menghindari kesalahan tipe data.
Keuntungan lain dari pendekatan sistematis untuk mengetik adalah menyederhanakan refactoring dan mengurangi kemungkinan kesalahan selama proses ini.
Pertimbangkan sebuah contoh:
public ngOnInit (): void { let myFlashObject = { name: 'My cool name', age: 'My cool age', loc: 'My cool location' } this.processObject(myFlashObject); } public processObject(myObject: any): void { console.log(`Name: ${myObject.name}`); console.log(`Age: ${myObject.age}`); console.log(`Location: ${myObject.loc}`); } // Name: My cool name Age: My cool age Location: My cool location
Misalkan kita ingin mengubah, di
myFlashObject
, nama properti
loc
ke
location
dan membuat kesalahan
myFlashObject
mengedit kode:
public ngOnInit (): void { let myFlashObject = { name: 'My cool name', age: 'My cool age', location: 'My cool location' } this.processObject(myFlashObject); } public processObject(myObject: any): void { console.log(`Name: ${myObject.name}`); console.log(`Age: ${myObject.age}`); console.log(`Location: ${myObject.loc}`); } // Name: My cool name Age: My cool age Location: undefined
Jika mengetik tidak digunakan saat membuat objek
myFlashObject
, dalam kasus kami, sistem mengasumsikan bahwa nilai properti
loc
dari
myFlashObject
tidak
undefined
. Dia tidak berpikir bahwa
loc
mungkin nama properti yang tidak valid.
Jika mengetik digunakan ketika menggambarkan objek
myFlashObject
, maka dalam situasi yang sama kita akan melihat, ketika menyusun kode, pesan kesalahan yang luar biasa:
type FlashObject = { name: string, age: string, location: string } public ngOnInit (): void { let myFlashObject: FlashObject = { name: 'My cool name', age: 'My cool age', // Type '{ name: string; age: string; loc: string; }' is not assignable to type 'FlashObjectType'. Object literal may only specify known properties, and 'loc' does not exist in type 'FlashObjectType'. loc: 'My cool location' } this.processObject(myFlashObject); } public processObject(myObject: FlashObject): void { console.log(`Name: ${myObject.name}`); console.log(`Age: ${myObject.age}`) // Property 'loc' does not exist on type 'FlashObjectType'. console.log(`Location: ${myObject.loc}`); }
Jika Anda mulai bekerja pada proyek baru, akan berguna untuk mengatur, dalam file
tsconfig.json
, opsi
strict:true
untuk memungkinkan pemeriksaan tipe yang ketat.
11. Tentang penggunaan linter
Tslint memiliki berbagai aturan standar seperti
no-any ,
no-magic-number ,
no-console . Linter dapat dikustomisasi dengan mengedit file
tslint.json
untuk mengatur verifikasi kode sesuai dengan aturan tertentu.
β Penjelasan
Menggunakan linter untuk memeriksa kode berarti bahwa jika sesuatu muncul dalam kode yang dilarang oleh aturan, Anda akan menerima pesan kesalahan. Ini berkontribusi pada keseragaman kode proyek, meningkatkan keterbacaannya. Lihat aturan tslint lainnya di sini.
Perlu dicatat bahwa beberapa aturan termasuk cara mengoreksi apa yang dianggap tidak dapat diterima sesuai dengan mereka. Jika perlu, Anda bisa membuat aturan sendiri. Jika Anda tertarik, lihat materi
ini , yang membahas pembuatan aturan khusus untuk linter menggunakan
TSQuery .
βTo
public ngOnInit (): void { console.log('I am a naughty console log message'); console.warn('I am a naughty console warning message'); console.error('I am a naughty console error message'); }
FterSetelah itu
// tslint.json { "rules": { ....... "no-console": [ true, "log", // console.log "warn" // console.warn ] } } // ..component.ts public ngOnInit (): void { console.log('I am a naughty console log message'); console.warn('I am a naughty console warning message'); console.error('I am a naughty console error message'); } // . console.log and console.warn console.error, Calls to 'console.log' are not allowed. Calls to 'console.warn' are not allowed.
Ringkasan
Hari ini kami meninjau 11 rekomendasi yang kami harap akan bermanfaat bagi pengembang Angular. Lain kali, tunggu 11 tips lagi.
Pembaca yang budiman! Apakah Anda menggunakan kerangka kerja Sudut untuk mengembangkan proyek web?
