Cara menjadi Frontend Developer (2)

Bagian dua: Experience with front end technologies and/or front end frameworks

Tulisan ini adalah lanjutan dari tulisan Cara menjadi Frontend Developer yang ini, tidak dipaksa untuk membaca tulisan sebelumnya. Bila ingin mengetahui apa yang sedang dibicarakan, silahkan bisa baca tulisan sebelumnya tersebut.

Sebelumnya kita sedang membahas tentang bagaimana cara menjadi seorang Frontend Developer berdasarkan kebutuhan yang ada di lowongan kerja, yang disini berarti Front End Software Engineer di Google. Ditulisan pertama kita membahas sekilas tentang JavaScript, HTML, dan CSS. Di kualifikasi kedua untuk kandidat adalah: Berpengalaman dengan teknologi dan atau framework di frontend. Dan itulah yang akan kita bahas sekarang. Sebelum masuk ke topik utama, izinkan saya berpendapat mengenai framework/library.

Mengapa menggunakan framework/library?

Daripada “ngikut” buta alias ikut-ikutan dalam memilih suatu jalan, mari kita fikirkan tentang mengapa menggunakan framework/library. Agar lebih singkat, disini saya akan menyebutnya framework saja.

Ingat bagaimana kita membuat aplikasi Todo App sederhana ditulisan sebelumnya? Dalam membuat fungsi tambah todo, menampilkan daftar todo, dsb kita menulisnya sendiri dengan tangan kita. Berikut logikanya juga. Apakah efektif? Tentu. Karena kita sendiri yang menulisnya, kita tau fungsionalitasnya seperti apa. Cara kerjanya seperti apa. Bahkan kita langsung ber-interaksi dengan platform.

Sekarang fikirkan.

Bagaimana bila fungsi-fungsi tersebut akan digunakan di cakupan lain juga? Misal seperti kode dibawah yang saya ambil dari tulisan sebelumnya:

function newActivityFactory(activity, position) {
const list = document.createElement('li')
const input = document.createElement('input')
  // ...
}

Bisa saja kita ubah nama methodnya menjadi newElementFactory daripada newActivityFactory yang terlalu eksplisit. Lalu misal jadi seperti ini:

function newActivityFactory (activity, position) {
  const list = newElementFactory({ element: 'li' })
const input = newElementFactory({
element: 'input',
type: 'checkbox',
appendWith: activity
})
}

Terlihat lebih deklaratif, kan? Daripada harus menulis sesuatu yang memiliki behavior sama berkali-kali (dan ini sulit untuk di maintenance) lebih baik kita pecah menjadi fungsi-fungsi yang lebih kecil.

Dan itulah library.

Kita ingin menyelesaikan pekerjaan kita agar lebih efektif, juga efisien. Pada dasarnya framework juga adalah sebuah library, namun framework merubah alur kerja kita. Ambil contoh React, bila sebelumnya kita memanggil fungsi “entrypoint” seperti ini:

document.addEventListener('DOMContentLoaded', renderActivities)

Di React, seperti ini:

ReactDOM.render(<Component />, target)

Yang maksudnya, dikode diawal kita definisikan event yang datang dan fungsi yang akan dipanggil ketika event tersebut datang. Di React, kita definisikan komponen apa yang akan dirender, dan dimana komponen tersebut akan dirender.

Itu contoh saja ya. Misal kita juga buat “callback” di renderActivities tersebut, bila sudah terpanggil (ketika halaman sudah bisa disebut interaktif) maka panggil fungsi setelahnya. Apakah di React juga bisa?

Bisa. Mungkin via lifecycle componentDidMount() kita memanggil fungsi setelahnya tersebut. Bukankah sudah jelas peran framework disini?

Juga, ketika kita ingin membuat element via JavaScript. Kita menggunakan method createElementFactory() . Bila sudut pandang kita dari kacamata library author, kita akan bilang hey sudah ada loh fungsi untuk itu! Lebih efektif, efisien, dan battle-tested :))

Framework apa yang akan kita gunakan?

Berdasarkan dari lowongan kerja tersebut, framework-framework yang disebutkan adalah: AngularJS, Polymer, Closure atau Backbone. Hanya 1 framework yang bukan dari “google”, dan sayangnya framework tersebut sudah ehm tidak lagi modern alias pernah modern pada masanya.

Maka disini kita akan memilih Polymer. Mengapa? Karena AngularJS pasarnya sudah lumayan sepi, atau Closure karena pasar & ekosistemnya kurang, dan Backbone karena alasan yang sudah disebut diatas.

Namun, demi tetap menjaga interaksi kita dengan platform (browser), kita akan menggunakan framework yang lebih lower-level dari Polymer, alias lit-html. Pada dasarnya, Polymer dibuat diatas lit-html. Mengapa menggunakan lit-html?

  1. Menghindari modern tooling di awal
  2. Agar tetap berada di level yang dekat dengan platform
  3. Setiap level abstraksi memiliki cost.

Maksud dari nomor 2 adalah seperti ini. Di React, untuk mengubah UI yang ada dibrowser semudah memanggil setState({ stateYangInginDiUbah: nilaiStateYangBaru }) . Kita tidak perlu ribet memikirkan:

  • Element mana saja nih yang harus diubah?
  • Element ini perlu diubah gak ya?
  • Dsb

Karena pada dasarnya, rumusnya adalah seperti ini: UI = f(state) . UI adalah representasi dari state yang ada. Alasan UI berubah adalah karena element yang memiliki state tersebut berubah.

Sesimple memanggil fungsi setState . Bahkan mungkin kita tidak berinteraksi dengan DOM element, ya? Tujuan dari menghindari abstraction over framework adalah agar “setidaknya” kita tau detail cara kerjanya, under the hood.

Mulai

Diawal kita menyebutkan untuk menghindari modern tooling. Maka dari itu untuk sekarang kita hanya berpangku pada teknologi-teknologi yang sudah disediakan oleh platform. No bundling, no hot module replacement, no transpiler.

Meskipun kita tidak membutuhkan web server untuk development, namun bekerja dengan “relative path” membuat mataku sakit. Install global module bernama serve yang tujuannya sudah jelas berdasarkan nama module tersebut.

yarn global add serve

Jika sudah, silahkan buat direktori project dan file-file inti seperti index.html , style.css , dan app.js . Jika sudah mari kita lanjutkan

Memuat module lit-html

Kita akan memuat module lit-html dari CDN (via unpkg.com) dan akan menggunakan ES Module disini. Ada beberapa jenis module di JavaScript: IIFE, ES, AMD, UMD, dan lain sebagainya. Beragamnya jenis-jenis module tersebut adalah karena “dulu”, browser memiliki standard untuk memuat module.

Sekarang, kita sudah memilikinya yakni ES Modules. Untuk ESM sendiri sudah di support dihampir evergreen browser kecuali ehm IE. Jadi kita perlu polyfill untuk pengguna IE bila kalian ingin.

Beginilah kira-kira struktur project kita:

Lihat di file JavaScript yang mana kita meload html dan render dari https://unpkg.com/lit-html?module yang mana module tersebut diload via CDN.

Dan hei, kok <script src="./app.mjs" type="module"></script> ? Bukan app.js dan memiliki atribut tambahan seperti type="module" ? Karena begitulah cara untuk memberitau browser bahwa kita menggunakan ESM. Jadi, silahkan ubah nama app.js anda menjadi app.mjs . Bila penasaran, silahka pelajari lebih lanjut tentang ESM disini.

Element pertama kita

Kita akan menggunakan aplikasi yang sama seperti sebelumnya. Dan behavior dari aplikasinya pun sama seperti sebelumnya, karena belum ada yang ngasih submission ke gue 😢

Jadi kita akan merender <h2> , <input type="text"> , dan <input type="checkbox"> . Tapi, kita akan merendernya menjadi lebih menarik, seperti ini:

masih ingat budi?

Sekarang, kumpulan element <li><input type="checkbox">{{ nama }} menjadi <my-todo name="{{ nama }}"> . Terlihat lebih rapih, ter-isolasi, dan so cute.

Mari kita buat.

Pertama, buat container terlebih dahulu agar element-element yang berada didalam <body> tidak tertindih.

Lalu kita akan mencoba membuat element via lit-html . Yakni <h2>Budi Todo</h2> . Buka file app.mjs lalu isi dengan kode ini:

Belum ada yang spesial. Kecuali, kita membuat element tersebut via JavaScript dengan cara yang deklaratif. Bandingkan dengan cara imperatif seperti ini:

Meskipun bisa saja kita menggunakan “JavaScript template literal” seperti ini:

const h2 = `<h2>Budi todo</h2>`

Namun untuk me-rendernya ke browser, appendChild ataupun append hanya menerima parameter node tree. Sedangkan h2 ber-tipe string. Cara paling kasarnya adalah dengan menggunakan innerHTML di container , yang pastinya sangat tidak direkomendasikan.

container.innerHTML = h2 // 😫

Oke cool kita telah menggunakan 2 library dari lit-html . Sekarang mari kita buat aplikasinya yang seperti kemarin.

Judul Aplikasi

Di aplikasi kemarin, kita membuatnya dengan nama Budi Todo. Dan di-set secara statis. Sekarang, kita membuatnya dinamis berdasarkan atribut yang terdapat di <my-app> . Jadi, bila Budi suatu waktu ingin mengubah nya misal menjadi: Bud1 t0d0 atau my lovely todos , bisa dengan mudah dilakukan dengan hanya mengubah atributnya saja.

Sebelum kita jalan lebih lanjut, izinkan saya untuk menjelaskan singkat tentang methods-methods diatas. Semua methods diatas (selain method yang kita buat sendiri) merupakan method bawaan platform: attributeChangedCallback , connectedCallback , dan observedAttributes . get dan set adalah fitur bawaan class di JavaScript. Yang maksudnya, bukan bagian langsung dari HTMLElement . Class HTMLElement pun bawaan dari platform yang berguna untuk membuat Element. Hmm.

Oke lanjut, mungkin ada beberapa istilah baru yang kurang familiar dengan kita: Shadow DOM. Shadow DOM mungkin hampir serupa dengan DOM, namun “ter-isolasi”. Istilah “Component” yang ada di Web merupakan gambaran yang tepat untuk Shadow DOM.

Ter-isolasi. Global CSS tidak berdampak ke Shadow DOM.

COOL. Zero runtime CSS module!

Daftar todos

Kemarin nyebutnya aktifitas, tapi gapapa deh ya sekarang kita sebutnya todo saja.

Hampir sama seperti my-app.js . Bedanya hanya difungsi template. Mungkin kalian bisa membuat class yang lebih general, sehingga semua kode yang sama tidak perlu ditulis lagi (use your imagination).

Dan ini hasilnya:

masih ter-isolasi.

Sekarang kita ke fitur lain yang belum ada di aplikasi sebelumnya.

Tandai bila sudah selesai

Di spesifikasi kita ingin memberi tanda “selesai” bila aktifitas tersebut sudah diceklis, disini kita akan buat tanda tersebut dengan mencoretnya.

Ini kodenya:

Ada beberapa istilah baru lagi, ya? Yang pertama, atribut ?checked= . Prefix ? buat ngasih tau bahwasannya atribut tesebut bernilai boolean, karena nilai atribut kan “string”. Dengan menambahkan prefix tanda tanya tersebut, atribut yang diawali tanda tanya akan dihilangkan bila nilainya false .

Juga @click= yang pada dasarnya sama seperti addEventListener('click') . Jadi, kode kita yang seperti ini:

<input @click="${this._handleClick}">

Sama dengan seperti ini:

someInputNode.addEventListener('click', this._handleClick)

Oh iya dan juga di fungsi render kita, kita menambahkan object { eventContext: this } . Itu untuk ngebinding this di event kita (yang klik). Jadi si fungsi _handleClick nanti enggak salah nunjuk this yang mana. Perbedaanya: Bila tidak menggunakan eventContext: this , ketika fungsi _handleClick dipanggil, this mengarah ke element <input type="checkbox"> yang mana bukan urusan kita.

Ketika kita menggunakan object tersebut, baru this mengarah ke <my-todo> yang mana itu adalah element yang kita maksud!

Demo

Sweet. Meskipun masih ada satu bug kecil, yakni ketika kita mengubah atribut done via element langsung, sedangkan kondisi checkbox dibrowser tersilang. But, siapa juga yang mau ngelakuin itu selain orang iseng sepertiku?

What you should learned here?

Ribet kan pakai framework?

Pertama, kita harus tau interface-interface apa saja yang sudah disediakan oleh library dan bagaimana behaviornya (ingat html, render, dan styleMap?)

Kedua, kita tidak berurusan dengan detail. Ini bagus untuk yang penting kelar moment, namun kurang bagus untuk pengetahuan. Apakah kalian tau bagaimana html memparsing element string kalian menjadi Node?

Bagaimana fungsi render bekerja? Karena lit-html adalah lower-level library untuk rendering DOM di JavaScript, jadi seharusnya level abstraksinya tidak terlalu banyak. Misal: do_some_task->do_some_task->element.appendChild(element) . Bedakan mungkin dengan React yang do_some_task->do_some_task->do_another_task->do_another_task->do_task->element.appendChild(element) Yang untuk proses “re-rendering” pun sampai impelementasi struktur data linked list. Haha.

Ketiga, every dependency has costs. Dari segi bundle-size, lit-html memakan 3.3kb (minified + gzipped) untuk digunakan. Lumayan kecil lah ya? Lalu dari sisi maintainability, bagaimana bila kita bergantung kepada library tersebut, sedangkan misalnya library tersebut sudah tidak maintenance? Atau, bagaimana bila library tersebut banyak mengandung bug? Costs bukan hanya tentang bundle size ternyata.

Namun, investasi kepada framework/library yang benar memerikan keuntungan yang benar-benar sepadan. Bayangkan ketika kalian menghabiskan waktu kalian untuk mempelajari lit-html , lalu Google mencari orang yang berpengalaman dengan lit-html , dan kebetulan anda lah orang yang mereka cari sesuai klasifikasi mereka :))

Juga, mempermudah pekerjaan kita. Bayangkan tanpa fungsi html , kita harus:

  • Membuat element via createElement dan melakukan beberapa konfigurasi secara “manual”
  • Membuat addEventListener berikut removeEventListener nya juga secara “manual lagi”
  • Menambah/menghapus attribut secara “manual” lagi
  • Membuat observable object secara “manual” juga.

Manual maksudnya “dengan tangan sendiri” ya, bukan automagically panggil fungsi boom selesai.

Gak ada praktiknya di tulisan ini, mas!

Sebenarnya tulisan ini ber-fokuskan ke mengapa menggunakan framework berikut pro dan kontranya baik di workflow maupun di pembelajaran. Karena setiap framework kan memiliki beda-beda fungsionalitas dan interface, jadi merasa “sangsi” ketika membahas suatu framework secara spesifik, sedangkan tujuanku adalah framework agnostik.

Jadi tujuannya biar: Oh pakai framework tuh gini toh, oh pro kontra nya gini, oh gue harus pakai framework ini itu, dan lain sebagainya. Bukan karena: Gue menggunakan React, karena belajarnya React. Lalu bergantung dengan React. Ketika kita agnostik, mau apapun frameworknya, yang penting udah tau framework tuh gimana dan menyelesaikan masalah yang mana.

Framework is framework, like others.

Karena kerangka kerja, kita harus mengikuti bagaimana alur kerja dari framework tersebut. Karena memiliki alur, pasti memiliki siklus. Dan ya, semua framework pasti memiliki siklusnya masing-masing.

React dengan componentDidMount() , componentWillMount() , dan componentWillUnmount() nya yang menggambarkan kondisinya sendiri. Vue dengan mounted() , created() , dan beforeDestroy() . Juga, “the platform” dengan connectedCallback() , attributeChangedCallback() , dan disconnectedCallback() nya.

Jika siklus dari platform hanyalah 3 tersebut, dan bila kalian ingin memiliki lebih dari 3 siklus tersebut, caranya adalah dengan membuat “sesuatu” diatas platform, dan seolah membuat siklus sendiri. Mungkin seperti componentDidMount , componentWillUpdate , dan componentDidUpdate yang kalau dibayangkan adalah bagian dari connectedCallback dan attributeChangedCallback .


Silahkan pelajari framework yang kalian inginkan. Polymer, Backbone, React, Vue, Svelte, dan beribu framework lainnya yang ada didunia ini. Yang penting — dan semoga — kalian sudah tau pro dan kontranya, juga “alasan” nya mengapa harus menggunakan framework, dan mengapa menggunakan framework A bukan B.

Bukan hanya karena mengikuti tren saja, ya.

Btw, sengaja menggunakan library lit-html untuk kasus disini, biar kalian banyak effort. Karena tujuan framework sendiri kan untuk mengurangi sebisa mungkin effort yang seharusnya ada, kan?