Hari ini kami menerbitkan terjemahan materi, yang penulisnya, setelah menganalisis fitur bekerja dengan objek dalam JavaScript, menawarkan pengembang Bereaksi metodologi untuk mempercepat aplikasi. Secara khusus, kita berbicara tentang fakta bahwa variabel, yang, seperti yang mereka katakan, "ditugaskan objek", dan yang sering disebut hanya "objek", pada kenyataannya, tidak menyimpan objek itu sendiri, tetapi tautan ke sana. Fungsi dalam JavaScript juga objek, jadi hal di atas berlaku untuk mereka. Dengan mengingat hal ini, merancang komponen Bereaksi dan menganalisis secara kritis kode mereka dapat meningkatkan mekanisme internal dan meningkatkan kinerja aplikasi.
Fitur bekerja dengan objek dalam JavaScript
Jika Anda membuat beberapa fungsi yang terlihat persis sama dan mencoba membandingkannya, ternyata mereka, dari sudut pandang sistem, berbeda. Untuk memverifikasi ini, Anda dapat menjalankan kode berikut:
const functionOne = function() { alert('Hello world!'); }; const functionTwo = function() { alert('Hello world!'); }; functionOne === functionTwo;
Sekarang mari kita coba untuk menetapkan variabel ke fungsi yang sudah ada yang ditugaskan ke variabel lain, dan membandingkan dua variabel ini:
const functionThree = function() { alert('Hello world!'); }; const functionFour = functionThree; functionThree === functionFour;
Seperti yang Anda lihat, dengan pendekatan ini, operator kesetaraan yang ketat mengembalikan
true
.
Objek secara alami berperilaku dengan cara yang sama:
const object1 = {}; const object2 = {}; const object3 = object1; object1 === object2; // false object1 === object3; // true
Di sini kita berbicara tentang JavaScript, tetapi jika Anda memiliki pengalaman mengembangkan dalam bahasa lain, maka Anda mungkin akrab dengan konsep pointer. Dalam kode di atas, setiap kali sebuah objek dibuat, sebagian dari memori sistem dialokasikan untuk itu. Ketika kita menggunakan perintah form
object1 = {}
, ini mengarah ke mengisi dengan beberapa data sepotong memori yang dialokasikan khusus untuk
object1
.
Sangat mungkin untuk membayangkan
object1
sebagai alamat di mana struktur data yang terkait dengan objek berada di memori. Eksekusi perintah
object2 = {}
mengarah ke alokasi area memori lain, yang dirancang khusus untuk
object2
. Apakah
obect1
dan
object2
di area memori yang sama? Tidak, masing-masing memiliki plot sendiri. Itulah sebabnya ketika kita mencoba membandingkan
object1
dan
object2
kita mendapatkan
false
. Objek-objek ini mungkin memiliki struktur yang identik, tetapi alamat dalam memori tempat mereka berada berbeda, dan itu adalah alamat yang diperiksa selama perbandingan.
Dengan mengeksekusi perintah
object3 = object1
, kita menulis alamat
object1
ke dalam
object1
konstan. Ini bukan objek baru. Konstanta ini diberikan alamat objek yang ada. Anda dapat memverifikasi ini dengan:
const object1 = { x: true }; const object3 = object1; object3.x = false; object1.x;
Dalam contoh ini, sebuah objek dibuat dalam memori dan alamatnya ditulis ke objek
object1
. Kemudian alamat yang sama ditulis ke objek
object3
. Mengubah
object3
mengubah objek dalam memori. Ini berarti bahwa ketika mengakses objek menggunakan referensi lain untuk itu, misalnya, yang disimpan di
object1
, kita sudah akan bekerja dengan versi yang dimodifikasi.
Fungsi, Objek, dan Bereaksi
Kesalahpahaman tentang mekanisme di atas oleh pengembang pemula sering menyebabkan kesalahan, dan, mungkin, pertimbangan fitur bekerja dengan objek layak untuk artikel yang terpisah. Namun, topik kita hari ini adalah kinerja aplikasi Bereaksi. Di bidang ini, kesalahan dapat dilakukan bahkan oleh pengembang yang cukup berpengalaman yang tidak memperhatikan bagaimana Bereaksi aplikasi dipengaruhi oleh fakta bahwa variabel JavaScript dan konstanta tidak disimpan dalam objek itu sendiri, tetapi hanya tautan ke mereka.
Apa hubungannya ini dengan Bereaksi? Bereaksi memiliki mekanisme cerdas untuk menghemat sumber daya sistem yang ditujukan untuk meningkatkan kinerja aplikasi: jika properti dan keadaan komponen tidak berubah, maka apa fungsi
render
tidak berubah. Jelas, jika komponennya tetap sama, tidak perlu dirender lagi. Jika tidak ada yang berubah, fungsi
render
akan kembali sama seperti sebelumnya, jadi tidak perlu untuk menjalankannya. Mekanisme ini membuat Bereaksi cepat. Sesuatu ditampilkan hanya jika perlu.
React memeriksa properti dan status komponen untuk kesetaraan menggunakan fitur JavaScript standar, yaitu, ia hanya membandingkannya menggunakan operator
==
. Bereaksi tidak melakukan perbandingan objek yang "dangkal" atau "dalam" untuk menentukan kesetaraannya. "Perbandingan dangkal" adalah konsep yang digunakan untuk menggambarkan perbandingan dari setiap pasangan kunci-nilai suatu objek, yang bertentangan dengan perbandingan di mana hanya alamat objek dalam memori yang dibandingkan (referensi untuk mereka). Perbandingan "dalam" objek bahkan lebih jauh, dan jika nilai-nilai properti yang dibandingkan dari objek juga objek, mereka juga membandingkan pasangan kunci-nilai dari objek-objek ini. Proses ini diulangi untuk semua objek yang bersarang di objek lain. Bereaksi tidak melakukan hal semacam itu, hanya memeriksa kesetaraan tautan.
Jika, misalnya, Anda mengubah properti komponen yang diwakili oleh objek bentuk
{ x: 1 }
ke objek lain yang terlihat persis sama, Bereaksi akan merender ulang komponen, karena objek ini berada di area memori yang berbeda. Jika Anda mengingat contoh di atas, maka, ketika mengubah properti komponen dari
object1
ke
object1
, Bereaksi tidak akan merender ulang komponen seperti itu, karena konstanta
object1
dan
object1
merujuk ke objek yang sama.
Bekerja dengan fungsi-fungsi dalam JavaScript diatur dengan cara yang persis sama. Jika Bereaksi bertemu dengan fitur yang sama yang alamatnya berbeda, itu akan merender ulang. Jika "fungsi baru" hanyalah tautan ke fungsi yang telah digunakan, tidak akan ada rendering ulang.
Masalah khas saat bekerja dengan komponen
Berikut adalah salah satu skenario bekerja dengan komponen, yang, sayangnya, terus menerus muncul ketika saya memeriksa kode orang lain:
class SomeComponent extends React.PureComponent { get instructions() { if (this.props.do) { return 'Click the button: '; } return 'Do NOT click the button: '; } render() { return ( <div> {this.instructions} <Button onClick={() => alert('!')} /> </div> ); } }
Di depan kita adalah komponen yang sangat sederhana. Ini adalah tombol, ketika diklik, pemberitahuan ditampilkan. Di sebelah tombol ditampilkan instruksi untuk penggunaannya, memberi tahu pengguna apakah ia harus menekan tombol ini. Kontrol instruksi mana yang akan ditampilkan dengan mengatur properti
do
(
do={true}
atau
do={false}
)
SomeComponent
.
Setiap kali komponen
SomeComponent
-render ulang (ketika nilai properti
do
diubah dari
true
menjadi
false
dan sebaliknya), elemen
Button
juga dirender. Handler
onClick
, meskipun selalu sama, dibuat kembali setiap kali fungsi
render
dipanggil. Akibatnya, ternyata setiap kali komponen ditampilkan dalam memori, fungsi baru dibuat, karena pembuatannya dilakukan dalam fungsi
render
, tautan ke alamat baru di memori diteruskan ke
<Button />
, dan komponen
Button
juga dirender lagi, meskipun faktanya dalam tidak ada yang berubah sama sekali.
Mari kita bicara tentang cara memperbaikinya.
Pemecahan masalah
Jika fungsi tidak tergantung pada komponen (konteks
this
), maka Anda dapat mendefinisikannya di luar komponen. Semua instance komponen akan menggunakan referensi fungsi yang sama, karena dalam semua kasus itu akan menjadi fungsi yang sama. Begini tampilannya:
const createAlertBox = () => alert('!'); class SomeComponent extends React.PureComponent { get instructions() { if (this.props.do) { return 'Click the button: '; } return 'Do NOT click the button: '; } render() { return ( <div> {this.instructions} <Button onClick={createAlertBox} /> </div> ); } }
Berbeda dengan contoh sebelumnya,
createAlertBox
, dengan setiap panggilan untuk
createAlertBox
, akan berisi tautan yang sama ke area yang sama dalam memori. Akibatnya, output berulang
Button
tidak akan dieksekusi.
Sementara komponen
Button
kecil dan cepat ditampilkan, masalah yang dijelaskan di atas terkait dengan deklarasi fungsi internal juga dapat ditemukan dalam komponen besar dan kompleks yang membutuhkan banyak waktu untuk merendernya. Ini secara signifikan dapat memperlambat aplikasi Bereaksi. Dalam hal ini, masuk akal untuk mengikuti rekomendasi, yang dengannya fungsi-fungsi tersebut tidak boleh dideklarasikan di dalam metode
render
.
Jika fungsi tergantung pada komponen, yaitu, itu tidak dapat didefinisikan di luar itu, metode komponen dapat diteruskan sebagai pengendali peristiwa:
class SomeComponent extends React.PureComponent { createAlertBox = () => { alert(this.props.message); }; get instructions() { if (this.props.do) { return 'Click the button: '; } return 'Do NOT click the button: '; } render() { return ( <div> {this.instructions} <Button onClick={this.createAlertBox} /> </div> ); } }
Dalam hal ini, dalam setiap instance
SomeComponent
ketika Anda mengklik tombol, berbagai pesan akan ditampilkan. Penangan event elemen
Button
harus unik untuk
SomeComponent
. Saat melewati metode
cteateAlertBox
, tidak masalah jika
SomeComponent
-render ulang. Tidak masalah jika properti
message
telah berubah. Alamat fungsi
createAlertBox
tidak berubah, yang berarti bahwa elemen
Button
tidak boleh dirender lagi. Berkat ini, Anda dapat menghemat sumber daya sistem dan meningkatkan kecepatan rendering aplikasi.
Semua ini bagus. Tetapi bagaimana jika fungsinya dinamis?
Memecahkan Masalah yang Lebih Kompleks
Penulis materi ini meminta Anda untuk memperhatikan fakta bahwa ia menyiapkan contoh-contoh di bagian ini, mengambil hal pertama yang muncul di benaknya, cocok untuk menggambarkan penggunaan kembali fungsi. Contoh-contoh ini dimaksudkan untuk membantu pembaca memahami esensi gagasan. Meskipun bagian ini direkomendasikan untuk membaca untuk memahami esensi dari apa yang terjadi, penulis menyarankan untuk memperhatikan komentar pada artikel asli , karena beberapa pembaca telah menyarankan versi yang lebih baik dari mekanisme yang dibahas di sini, yang mempertimbangkan fitur-fitur pembatalan cache dan mekanisme manajemen memori yang dibangun dalam React.Jadi, sangat umum bahwa dalam satu komponen ada banyak penangan acara yang unik dan dinamis, misalnya, sesuatu yang serupa dapat dilihat dalam kode, di mana metode array
map
digunakan dalam metode
render
:
class SomeComponent extends React.PureComponent { render() { return ( <ul> {this.props.list.map(listItem => <li key={listItem.text}> <Button onClick={() => alert(listItem.text)} /> </li> )} </ul> ); } }
Di sini, sejumlah tombol yang berbeda akan ditampilkan dan sejumlah penangan acara yang berbeda akan dibuat, masing-masing diwakili oleh fungsi unik, dan, sebelumnya, ketika membuat
SomeComponent
, tidak diketahui apa fungsi-fungsi ini. Bagaimana cara memecahkan teka-teki ini?
Di sini memoisasi akan membantu kami, atau, lebih sederhana, caching. Untuk setiap nilai unik, buat fungsi dan masukkan ke dalam cache. Jika nilai unik ini terjadi lagi, itu akan cukup untuk mengambil dari cache fungsi yang sesuai dengannya, yang sebelumnya ditempatkan dalam cache.
Beginilah implementasi dari ide ini:
class SomeComponent extends React.PureComponent {
Setiap elemen array diproses oleh metode
getClickHandler
. Metode ini, pertama kali dipanggil dengan nilai tertentu, akan membuat fungsi yang unik untuk nilai ini, memasukkannya ke dalam cache dan mengembalikannya. Semua panggilan selanjutnya ke metode ini, dengan memberikan nilai yang sama dengannya, akan menyebabkannya mengembalikan tautan ke fungsi dari cache.
Akibatnya, rendering ulang
SomeComponent
tidak akan merender kembali
Button
. Demikian pula, menambahkan elemen ke properti
list
akan secara dinamis membuat pengendali acara untuk setiap tombol.
Anda harus kreatif dalam membuat pengidentifikasi unik untuk penangan jika mereka didefinisikan oleh lebih dari satu variabel, tetapi ini tidak jauh lebih rumit daripada pembuatan properti
key
unik untuk setiap objek JSX yang diperoleh sebagai hasil dari metode
map
.
Di sini saya ingin memperingatkan Anda tentang kemungkinan masalah menggunakan indeks array sebagai pengidentifikasi. Faktanya adalah bahwa dengan pendekatan ini, Anda dapat menemukan kesalahan jika urutan elemen dalam array berubah atau beberapa elemennya dihapus. Jadi, misalnya, jika pada awalnya array serupa tampak seperti
[ 'soda', 'pizza' ]
, dan kemudian berubah menjadi
[ 'pizza' ]
, dan Anda membuat cache event handler menggunakan perintah dari
listeners[0] = () => alert('soda')
form
listeners[0] = () => alert('soda')
, Anda akan menemukan bahwa ketika pengguna mengklik tombol di mana penangan dengan pengenal 0 ditugaskan dan yang, sesuai dengan isi array
[ 'pizza' ]
, harus menampilkan pesan
pizza
, pesan
soda
akan ditampilkan. Untuk alasan yang sama, tidak disarankan untuk menggunakan indeks array sebagai properti utama.
Ringkasan
Pada artikel ini, kami memeriksa fitur mekanisme JavaScript internal, dengan mempertimbangkan mana Anda dapat mempercepat rendering aplikasi Bereaksi. Kami berharap ide-ide yang disajikan di sini berguna.
Pembaca yang budiman! Jika Anda mengetahui cara menarik untuk mengoptimalkan Bereaksi aplikasi, silakan bagikan.
