Komponen React yang Efisien: Panduan untuk Mengoptimalkan Performa React

Diterbitkan: 2022-03-11

Sejak diperkenalkan, React telah mengubah cara berpikir pengembang front-end dalam membangun aplikasi web. Dengan DOM virtual, React membuat pembaruan UI seefisien mungkin, membuat aplikasi web Anda lebih cepat. Tapi, mengapa aplikasi web React berukuran sedang masih cenderung berkinerja buruk?

Nah, petunjuknya adalah bagaimana Anda menggunakan React.

Pustaka front-end modern seperti React tidak secara ajaib membuat aplikasi Anda lebih cepat. Ini membutuhkan pengembang untuk memahami cara kerja React dan bagaimana komponen hidup melalui berbagai fase siklus hidup komponen.

Dengan React, Anda bisa mendapatkan banyak peningkatan kinerja yang ditawarkan dengan mengukur dan mengoptimalkan bagaimana dan kapan komponen Anda dirender. Dan, React hanya menyediakan alat dan fungsi yang diperlukan untuk membuatnya mudah.

Percepat aplikasi React Anda dengan mengoptimalkan proses render-diff komponen Anda.

Dalam tutorial React ini, Anda akan belajar bagaimana Anda dapat mengukur kinerja komponen React Anda dan mengoptimalkannya untuk membangun aplikasi web React yang jauh lebih berkinerja. Anda juga akan mempelajari bagaimana beberapa praktik terbaik JavaScript juga membantu dalam membuat aplikasi web React Anda memberikan pengalaman pengguna yang jauh lebih lancar.

Bagaimana React Bekerja?

Sebelum kita menyelami teknik optimasi, kita perlu memiliki pemahaman yang lebih baik tentang cara kerja React.

Inti dari pengembangan React, Anda memiliki sintaks JSX yang sederhana dan jelas, dan kemampuan React untuk membangun dan membandingkan DOM virtual. Sejak dirilis, React telah mempengaruhi banyak perpustakaan front-end lainnya. Perpustakaan seperti Vue.js juga mengandalkan ide DOM virtual.

Inilah cara kerja React:

Setiap aplikasi React dimulai dengan komponen root, dan terdiri dari banyak komponen dalam formasi pohon. Komponen dalam React adalah "fungsi" yang merender UI berdasarkan data (props dan state) yang diterimanya.

Kita dapat melambangkan ini sebagai F .

 UI = F(data)

Pengguna berinteraksi dengan UI dan menyebabkan data berubah. Apakah interaksi melibatkan mengklik tombol, mengetuk gambar, menyeret item daftar di sekitar, AJAX meminta memanggil API, dll., semua interaksi itu hanya mengubah data. Mereka tidak pernah menyebabkan UI berubah secara langsung.

Di sini, data adalah segala sesuatu yang menentukan status aplikasi web, dan bukan hanya apa yang telah Anda simpan di database Anda. Bahkan sedikit status front-end (misalnya, tab mana yang saat ini dipilih atau apakah kotak centang saat ini dicentang) adalah bagian dari data ini.

Setiap kali ada perubahan dalam data ini, React menggunakan fungsi komponen untuk merender ulang UI, tetapi hanya secara virtual:

 UI1 = F(data1) UI2 = F(data2)

React menghitung perbedaan antara UI saat ini dan UI baru dengan menerapkan algoritme perbandingan pada dua versi DOM virtualnya.

 Changes = Diff(UI1, UI2)

React kemudian melanjutkan untuk menerapkan hanya perubahan UI ke UI asli di browser.

Ketika data yang terkait dengan perubahan komponen, React menentukan apakah pembaruan DOM yang sebenarnya diperlukan. Ini memungkinkan React untuk menghindari operasi manipulasi DOM yang berpotensi mahal di browser, seperti membuat node DOM dan mengakses yang sudah ada di luar kebutuhan.

Diff dan rendering komponen yang berulang ini dapat menjadi salah satu sumber utama masalah kinerja React di aplikasi React apa pun. Membangun aplikasi React di mana algoritme diff gagal untuk merekonsiliasi secara efektif, menyebabkan seluruh aplikasi dirender berulang kali dapat menghasilkan pengalaman yang sangat lambat.

Di mana Mulai Mengoptimalkan?

Tapi apa sebenarnya yang kita optimalkan?

Anda lihat, selama proses render awal, React membangun pohon DOM seperti ini:

DOM virtual dari komponen React

Mengingat sebagian dari perubahan data, apa yang kami ingin React lakukan adalah merender ulang hanya komponen yang terpengaruh secara langsung oleh perubahan (dan bahkan mungkin melewatkan proses diff untuk komponen lainnya):

Bereaksi merender jumlah komponen yang optimal

Namun, apa yang akhirnya dilakukan React adalah:

Bereaksi pemborosan sumber daya dengan merender semua komponen

Pada gambar di atas, semua node kuning dirender dan dibedakan, menghasilkan waktu/sumber daya komputasi yang terbuang. Di sinilah kami terutama akan menempatkan upaya pengoptimalan kami. Mengonfigurasi setiap komponen menjadi hanya render-diff bila diperlukan akan memungkinkan kami untuk mengklaim kembali siklus CPU yang terbuang ini.

Pengembang library React mempertimbangkan hal ini dan menyediakan pengait bagi kami untuk melakukan hal itu: sebuah fungsi yang memungkinkan kami memberi tahu React kapan boleh melewatkan rendering komponen.

Mengukur Pertama

Seperti yang dikatakan Rob Pike dengan agak elegan sebagai salah satu aturan pemrogramannya:

Ukuran. Jangan menyetel kecepatan sampai Anda mengukurnya, dan meskipun demikian, jangan lakukan kecuali satu bagian kode menguasai bagian lainnya.

Jangan mulai mengoptimalkan kode yang menurut Anda dapat memperlambat aplikasi Anda. Sebagai gantinya, biarkan alat pengukur kinerja React memandu Anda.

React memiliki alat yang ampuh hanya untuk ini. Dengan menggunakan pustaka react-addons-perf Anda bisa mendapatkan gambaran umum tentang kinerja keseluruhan aplikasi Anda.

Penggunaannya sangat sederhana:

 Import Perf from 'react-addons-perf' Perf.start(); // use the app Perf.stop(); Perf.printWasted();

Ini akan mencetak tabel dengan jumlah komponen waktu yang terbuang dalam rendering.

Tabel komponen yang membuang waktu dalam rendering

Pustaka menyediakan fungsi lain yang memungkinkan Anda mencetak aspek berbeda dari waktu yang terbuang secara terpisah (misalnya, menggunakan fungsi printInclusive() atau printExclusive() ), atau bahkan mencetak operasi manipulasi DOM (menggunakan fungsi printOperations() ).

Mengambil Benchmarking Selangkah Lebih Jauh

Jika Anda adalah orang yang visual, maka react-perf-tool adalah hal yang Anda butuhkan.

react-perf-tool didasarkan pada perpustakaan react-addons-perf . Ini memberi Anda cara yang lebih visual untuk men-debug kinerja aplikasi React Anda. Ini menggunakan perpustakaan yang mendasarinya untuk mendapatkan pengukuran dan kemudian memvisualisasikannya sebagai grafik.

Visualisasi komponen yang membuang waktu dalam rendering

Lebih sering daripada tidak, ini adalah cara yang jauh lebih nyaman untuk menemukan kemacetan. Anda dapat menggunakannya dengan mudah dengan menambahkannya sebagai komponen ke aplikasi Anda.

Haruskah Bereaksi Memperbarui Komponen?

Secara default, React akan menjalankan, merender DOM virtual, dan membandingkan perbedaan untuk setiap komponen di pohon untuk setiap perubahan pada properti atau statusnya. Tapi itu jelas tidak masuk akal.

Seiring pertumbuhan aplikasi Anda, upaya untuk merender ulang dan membandingkan seluruh DOM virtual di setiap tindakan pada akhirnya akan melambat.

React menyediakan cara sederhana bagi pengembang untuk menunjukkan jika suatu komponen perlu dirender ulang. Di sinilah metode shouldComponentUpdate berperan.

 function shouldComponentUpdate(nextProps, nextState) { return true; }

Ketika fungsi ini mengembalikan nilai true untuk komponen apa pun, ini memungkinkan proses render-diff dipicu.

Ini memberi Anda cara sederhana untuk mengontrol proses render-diff. Kapan pun Anda perlu mencegah komponen dirender ulang sama sekali, cukup kembalikan false dari fungsi tersebut. Di dalam fungsi, Anda dapat membandingkan set alat peraga dan status saat ini dan berikutnya untuk menentukan apakah diperlukan rendering ulang:

 function shouldComponentUpdate(nextProps, nextState) { return nextProps.id !== this.props.id; }

Menggunakan React.PureComponent

Untuk memudahkan dan mengotomatisasi sedikit teknik pengoptimalan ini, React menyediakan apa yang dikenal sebagai komponen "murni". React.PureComponent persis seperti React.Component yang mengimplementasikan fungsi shouldComponentUpdate() dengan prop dangkal dan perbandingan status.

React.PureComponent kurang lebih setara dengan ini:

 class MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this.props, nextProps) && shallowCompare(this.state, nextState); } … }

Karena hanya melakukan perbandingan dangkal, Anda mungkin merasa berguna hanya ketika:

  • Alat peraga atau status Anda berisi data primitif.
  • Alat peraga dan status Anda memiliki data yang kompleks, tetapi Anda tahu kapan harus memanggil forceUpdate() untuk memperbarui komponen Anda.

Membuat Data Tidak Berubah

Bagaimana jika Anda dapat menggunakan React.PureComponent tetapi masih memiliki cara yang efisien untuk mengetahui kapan properti atau status kompleks telah berubah secara otomatis? Di sinilah struktur data yang tidak dapat diubah membuat hidup lebih mudah.

Gagasan di balik penggunaan struktur data yang tidak dapat diubah itu sederhana. Setiap kali objek yang berisi data kompleks berubah, alih-alih membuat perubahan pada objek itu, buat salinan objek itu dengan perubahannya. Ini membuat pendeteksian perubahan data semudah membandingkan referensi dari dua objek.

Anda dapat menggunakan Object.assign atau _.extend (dari Underscore.js atau Lodash):

 const newValue2 = Object.assign({}, oldValue); const newValue2 = _.extend({}, oldValue);

Lebih baik lagi, Anda dapat menggunakan pustaka yang menyediakan struktur data yang tidak dapat diubah:

 var map1 = Immutable.Map({a:1, b:2, c:3}); var map2 = map1.set('b', 2); assert(map1.equals(map2) === true); var map3 = map1.set('b', 50); assert(map1.equals(map3) === false);

Di sini, Immutable.Map disediakan oleh library Immutable.js.

Setiap kali peta diperbarui dengan set metodenya, peta baru dikembalikan hanya jika operasi yang ditetapkan mengubah nilai dasarnya. Jika tidak, peta yang sama dikembalikan.

Anda dapat mempelajari lebih lanjut tentang menggunakan struktur data yang tidak dapat diubah di sini.

Lebih Banyak Teknik Pengoptimalan Aplikasi Bereaksi

Menggunakan Build Produksi

Saat mengembangkan aplikasi React, Anda akan diberikan peringatan dan pesan kesalahan yang sangat berguna. Ini membuat mengidentifikasi bug dan masalah selama pengembangan menjadi suatu kebahagiaan. Tapi mereka datang dengan biaya kinerja.

Jika Anda melihat kode sumber React, Anda akan melihat banyak pemeriksaan if (process.env.NODE_ENV != 'production') . Potongan kode yang dijalankan React di lingkungan pengembangan Anda bukanlah sesuatu yang dibutuhkan oleh pengguna akhir. Untuk lingkungan produksi, semua kode yang tidak perlu ini dapat dibuang.

Jika Anda mem-bootstrap proyek Anda menggunakan create-react-app , maka Anda cukup menjalankan npm run build untuk menghasilkan build produksi tanpa kode tambahan ini. Jika Anda menggunakan Webpack secara langsung, Anda dapat menjalankan webpack -p (yang setara dengan webpack --optimize-minimize --define process.env.NODE_ENV="'production'" .

Fungsi Binding Dini

Sangat umum untuk melihat fungsi terikat pada konteks komponen di dalam fungsi render. Ini sering terjadi ketika kita menggunakan fungsi ini untuk menangani kejadian komponen anak.

 // Creates a new `handleUpload` function during each render() <TopBar onUpload={this.handleUpload.bind(this)} /> // ...as do inlined arrow functions <TopBar onUpload={files => this.handleUpload(files)} />

Ini akan menyebabkan fungsi render() membuat fungsi baru pada setiap render. Cara yang jauh lebih baik untuk melakukan hal yang sama adalah:

 class App extends React.Component { constructor(props) { super(props); this.handleUpload = this.handleUpload.bind(this); } render() { … <TopBar onUpload={this.handleUpload} /> … } }

Menggunakan Beberapa File Potongan

Untuk aplikasi web React satu halaman, kami sering kali akhirnya menggabungkan semua kode JavaScript front-end kami dalam satu file yang diperkecil. Ini berfungsi dengan baik untuk aplikasi web berukuran kecil hingga sedang. Namun saat aplikasi mulai berkembang, mengirimkan file JavaScript yang dibundel ini ke browser itu sendiri bisa menjadi proses yang memakan waktu.

Jika Anda menggunakan Webpack untuk membangun aplikasi React Anda, Anda dapat memanfaatkan kemampuan pemecahan kodenya untuk memisahkan kode aplikasi yang Anda buat menjadi beberapa "potongan" dan mengirimkannya ke browser sesuai kebutuhan.

Ada dua jenis pemisahan: pemisahan sumber daya dan pemisahan kode sesuai permintaan.

Dengan pemisahan sumber daya, Anda membagi konten sumber daya menjadi beberapa file. Misalnya, menggunakan CommonsChunkPlugin, Anda dapat mengekstrak kode umum (seperti semua perpustakaan eksternal) ke dalam file "potongan" sendiri. Menggunakan ExtractTextWebpackPlugin, Anda dapat mengekstrak semua kode CSS ke dalam file CSS terpisah.

Pemisahan semacam ini akan membantu dalam dua cara. Ini membantu browser untuk men-cache sumber daya yang jarang berubah. Ini juga akan membantu browser untuk memanfaatkan pengunduhan paralel untuk berpotensi mengurangi waktu buka.

Fitur yang lebih menonjol dari Webpack adalah pemecahan kode sesuai permintaan. Anda dapat menggunakannya untuk membagi kode menjadi potongan yang dapat dimuat sesuai permintaan. Ini dapat membuat unduhan awal tetap kecil, mengurangi waktu yang diperlukan untuk memuat aplikasi. Browser kemudian dapat mengunduh potongan kode lain sesuai permintaan saat dibutuhkan oleh aplikasi.

Anda dapat mempelajari lebih lanjut tentang pemecahan kode Webpack di sini.

Mengaktifkan Gzip di Server Web Anda

File JS bundel aplikasi React biasanya berukuran sangat besar, jadi untuk membuat halaman web dimuat lebih cepat, kita dapat mengaktifkan Gzip di server web (Apache, Nginx, dll.)

Semua browser modern mendukung dan secara otomatis menegosiasikan kompresi Gzip untuk permintaan HTTP. Mengaktifkan kompresi Gzip dapat mengurangi ukuran respons yang ditransfer hingga 90%, yang secara signifikan dapat mengurangi jumlah waktu untuk mengunduh sumber daya, mengurangi penggunaan data untuk klien, dan meningkatkan waktu untuk pertama kali merender halaman Anda.

Periksa dokumentasi untuk server web Anda tentang cara mengaktifkan kompresi:

  • Apache: Gunakan mod_deflate
  • Nginx: Gunakan ngx_http_gzip_module

Menggunakan Eslint-plugin-bereaksi

Anda harus menggunakan ESLint untuk hampir semua proyek JavaScript. Bereaksi tidak berbeda.

Dengan eslint-plugin-react , Anda akan memaksa diri Anda untuk beradaptasi dengan banyak aturan dalam pemrograman React yang dapat menguntungkan kode Anda dalam jangka panjang dan menghindari banyak masalah umum dan masalah yang terjadi karena kode yang ditulis dengan buruk.

Jadikan Aplikasi Web React Anda Cepat Lagi

Untuk memaksimalkan React, Anda perlu memanfaatkan alat dan tekniknya. Performa aplikasi web React terletak pada kesederhanaan komponennya. Melebihi algoritme render-diff dapat membuat aplikasi Anda berperforma buruk dengan cara yang membuat frustrasi.

Sebelum Anda dapat mengoptimalkan aplikasi Anda, Anda perlu memahami cara kerja komponen React dan bagaimana komponen tersebut dirender di browser. Metode daur hidup React memberi Anda cara untuk mencegah komponen Anda dirender ulang secara tidak perlu. Hilangkan hambatan tersebut dan Anda akan mendapatkan kinerja aplikasi yang layak diterima pengguna Anda.

Meskipun ada lebih banyak cara untuk mengoptimalkan aplikasi web React, menyetel komponen untuk diperbarui hanya jika diperlukan menghasilkan peningkatan kinerja terbaik.

Bagaimana Anda mengukur dan mengoptimalkan kinerja aplikasi web React Anda? Bagikan di komentar di bawah.

Terkait: Pengambilan Data Basi-sementara-validasi ulang dengan React Hooks: Panduan