Meniru React dan JSX di Vanilla JS
Diterbitkan: 2022-03-11Hanya sedikit orang yang tidak menyukai kerangka kerja, tetapi bahkan jika Anda salah satunya, Anda harus memperhatikan dan mengadopsi fitur-fitur yang membuat hidup sedikit lebih mudah.
Saya menentang penggunaan kerangka kerja di masa lalu. Namun, akhir-akhir ini, saya memiliki pengalaman bekerja dengan React dan Angular di beberapa proyek saya. Beberapa kali pertama saya membuka editor kode saya dan mulai menulis kode di Angular rasanya aneh dan tidak wajar; terutama setelah lebih dari sepuluh tahun pengkodean tanpa menggunakan kerangka kerja apa pun. Setelah beberapa saat, saya memutuskan untuk berkomitmen untuk mempelajari teknologi ini. Sangat cepat satu perbedaan besar menjadi jelas: sangat mudah untuk memanipulasi DOM, begitu mudah untuk menyesuaikan urutan node bila diperlukan, dan tidak perlu halaman dan halaman kode untuk membangun UI.
Meskipun saya masih lebih suka kebebasan untuk tidak terikat pada kerangka kerja atau arsitektur, saya tidak dapat mengabaikan fakta bahwa membuat elemen DOM dalam satu elemen jauh lebih nyaman. Jadi saya mulai mencari cara untuk meniru pengalaman di Vanilla JS. Tujuan saya adalah mengekstrak beberapa ide tersebut dari React dan mendemonstrasikan bagaimana prinsip yang sama dapat diterapkan dalam JavaScript biasa (sering disebut sebagai vanilla JS) untuk membuat hidup pengembang sedikit lebih mudah. Untuk mencapai ini, mari buat aplikasi sederhana untuk menjelajahi proyek GitHub.
Apapun cara kita membangun front end menggunakan JavaScript, kita akan mengakses dan memanipulasi DOM. Untuk aplikasi kita, kita perlu membuat representasi dari setiap repositori (thumbnail, nama, dan deskripsi), dan menambahkannya ke DOM sebagai elemen daftar. Kami akan menggunakan API Pencarian GitHub untuk mengambil hasil kami. Dan, karena kita berbicara tentang JavaScript, mari kita cari di repositori JavaScript. Saat kami meminta API, kami mendapatkan respons JSON berikut:
{ "total_count": 398819, "incomplete_results": false, "items": [ { "id": 28457823, "name": "freeCodeCamp", "full_name": "freeCodeCamp/freeCodeCamp", "owner": { "login": "freeCodeCamp", "id": 9892522, "avatar_url": "https://avatars0.githubusercontent.com/u/9892522?v=4", "gravatar_id": "", "url": "https://api.github.com/users/freeCodeCamp", "site_admin": false }, "private": false, "html_url": "https://github.com/freeCodeCamp/freeCodeCamp", "description": "The https://freeCodeCamp.org open source codebase "+ "and curriculum. Learn to code and help nonprofits.", // more omitted information }, //... ] }
Pendekatan reaksi
React membuatnya sangat mudah untuk menulis elemen HTML ke dalam halaman dan merupakan salah satu fitur yang selalu ingin saya miliki saat menulis komponen dalam JavaScript murni. React menggunakan JSX, yang sangat mirip dengan HTML biasa.
Namun, bukan itu yang dibaca browser.
Di bawah tenda, React mengubah JSX menjadi sekelompok panggilan ke fungsi React.createElement
. Mari kita lihat contoh BEJ menggunakan satu item dari GitHub API dan lihat terjemahannya.
<div className="repository"> <div>{item.name}</div> <p>{item.description}</p> <img src={item.owner.avatar_url} /> </div>;
; React.createElement( "div", { className: "repository" }, React.createElement( "div", null, item.name ), React.createElement( "p", null, item.description ), React.createElement( "img", { src: item.owner.avatar_url } ) );
BEJ sangat sederhana. Anda menulis kode HTML biasa dan menyuntikkan data dari objek dengan menambahkan tanda kurung kurawal. Kode JavaScript di dalam tanda kurung akan dieksekusi, dan nilainya akan dimasukkan ke dalam DOM yang dihasilkan. Salah satu keuntungan JSX adalah React membuat DOM virtual (representasi virtual halaman) untuk melacak perubahan dan pembaruan. Alih-alih menulis ulang seluruh HTML, React memodifikasi DOM halaman setiap kali informasi diperbarui. Ini adalah salah satu masalah utama yang harus dipecahkan oleh React.
pendekatan jQuery
Pengembang sering menggunakan jQuery. Saya ingin menyebutkannya di sini karena masih populer dan juga karena cukup dekat dengan solusi dalam JavaScript murni. jQuery mendapatkan referensi ke simpul DOM (atau kumpulan simpul DOM) dengan menanyakan DOM. Itu juga membungkus referensi itu dengan berbagai fungsi untuk memodifikasi isinya.
Sementara jQuery memiliki alat konstruksi DOM sendiri, hal yang paling sering saya lihat di alam liar hanyalah penggabungan HTML. Misalnya, kita dapat memasukkan kode HTML ke dalam node yang dipilih dengan memanggil fungsi html()
. Menurut dokumentasi jQuery, jika kita ingin mengubah konten simpul div
dengan demo-container
kelas, kita dapat melakukannya seperti ini:
$( "div.demo-container" ).html( "<p>All new content.<em>You bet!</em></p>" );
Pendekatan ini memudahkan untuk membuat elemen DOM; namun, saat kita perlu memperbarui node, kita perlu mengkueri node yang kita butuhkan atau (lebih umum) kembali untuk membuat ulang seluruh cuplikan setiap kali pembaruan diperlukan.
Pendekatan DOM API
JavaScript di browser memiliki DOM API bawaan yang memberi kita akses langsung untuk membuat, memodifikasi, dan menghapus node di halaman. Ini tercermin dalam pendekatan React, dan dengan menggunakan DOM API, kita selangkah lebih dekat ke manfaat dari pendekatan itu. Kami hanya memodifikasi elemen halaman yang benar-benar perlu diubah. Namun, React juga melacak DOM virtual yang terpisah. Dengan membandingkan perbedaan antara DOM virtual dan aktual, React kemudian dapat mengidentifikasi bagian mana yang memerlukan modifikasi.
Langkah-langkah tambahan itu terkadang berguna, tetapi tidak selalu, dan memanipulasi DOM secara langsung bisa lebih efisien. Kita dapat membuat node DOM baru menggunakan fungsi _document.createElement_
, yang akan mengembalikan referensi ke node yang dibuat. Melacak referensi ini memberi kita cara mudah untuk memodifikasi hanya node yang berisi bagian yang perlu diperbarui.
Dengan menggunakan struktur dan sumber data yang sama seperti pada contoh BEJ, kita dapat membuat DOM dengan cara berikut:
var item = document.createElement('div'); item.className = 'repository'; var nameNode = document.createElement('div'); nameNode.innerHTML = item.name item.appendChild(nameNode); var description = document.createElement('p'); description.innerHTML = item.description; item.appendChild(description ); var image = new Image(); Image.src = item.owner.avatar_url; item.appendChild(image); document.body.appendChild(item);
Jika satu-satunya hal yang Anda pikirkan adalah efisiensi eksekusi kode, maka pendekatan ini sangat bagus. Namun, efisiensi tidak diukur hanya dalam kecepatan eksekusi, tetapi juga dalam kemudahan perawatan, skalabilitas, dan plastisitas. Masalah dengan pendekatan ini adalah sangat bertele-tele dan terkadang berbelit-belit. Kita perlu menulis banyak pemanggilan fungsi bahkan jika kita hanya membangun struktur dasar. Kerugian besar kedua adalah banyaknya variabel yang dibuat dan dilacak. Katakanlah, sebuah komponen yang Anda kerjakan berisi 30 elemen DOM-nya sendiri, Anda harus membuat dan menggunakan 30 elemen dan variabel DOM yang berbeda. Anda dapat menggunakan kembali beberapa dari mereka dan melakukan beberapa juggling dengan mengorbankan pemeliharaan dan plastisitas, tetapi itu bisa menjadi sangat berantakan, sangat cepat.

Kerugian signifikan lainnya adalah karena jumlah baris kode yang perlu Anda tulis. Seiring waktu, semakin sulit untuk memindahkan elemen dari satu induk ke induk lainnya. Itu satu hal yang sangat saya hargai dari React. Saya dapat melihat sintaks JSX dan mendapatkan dalam beberapa detik simpul mana yang terdapat, di mana, dan ubah jika diperlukan. Dan, meskipun mungkin tampak seperti bukan masalah besar pada awalnya, sebagian besar proyek memiliki perubahan konstan yang akan membuat Anda mencari cara yang lebih baik.
Solusi yang Diusulkan
Bekerja langsung dengan DOM berfungsi dan menyelesaikan pekerjaan, tetapi juga membuat pembuatan halaman menjadi sangat bertele-tele, terutama ketika kita perlu menambahkan atribut HTML dan node sarang. Jadi, idenya adalah untuk menangkap beberapa manfaat dari bekerja dengan teknologi seperti BEJ dan membuat hidup kita lebih sederhana. Keuntungan yang kami coba tiru adalah sebagai berikut:
- Tulis kode dalam sintaks HTML sehingga pembuatan elemen DOM menjadi mudah dibaca dan dimodifikasi.
- Karena kita tidak menggunakan DOM virtual yang setara seperti dalam kasus React, kita perlu memiliki cara mudah untuk menunjukkan dan melacak node yang kita minati.
Berikut adalah fungsi sederhana yang akan melakukannya menggunakan cuplikan HTML.
Browser.DOM = function (html, scope) { // Creates empty node and injects html string using .innerHTML // in case the variable isn't a string we assume is already a node var node; if (html.constructor === String) { var node = document.createElement('div'); node.innerHTML = html; } else { node = html; } // Creates of uses and object to which we will create variables // that will point to the created nodes var _scope = scope || {}; // Recursive function that will read every node and when a node // contains the var attribute add a reference in the scope object function toScope(node, scope) { var children = node.children; for (var iChild = 0; iChild < children.length; iChild++) { if (children[iChild].getAttribute('var')) { var names = children[iChild].getAttribute('var').split('.'); var obj = scope; while (names.length > 0) { var _property = names.shift(); if (names.length == 0) { obj[_property] = children[iChild]; } else { if (!obj.hasOwnProperty(_property)){ obj[_property] = {}; } obj = obj[_property]; } } } toScope(children[iChild], scope); } } toScope(node, _scope); if (html.constructor != String) { return html; } // If the node in the highest hierarchy is one return it if (node.childNodes.length == 1) { // if a scope to add node variables is not set // attach the object we created into the highest hierarchy node // by adding the nodes property. if (!scope) { node.childNodes[0].nodes = _scope; } return node.childNodes[0]; } // if the node in highest hierarchy is more than one return a fragment var fragment = document.createDocumentFragment(); var children = node.childNodes; // add notes into DocumentFragment while (children.length > 0) { if (fragment.append){ fragment.append(children[0]); }else{ fragment.appendChild(children[0]); } } fragment.nodes = _scope; return fragment; }
Idenya sederhana namun kuat; kami mengirim fungsi HTML yang ingin kami buat sebagai string, dalam string HTML kami menambahkan atribut var ke node yang ingin kami buatkan referensi untuk kami. Parameter kedua adalah objek di mana referensi tersebut akan disimpan. Jika tidak ditentukan, kami akan membuat properti "node" pada node yang dikembalikan atau fragmen dokumen (jika node hierarki tertinggi lebih dari satu). Semuanya dilakukan dalam waktu kurang dari 60 baris kode.
Fungsi ini bekerja dalam tiga langkah:
- Buat simpul kosong baru dan gunakan innerHTML di simpul baru itu untuk membuat seluruh struktur DOM.
- Ulangi node dan jika atribut var ada, tambahkan properti di objek lingkup yang menunjuk ke node dengan atribut itu.
- Kembalikan simpul teratas dalam hierarki, atau fragmen dokumen jika ada lebih dari satu.
Jadi bagaimana tampilan kode kita untuk merender contoh sekarang?
var UI = {}; var template = ''; template += '<div class="repository">' template += ' <div var="name"></div>'; template += ' <p var="text"></p>' template += ' <img var="image"/>' template += '</div>'; var item = Browser.DOM(template, UI); UI.name.innerHTML = data.name; UI.text.innerHTML = data.description; UI.image.src = data.owner.avatar_url;
Pertama, kita mendefinisikan objek (UI) di mana kita akan menyimpan referensi ke node yang dibuat. Kami kemudian membuat template HTML yang akan kami gunakan, sebagai string, menandai node target dengan atribut "var". Setelah itu, kita memanggil fungsi Browser.DOM dengan template dan objek kosong yang akan menyimpan referensi. Akhirnya, kami menggunakan referensi yang disimpan untuk menempatkan data di dalam node.
Pendekatan ini juga memisahkan membangun struktur DOM dan memasukkan data ke dalam langkah-langkah terpisah yang membantu menjaga kode tetap teratur dan terstruktur dengan baik. Ini memungkinkan kami untuk membuat struktur DOM secara terpisah dan mengisi (atau memperbarui) data saat tersedia.
Kesimpulan
Sementara beberapa dari kita tidak menyukai gagasan untuk beralih ke kerangka kerja dan menyerahkan kendali, penting bagi kita untuk mengenali manfaat yang dibawa oleh kerangka kerja tersebut. Ada alasan mengapa mereka begitu populer.
Dan sementara kerangka kerja mungkin tidak selalu sesuai dengan gaya atau kebutuhan Anda, beberapa fungsi dan teknik dapat diadopsi, ditiru, atau terkadang bahkan dipisahkan dari kerangka kerja. Beberapa hal akan selalu hilang dalam terjemahan, tetapi banyak yang bisa diperoleh dan digunakan dengan biaya yang sangat kecil dari kerangka kerja.